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:
@@ -98,6 +98,11 @@ func NewMonitor(config *c.Config) *MonitorMgr {
|
||||
if config.Agent.CleanupTimer == 0 {
|
||||
config.Agent.CleanupTimer = defaultCleanupTimer
|
||||
}
|
||||
// Set iptables binary (defaults to "iptables" if not specified)
|
||||
if config.Agent.IptablesBinary == "" {
|
||||
config.Agent.IptablesBinary = "iptables"
|
||||
}
|
||||
SetIptablesBinary(config.Agent.IptablesBinary)
|
||||
mon.config = config
|
||||
// add apps defined in config
|
||||
for _, a := range config.Apps {
|
||||
@@ -356,6 +361,15 @@ func (m *MonitorMgr) Reload(configPath string) error {
|
||||
if newConfig.Agent.CleanupTimer == 0 {
|
||||
newConfig.Agent.CleanupTimer = defaultCleanupTimer
|
||||
}
|
||||
if newConfig.Agent.IptablesBinary == "" {
|
||||
newConfig.Agent.IptablesBinary = "iptables"
|
||||
}
|
||||
|
||||
// Update iptables binary if changed
|
||||
if m.config.Agent.IptablesBinary != newConfig.Agent.IptablesBinary {
|
||||
glog.Infof("Iptables binary changed from %s to %s", m.config.Agent.IptablesBinary, newConfig.Agent.IptablesBinary)
|
||||
SetIptablesBinary(newConfig.Agent.IptablesBinary)
|
||||
}
|
||||
|
||||
// Check if BGP configuration has changed
|
||||
bgpChanged := m.bgpConfigChanged(m.config.Bgp, newConfig.Bgp)
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
)
|
||||
|
||||
var execCmd = "bash"
|
||||
var iptablesBinary = "iptables"
|
||||
|
||||
func getCmdList(mainCmd string) []string {
|
||||
cmdList := []string{}
|
||||
@@ -88,8 +89,8 @@ func deleteLoopback(addr *net.IPNet) error {
|
||||
|
||||
func natRule(op string, vip, localAddr net.IP, protocol, lport, dport string) error {
|
||||
cmd := fmt.Sprintf(
|
||||
"iptables -t nat -%s PREROUTING -p %s -d %s --dport %s -j DNAT --to-destination %s:%s",
|
||||
op, protocol, vip.String(), lport, localAddr.String(), dport,
|
||||
"%s -t nat -%s PREROUTING -p %s -d %s --dport %s -j DNAT --to-destination %s:%s",
|
||||
iptablesBinary, op, protocol, vip.String(), lport, localAddr.String(), dport,
|
||||
)
|
||||
cmdList := getCmdList(cmd)
|
||||
_, err := exec.Command(execCmd, cmdList...).Output()
|
||||
@@ -98,3 +99,10 @@ func natRule(op string, vip, localAddr net.IP, protocol, lport, dport string) er
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetIptablesBinary sets the iptables binary to use for NAT rules
|
||||
func SetIptablesBinary(binary string) {
|
||||
if binary != "" {
|
||||
iptablesBinary = binary
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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":
|
||||
|
||||
Reference in New Issue
Block a user