diff --git a/app/Livewire/Concerns/InteractsWithCloudflareDomainConnect.php b/app/Livewire/Concerns/InteractsWithCloudflareDomainConnect.php index 44dba0d5e..6ecea8e96 100644 --- a/app/Livewire/Concerns/InteractsWithCloudflareDomainConnect.php +++ b/app/Livewire/Concerns/InteractsWithCloudflareDomainConnect.php @@ -209,19 +209,20 @@ protected function serverIpsForDnsHints(): array } } - // Prefer instance public IPv6 when the destination IP is IPv4-only (and vice versa). - try { - $settings = instanceSettings(); - $publicV4 = data_get($settings, 'public_ipv4'); - $publicV6 = data_get($settings, 'public_ipv6'); - if ($ipv4 === null && is_string($publicV4) && filter_var($publicV4, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) { - $ipv4 = $publicV4; + if ($this->usesInstanceNetworkAddressesForDnsHints()) { + try { + $settings = instanceSettings(); + $publicV4 = data_get($settings, 'public_ipv4'); + $publicV6 = data_get($settings, 'public_ipv6'); + if ($ipv4 === null && is_string($publicV4) && filter_var($publicV4, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) { + $ipv4 = $publicV4; + } + if ($ipv6 === null && is_string($publicV6) && filter_var($publicV6, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) { + $ipv6 = $publicV6; + } + } catch (\Throwable) { + // } - if ($ipv6 === null && is_string($publicV6) && filter_var($publicV6, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) { - $ipv6 = $publicV6; - } - } catch (\Throwable) { - // } return [$ipv4, $ipv6]; @@ -253,5 +254,7 @@ protected function serverIpForDomainConnect(): ?string return null; } + abstract protected function usesInstanceNetworkAddressesForDnsHints(): bool; + abstract protected function authorizeUpdateForDomainConnect(): void; } diff --git a/app/Livewire/Project/Application/Domains.php b/app/Livewire/Project/Application/Domains.php index d7797935f..839c61e36 100644 --- a/app/Livewire/Project/Application/Domains.php +++ b/app/Livewire/Project/Application/Domains.php @@ -661,6 +661,11 @@ protected function authorizeUpdateForDomainConnect(): void $this->authorize('update', $this->application); } + protected function usesInstanceNetworkAddressesForDnsHints(): bool + { + return $this->application->destination?->server?->id === 0; + } + public function checkAllDns(): void { $this->authorize('update', $this->application); diff --git a/app/Livewire/Project/Service/Domains.php b/app/Livewire/Project/Service/Domains.php index d5254e093..4ac2f40b2 100644 --- a/app/Livewire/Project/Service/Domains.php +++ b/app/Livewire/Project/Service/Domains.php @@ -459,6 +459,11 @@ protected function authorizeUpdateForDomainConnect(): void $this->authorize('update', $this->service); } + protected function usesInstanceNetworkAddressesForDnsHints(): bool + { + return $this->service->server?->id === 0; + } + public function checkAllDns(): void { $this->authorize('update', $this->service); diff --git a/tests/Feature/ApplicationDomainsTest.php b/tests/Feature/ApplicationDomainsTest.php index f2beb22f1..396cccbfc 100644 --- a/tests/Feature/ApplicationDomainsTest.php +++ b/tests/Feature/ApplicationDomainsTest.php @@ -716,6 +716,63 @@ ->not->toContain('app.example.com'); }); +it('does not use instance network addresses for dns entries on a remote server', function () { + InstanceSettings::get()->update([ + 'public_ipv4' => '198.51.100.20', + 'public_ipv6' => '2001:db8::20', + ]); + $this->application->update(['fqdn' => 'https://app.example.com']); + + $component = Livewire::test(Domains::class, ['application' => $this->application->fresh()]); + $records = $component->instance()->dnsRecordHints(); + + expect($records)->toBe([ + [ + 'type' => 'A', + 'name' => 'app.example.com', + 'value' => '203.0.113.10', + ], + ]); +}); + +it('uses instance network addresses for dns entries on the localhost server', function () { + InstanceSettings::get()->update([ + 'public_ipv4' => '198.51.100.20', + 'public_ipv6' => '2001:db8::20', + ]); + $localhost = Server::factory()->create([ + 'id' => 0, + 'team_id' => $this->team->id, + 'private_key_id' => $this->server->private_key_id, + 'ip' => 'localhost', + ]); + $destination = StandaloneDocker::withoutEvents(fn () => StandaloneDocker::forceCreate([ + 'uuid' => (string) Str::uuid(), + 'name' => 'localhost-docker', + 'network' => 'coolify-localhost', + 'server_id' => $localhost->id, + ])); + $this->application->update([ + 'destination_id' => $destination->id, + 'fqdn' => 'https://app.example.com', + ]); + + $component = Livewire::test(Domains::class, ['application' => $this->application->fresh()]); + + expect($component->instance()->dnsRecordHints())->toBe([ + [ + 'type' => 'A', + 'name' => 'app.example.com', + 'value' => '198.51.100.20', + ], + [ + 'type' => 'AAAA', + 'name' => 'app.example.com', + 'value' => '2001:db8::20', + ], + ]); +}); + it('shows cloudflare domain connect only on cloud with a key', function () { config([ 'constants.coolify.self_hosted' => false, diff --git a/tests/Feature/ServiceDomainsTest.php b/tests/Feature/ServiceDomainsTest.php index 4f825f8e2..c93a4f1e9 100644 --- a/tests/Feature/ServiceDomainsTest.php +++ b/tests/Feature/ServiceDomainsTest.php @@ -280,6 +280,24 @@ ->not->toContain('web.example.com'); }); +it('does not use instance network addresses for service dns entries on a remote server', function () { + InstanceSettings::get()->update([ + 'public_ipv4' => '198.51.100.20', + 'public_ipv6' => '2001:db8::20', + ]); + $this->apiApp->update(['fqdn' => 'https://api.example.com']); + + $component = Livewire::test(Domains::class, ['service' => $this->service->fresh(['applications', 'server'])]); + + expect($component->instance()->dnsRecordHints())->toBe([ + [ + 'type' => 'A', + 'name' => 'api.example.com', + 'value' => '203.0.113.10', + ], + ]); +}); + it('persists a service redirect when its dropdown changes', function () { $this->webApp->update(['fqdn' => 'https://web.example.com', 'redirect' => 'both']);