From ce2a4ba05723fddf6849e488c13f8dcf7ecc8564 Mon Sep 17 00:00:00 2001 From: RandyZhang Date: Sun, 8 Feb 2026 23:00:44 +0800 Subject: [PATCH] fix: filter port conflict checks to only consider wildcard-bound addresses Coolify's proxy uses Docker's 0.0.0.0 binding, which can coexist with specific-IP bindings (e.g., Tailscale 100.x.x.x) via SO_REUSEADDR. Previously, the port conflict check incorrectly flagged all port listeners as conflicts, regardless of which IP address they bound to. This caused false positives when services like Tailscale were running on internal network interfaces. This fix filters port conflict detection to only consider listeners on wildcard addresses (0.0.0.0, *, [::], ::) as real conflicts, while allowing specific-IP bindings to coexist with the proxy. The filtering is applied across all port check paths: - ss command path (buildPortCheckCommands) - netstat fallback path (buildPortCheckCommands) - isPortConflict() method for sequential checks --- app/Actions/Proxy/CheckProxy.php | 38 +++++++++++++++++++++++++++----- 1 file changed, 32 insertions(+), 6 deletions(-) diff --git a/app/Actions/Proxy/CheckProxy.php b/app/Actions/Proxy/CheckProxy.php index 99537e606..6817a2a9c 100644 --- a/app/Actions/Proxy/CheckProxy.php +++ b/app/Actions/Proxy/CheckProxy.php @@ -188,17 +188,24 @@ class CheckProxy echo 'port_free'; exit 0; fi; - count=\$(echo \"\$ss_output\" | grep -c ':$port '); + # Filter: only wildcard-bound listeners (0.0.0.0, *, [::], ::) conflict with Docker's 0.0.0.0 binding + # Specific IP bindings (e.g., Tailscale 100.x.x.x) coexist via SO_REUSEADDR + wildcard_output=\$(echo \"\$ss_output\" | grep -E '(0\\.0\\.0\\.0|\\*|\\[::\\]):{$port}[[:space:]]|:::{$port}[[:space:]]'); + if [ -z \"\$wildcard_output\" ]; then + echo 'port_free'; + exit 0; + fi; + count=\$(echo \"\$wildcard_output\" | grep -c ':$port '); if [ \$count -eq 0 ]; then echo 'port_free'; exit 0; fi; # Check for dual-stack or docker processes - if [ \$count -le 2 ] && (echo \"\$ss_output\" | grep -q 'docker\\|coolify'); then + if [ \$count -le 2 ] && (echo \"\$wildcard_output\" | grep -q 'docker\\|coolify'); then echo 'port_free'; exit 0; fi; - echo \"port_conflict|\$ss_output\"; + echo \"port_conflict|\$wildcard_output\"; exit 0; fi; @@ -209,16 +216,22 @@ class CheckProxy echo 'port_free'; exit 0; fi; - count=\$(echo \"\$netstat_output\" | grep -c 'LISTEN'); + # Filter: only wildcard-bound listeners conflict with Docker's 0.0.0.0 binding + wildcard_output=\$(echo \"\$netstat_output\" | grep -E '(0\\.0\\.0\\.0|\\*|\\[::\\]):{$port}[[:space:]]|:::{$port}[[:space:]]'); + if [ -z \"\$wildcard_output\" ]; then + echo 'port_free'; + exit 0; + fi; + count=\$(echo \"\$wildcard_output\" | grep -c 'LISTEN'); if [ \$count -eq 0 ]; then echo 'port_free'; exit 0; fi; - if [ \$count -le 2 ] && (echo \"\$netstat_output\" | grep -q 'docker\\|coolify'); then + if [ \$count -le 2 ] && (echo \"\$wildcard_output\" | grep -q 'docker\\|coolify'); then echo 'port_free'; exit 0; fi; - echo \"port_conflict|\$netstat_output\"; + echo \"port_conflict|\$wildcard_output\"; exit 0; fi; @@ -356,6 +369,19 @@ class CheckProxy return false; } + // Filter: only wildcard-bound listeners (0.0.0.0, *, [::], ::) conflict with Docker's 0.0.0.0 binding + // Specific IP bindings (e.g., Tailscale 100.x.x.x) coexist via SO_REUSEADDR + $detailLines = explode("\n", $details); + $wildcardPattern = '/(0\.0\.0\.0|\*|\[::\]):'.preg_quote($port, '/').'(\s|$)|:::'.preg_quote($port, '/').'(\s|$)/'; + $wildcardLines = array_filter($detailLines, function ($line) use ($wildcardPattern) { + return preg_match($wildcardPattern, $line); + }); + if (empty($wildcardLines)) { + return false; + } + $details = trim(implode("\n", $wildcardLines)); + $count = count($wildcardLines); + // Try to detect if this is our coolify-proxy if (strpos($details, 'docker') !== false || strpos($details, $proxyContainerName) !== false) { // It's likely our docker or proxy, which is fine