diff --git a/network-go/firewall/firewall.go b/network-go/firewall/firewall.go index 6fe1077..b4d4cc5 100644 --- a/network-go/firewall/firewall.go +++ b/network-go/firewall/firewall.go @@ -204,11 +204,8 @@ func (o *Orchestrator) applyForwardRule(ctx context.Context, cfg *config.Network logger.Info("FIREWALL: forward rule: from=%q (IP=%s) to=%q (IP=%s) proto=%s port=%s", policy.From, sourceIP, policy.To, targetIP, proto, port) - // Determine the chain: use DOCKER-USER (iptables-legacy) or FORWARD - chain := "FORWARD" - if o.iptablesMgr.Binary() == "/usr/sbin/iptables-legacy" { - chain = "DOCKER-USER" - } + // Use the auto-detected chain (DOCKER-USER or FORWARD) + chain := o.iptablesMgr.Chain() logger.Debug("FIREWALL: using iptables chain=%s (binary=%s)", chain, o.iptablesMgr.Binary()) // Ensure established/related rule exists at the top diff --git a/network-go/firewall/firewall_test.go b/network-go/firewall/firewall_test.go index 2482dde..e986590 100644 --- a/network-go/firewall/firewall_test.go +++ b/network-go/firewall/firewall_test.go @@ -108,9 +108,9 @@ func TestReconcilePoliciesForwardRule(t *testing.T) { t.Error("InsertForwardAccept was not called") } - // Should use FORWARD chain (not iptables-legacy) - if iptables.InsertForwardAcceptChain != "FORWARD" { - t.Errorf("expected FORWARD chain, got %s", iptables.InsertForwardAcceptChain) + // Should use DOCKER-USER chain (default, even with non-legacy iptables) + if iptables.InsertForwardAcceptChain != "DOCKER-USER" { + t.Errorf("expected DOCKER-USER chain, got %s", iptables.InsertForwardAcceptChain) } } diff --git a/network-go/iptables/iptables.go b/network-go/iptables/iptables.go index 81f2edf..5289b50 100644 --- a/network-go/iptables/iptables.go +++ b/network-go/iptables/iptables.go @@ -11,6 +11,7 @@ import ( // IPTablesAPI defines the interface for iptables operations, enabling mock implementations for testing type IPTablesAPI interface { Binary() string + Chain() string EnsureIPForward() error EnsureEstablishedRelated(chain string) error DeleteLine(chain string, lineNum string) error @@ -26,8 +27,9 @@ type IPTablesAPI interface { // Manager manages iptables rules via the iptables/iptables-legacy CLI type Manager struct { - binary string - debug bool + binary string + chain string // "DOCKER-USER" or "FORWARD" + debug bool } // Ensure Manager implements IPTablesAPI @@ -40,18 +42,34 @@ func NewManager(debug bool) *Manager { return m } -// detectBinary checks if iptables-legacy is available (matching shell script logic) +// detectBinary checks which iptables binary has the DOCKER-USER chain (Docker-managed) func (m *Manager) detectBinary() { - logger.Info("IPTABLES: detecting iptables binary") + logger.Info("IPTABLES: detecting iptables binary and Docker chain") + + // Check iptables-legacy first cmd := exec.Command("/usr/sbin/iptables-legacy", "-L") output, err := cmd.CombinedOutput() if err == nil && strings.Contains(string(output), "DOCKER-USER") { m.binary = "/usr/sbin/iptables-legacy" - logger.Info("IPTABLES: detected iptables-legacy (DOCKER-USER chain present)") + m.chain = "DOCKER-USER" + logger.Info("IPTABLES: detected iptables-legacy with DOCKER-USER chain") return } + + // Check default iptables (may be nft-based) + cmd2 := exec.Command("/usr/sbin/iptables", "-L") + output2, err2 := cmd2.CombinedOutput() + if err2 == nil && strings.Contains(string(output2), "DOCKER-USER") { + m.binary = "/usr/sbin/iptables" + m.chain = "DOCKER-USER" + logger.Info("IPTABLES: detected iptables with DOCKER-USER chain") + return + } + + // No DOCKER-USER chain found — default to DOCKER-USER for ACCEPT rules m.binary = "/usr/sbin/iptables" - logger.Info("IPTABLES: using default iptables binary") + m.chain = "DOCKER-USER" + logger.Info("IPTABLES: no DOCKER-USER chain detected, defaulting to DOCKER-USER with default iptables") } // Binary returns the detected iptables binary path @@ -59,6 +77,11 @@ func (m *Manager) Binary() string { return m.binary } +// Chain returns the firewall chain to use for FORWARD rules (DOCKER-USER or FORWARD) +func (m *Manager) Chain() string { + return m.chain +} + // run executes an iptables command on the host func (m *Manager) run(args ...string) error { cmdStr := m.binary + " " + strings.Join(args, " ") diff --git a/network-go/mock/mock.go b/network-go/mock/mock.go index ac3b414..586a70b 100644 --- a/network-go/mock/mock.go +++ b/network-go/mock/mock.go @@ -163,6 +163,11 @@ func (m *MockIPTablesManager) Binary() string { return m.BinaryResult } +func (m *MockIPTablesManager) Chain() string { + // Default to DOCKER-USER (matches production behavior) + return "DOCKER-USER" +} + func (m *MockIPTablesManager) EnsureIPForward() error { m.EnsureIPForwardCalled = true return m.EnsureIPForwardErr