mirror of
https://github.com/coollabsio/coolify.git
synced 2026-11-06 13:10:37 -04:00
fix(database): listen on IPv6 in public TCP proxy when network has IPv6
Docker publishes the proxy port on [::] too and forwards it to the container's IPv6 address when the network has IPv6 enabled. nginx listened only on IPv4, so every IPv6 connection was reset. Add listen [::]:<port> when the destination network has IPv6 enabled. Networks without IPv6 keep the IPv4-only listener. Fixes #12133
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Actions\Database;
|
||||
|
||||
use App\Models\Server;
|
||||
use App\Models\ServiceDatabase;
|
||||
use App\Models\StandaloneClickhouse;
|
||||
use App\Models\StandaloneDragonfly;
|
||||
@@ -58,6 +59,7 @@ class StartDatabaseProxy
|
||||
$configuration_dir = database_proxy_dir($database->uuid);
|
||||
$host_configuration_dir = devHostDockerPath($server, $configuration_dir);
|
||||
$timeoutConfig = $this->buildProxyTimeoutConfig($database->public_port_timeout);
|
||||
$listenConfig = $this->buildListenConfig($database->public_port, $this->isNetworkIpv6Enabled($network, $server));
|
||||
$nginxconf = <<<EOF
|
||||
user nginx;
|
||||
worker_processes auto;
|
||||
@@ -69,7 +71,7 @@ class StartDatabaseProxy
|
||||
}
|
||||
stream {
|
||||
server {
|
||||
listen $database->public_port;
|
||||
$listenConfig
|
||||
proxy_pass $containerName:$internalPort;
|
||||
$timeoutConfig
|
||||
}
|
||||
@@ -164,6 +166,27 @@ class StartDatabaseProxy
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Docker publishes the port on [::] too and forwards it to the container's IPv6 address
|
||||
* when the network has IPv6 enabled, so nginx must listen there as well.
|
||||
*/
|
||||
private function isNetworkIpv6Enabled(string $network, Server $server): bool
|
||||
{
|
||||
$safeNetwork = escapeshellarg($network);
|
||||
$output = instant_remote_process(["docker network inspect {$safeNetwork} --format '{{.EnableIPv6}}'"], $server, false);
|
||||
|
||||
return trim((string) $output) === 'true';
|
||||
}
|
||||
|
||||
private function buildListenConfig(int $port, bool $ipv6Enabled): string
|
||||
{
|
||||
if (! $ipv6Enabled) {
|
||||
return "listen {$port};";
|
||||
}
|
||||
|
||||
return "listen {$port};\n listen [::]:{$port};";
|
||||
}
|
||||
|
||||
private function buildProxyTimeoutConfig(?int $timeout): string
|
||||
{
|
||||
if ($timeout === null || $timeout < 1) {
|
||||
|
||||
@@ -70,3 +70,15 @@ test('buildProxyTimeoutConfig normalizes invalid values to default', function (?
|
||||
[-10, 'proxy_timeout 3600s;'],
|
||||
[120, 'proxy_timeout 120s;'],
|
||||
]);
|
||||
|
||||
test('buildListenConfig adds an IPv6 listener only when the network has IPv6 enabled', function (bool $ipv6Enabled, array $expectedListeners) {
|
||||
$action = new StartDatabaseProxy;
|
||||
$method = new ReflectionMethod($action, 'buildListenConfig');
|
||||
|
||||
$listeners = array_map('trim', explode("\n", $method->invoke($action, 28197, $ipv6Enabled)));
|
||||
|
||||
expect($listeners)->toBe($expectedListeners);
|
||||
})->with([
|
||||
'IPv4 only network' => [false, ['listen 28197;']],
|
||||
'IPv6 enabled network' => [true, ['listen 28197;', 'listen [::]:28197;']],
|
||||
]);
|
||||
|
||||
Reference in New Issue
Block a user