diff --git a/app/Models/ServerSetting.php b/app/Models/ServerSetting.php index 79b541a328..1314ad9bb2 100644 --- a/app/Models/ServerSetting.php +++ b/app/Models/ServerSetting.php @@ -319,7 +319,9 @@ class ServerSetting extends Model { $url = $this->sentinel_custom_url; - if ($this->server->isLocalhost() && $url === 'http://host.docker.internal:8000') { + // The local Sentinel runs on the coolify network. A host gateway URL leaves that network + // through the host INPUT chain, where host firewalls such as UFW drop it. + if ($this->server->isLocalhost() && strtolower((string) parse_url((string) $url, PHP_URL_HOST)) === 'host.docker.internal') { $url = null; } diff --git a/tests/Feature/SentinelTokenValidationTest.php b/tests/Feature/SentinelTokenValidationTest.php index 6c59952522..2416317c64 100644 --- a/tests/Feature/SentinelTokenValidationTest.php +++ b/tests/Feature/SentinelTokenValidationTest.php @@ -193,6 +193,30 @@ describe('ServerSetting::ensureSentinelUrl', function () { ->and($this->server->settings->fresh()->sentinel_custom_url)->toBe($url); }); + it('replaces a local host gateway endpoint on any port, because the Coolify network firewall can block it', function (string $legacyUrl) { + $this->server->update(['ip' => 'host.docker.internal']); + DB::table('server_settings')->where('id', $this->server->settings->id)->update([ + 'sentinel_custom_url' => $legacyUrl, + ]); + + $url = $this->server->settings->fresh()->ensureSentinelUrl(); + + expect($url)->toBe('http://coolify:8080') + ->and($this->server->settings->fresh()->sentinel_custom_url)->toBe($url); + })->with([ + 'custom APP_PORT' => 'http://host.docker.internal:8010', + 'trailing slash' => 'http://host.docker.internal:8000/', + 'uppercase host' => 'HTTP://Host.Docker.Internal:8010', + ]); + + it('keeps a host gateway endpoint for a remote server', function () { + DB::table('server_settings')->where('id', $this->server->settings->id)->update([ + 'sentinel_custom_url' => 'http://host.docker.internal:8010', + ]); + + expect($this->server->settings->fresh()->ensureSentinelUrl())->toBe('http://host.docker.internal:8010'); + }); + it('preserves an explicit custom URL for the local server', function () { $this->server->update(['ip' => 'host.docker.internal']); DB::table('server_settings')->where('id', $this->server->settings->id)->update([