Refactor iptables chain detection to centralize and default to DOCKER-USER
continuous-integration/drone/push Build is passing

Move chain detection logic from firewall to iptables manager for better encapsulation. The manager now auto-detects both the iptables binary and chain (DOCKER-USER or FORWARD) based on the presence of the Docker-managed chain, but always defaults to DOCKER-USER for consistency. This simplifies firewall code and ensures proper Docker integration regardless of iptables version.
This commit is contained in:
gyurix
2026-06-16 12:46:25 +02:00
parent 77f80dea1b
commit d5757e623a
4 changed files with 39 additions and 14 deletions
+2 -5
View File
@@ -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", 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) policy.From, sourceIP, policy.To, targetIP, proto, port)
// Determine the chain: use DOCKER-USER (iptables-legacy) or FORWARD // Use the auto-detected chain (DOCKER-USER or FORWARD)
chain := "FORWARD" chain := o.iptablesMgr.Chain()
if o.iptablesMgr.Binary() == "/usr/sbin/iptables-legacy" {
chain = "DOCKER-USER"
}
logger.Debug("FIREWALL: using iptables chain=%s (binary=%s)", chain, o.iptablesMgr.Binary()) logger.Debug("FIREWALL: using iptables chain=%s (binary=%s)", chain, o.iptablesMgr.Binary())
// Ensure established/related rule exists at the top // Ensure established/related rule exists at the top
+3 -3
View File
@@ -108,9 +108,9 @@ func TestReconcilePoliciesForwardRule(t *testing.T) {
t.Error("InsertForwardAccept was not called") t.Error("InsertForwardAccept was not called")
} }
// Should use FORWARD chain (not iptables-legacy) // Should use DOCKER-USER chain (default, even with non-legacy iptables)
if iptables.InsertForwardAcceptChain != "FORWARD" { if iptables.InsertForwardAcceptChain != "DOCKER-USER" {
t.Errorf("expected FORWARD chain, got %s", iptables.InsertForwardAcceptChain) t.Errorf("expected DOCKER-USER chain, got %s", iptables.InsertForwardAcceptChain)
} }
} }
+29 -6
View File
@@ -11,6 +11,7 @@ import (
// IPTablesAPI defines the interface for iptables operations, enabling mock implementations for testing // IPTablesAPI defines the interface for iptables operations, enabling mock implementations for testing
type IPTablesAPI interface { type IPTablesAPI interface {
Binary() string Binary() string
Chain() string
EnsureIPForward() error EnsureIPForward() error
EnsureEstablishedRelated(chain string) error EnsureEstablishedRelated(chain string) error
DeleteLine(chain string, lineNum 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 // Manager manages iptables rules via the iptables/iptables-legacy CLI
type Manager struct { type Manager struct {
binary string binary string
debug bool chain string // "DOCKER-USER" or "FORWARD"
debug bool
} }
// Ensure Manager implements IPTablesAPI // Ensure Manager implements IPTablesAPI
@@ -40,18 +42,34 @@ func NewManager(debug bool) *Manager {
return m 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() { 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") cmd := exec.Command("/usr/sbin/iptables-legacy", "-L")
output, err := cmd.CombinedOutput() output, err := cmd.CombinedOutput()
if err == nil && strings.Contains(string(output), "DOCKER-USER") { if err == nil && strings.Contains(string(output), "DOCKER-USER") {
m.binary = "/usr/sbin/iptables-legacy" 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 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" 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 // Binary returns the detected iptables binary path
@@ -59,6 +77,11 @@ func (m *Manager) Binary() string {
return m.binary 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 // run executes an iptables command on the host
func (m *Manager) run(args ...string) error { func (m *Manager) run(args ...string) error {
cmdStr := m.binary + " " + strings.Join(args, " ") cmdStr := m.binary + " " + strings.Join(args, " ")
+5
View File
@@ -163,6 +163,11 @@ func (m *MockIPTablesManager) Binary() string {
return m.BinaryResult return m.BinaryResult
} }
func (m *MockIPTablesManager) Chain() string {
// Default to DOCKER-USER (matches production behavior)
return "DOCKER-USER"
}
func (m *MockIPTablesManager) EnsureIPForward() error { func (m *MockIPTablesManager) EnsureIPForward() error {
m.EnsureIPForwardCalled = true m.EnsureIPForwardCalled = true
return m.EnsureIPForwardErr return m.EnsureIPForwardErr