Add configuration option to select iptables implementation

When running gocast in a container, the default iptables implementation
may not match that used on the underlying host kernel. The current
container uses the legacy iptables implementation and calls the
`iptables` binary. This fails with exit code 3 when running on a host
using the newer nftables implementation. The container already has
`iptables-nft` binary included, so just needs a way to call this instead
of the default `iptables` binary.

This change implements a new `iptables_binary` config option, defaulting
to `iptables`, and calls this when adding or removing NAT rules.

Fixes #32

This change was written using AI LLM.

Authored-By: Claude Code (Sonnet 4.5)
This commit is contained in:
Ben Roberts
2026-06-17 17:18:59 +01:00
parent fe399e2f03
commit 256afcbd97
6 changed files with 104 additions and 4 deletions

View File

@@ -43,6 +43,71 @@ func TestAddLoopback(t *testing.T) {
assert.NotNil(t, err)
}
func TestSetIptablesBinary(t *testing.T) {
a := assert.New(t)
// Save original value
originalBinary := iptablesBinary
defer func() {
iptablesBinary = originalBinary
}()
// Test setting custom binary
SetIptablesBinary("iptables-nft")
a.Equal("iptables-nft", iptablesBinary)
// Test setting back to default
SetIptablesBinary("iptables")
a.Equal("iptables", iptablesBinary)
// Test that empty string doesn't change the value
SetIptablesBinary("iptables-custom")
a.Equal("iptables-custom", iptablesBinary)
SetIptablesBinary("")
a.Equal("iptables-custom", iptablesBinary, "Empty string should not change the binary")
}
func TestNatRuleCommandFormat(t *testing.T) {
a := assert.New(t)
// Save original values
originalBinary := iptablesBinary
originalExecCmd := execCmd
defer func() {
iptablesBinary = originalBinary
execCmd = originalExecCmd
}()
vip := net.ParseIP("192.0.2.1")
localAddr := net.ParseIP("10.0.0.1")
// Test with default iptables
SetIptablesBinary("iptables")
err := natRule("A", vip, localAddr, "tcp", "80", "8080")
// We expect this to fail in test environment, but we can check the error message
// contains our command
if err != nil {
a.Contains(err.Error(), "iptables -t nat -A PREROUTING")
}
// Test with iptables-nft
SetIptablesBinary("iptables-nft")
err = natRule("A", vip, localAddr, "tcp", "80", "8080")
if err != nil {
a.Contains(err.Error(), "iptables-nft -t nat -A PREROUTING")
}
// Test with custom binary path
SetIptablesBinary("/usr/sbin/iptables")
err = natRule("D", vip, localAddr, "udp", "53", "5353")
if err != nil {
a.Contains(err.Error(), "/usr/sbin/iptables -t nat -D PREROUTING")
a.Contains(err.Error(), "udp")
a.Contains(err.Error(), "53")
a.Contains(err.Error(), "5353")
}
}
func TestMain(m *testing.M) {
switch os.Getenv("test_name") {
case "test_gateway":