diff --git a/integration/networking/bridge_linux_test.go b/integration/networking/bridge_linux_test.go index 8d2e016a6f..f2a22cba3c 100644 --- a/integration/networking/bridge_linux_test.go +++ b/integration/networking/bridge_linux_test.go @@ -356,6 +356,7 @@ func TestBridgeINCRouted(t *testing.T) { d := daemon.New(t) d.StartWithBusybox(ctx, t) t.Cleanup(func() { d.Stop(t) }) + firewallBackend := d.FirewallBackendDriver(t) c := d.NewClientT(t) t.Cleanup(func() { c.Close() }) @@ -455,7 +456,7 @@ func TestBridgeINCRouted(t *testing.T) { } for _, fwdPolicy := range []string{"ACCEPT", "DROP"} { - networking.SetFilterForwardPolicies(t, fwdPolicy) + networking.SetFilterForwardPolicies(t, firewallBackend, fwdPolicy) t.Run(fwdPolicy, func(t *testing.T) { for _, tc := range testcases { t.Run(tc.name+"/v4/ping", func(t *testing.T) { diff --git a/integration/networking/port_mapping_linux_test.go b/integration/networking/port_mapping_linux_test.go index f17955d37e..d9251fffed 100644 --- a/integration/networking/port_mapping_linux_test.go +++ b/integration/networking/port_mapping_linux_test.go @@ -649,6 +649,7 @@ func TestDirectRoutingOpenPorts(t *testing.T) { d := daemon.New(t) d.StartWithBusybox(ctx, t) t.Cleanup(func() { d.Stop(t) }) + firewallBackend := d.FirewallBackendDriver(t) c := d.NewClientT(t) t.Cleanup(func() { c.Close() }) @@ -771,7 +772,7 @@ func TestDirectRoutingOpenPorts(t *testing.T) { // ping/http timeouts separately. (The iptables filter-FORWARD policy affects the // whole host, so ACCEPT/DROP tests can't be parallelized). for _, fwdPolicy := range []string{"ACCEPT", "DROP"} { - networking.SetFilterForwardPolicies(t, fwdPolicy) + networking.SetFilterForwardPolicies(t, firewallBackend, fwdPolicy) t.Run(fwdPolicy, func(t *testing.T) { for gwMode := range networks { t.Run(gwMode+"/v4/ping", func(t *testing.T) { diff --git a/internal/testutils/networking/iptables.go b/internal/testutils/networking/firewall.go similarity index 51% rename from internal/testutils/networking/iptables.go rename to internal/testutils/networking/firewall.go index aa578246f6..4860022661 100644 --- a/internal/testutils/networking/iptables.go +++ b/internal/testutils/networking/firewall.go @@ -1,6 +1,7 @@ package networking import ( + "fmt" "os/exec" "strings" "testing" @@ -12,8 +13,15 @@ import ( "gotest.tools/v3/poll" ) +const ( + // The name of the bridge driver's nftables tables. + nftTable = "docker-bridges" + // The name of the filter-FORWARD chain in nftTable. + nftFFChain = "filter-FORWARD" +) + // Find the policy in, for example "Chain FORWARD (policy ACCEPT)". -var rePolicy = lazyregexp.New("policy ([A-Z]+)") +var rePolicy = lazyregexp.New("policy ([A-Za-z]+)") // SetFilterForwardPolicies sets the default policy for the FORWARD chain in // the filter tables for both IPv4 and IPv6. The original policy is restored @@ -22,20 +30,24 @@ var rePolicy = lazyregexp.New("policy ([A-Z]+)") // There's only one filter-FORWARD policy, so this won't behave well if used by // tests running in parallel in a single network namespace that expect different // behaviour. -func SetFilterForwardPolicies(t *testing.T, policy string) { +func SetFilterForwardPolicies(t *testing.T, firewallBackend string, policy string) { t.Helper() + if strings.Contains(firewallBackend, "iptables") { + setIptablesFFP(t, policy) + return + } + if firewallBackend == "nftables" { + setNftablesFFP(t, policy) + return + } + t.Fatalf("unknown firewall backend %s", firewallBackend) +} +func setIptablesFFP(t *testing.T, policy string) { + t.Helper() for _, iptablesCmd := range []string{"iptables", "ip6tables"} { - cmd := exec.Command(iptablesCmd, "-L", "FORWARD") - out, err := cmd.Output() - if err != nil { - t.Fatalf("Failed to get %s FORWARD policy: %v", iptablesCmd, err) - } - opMatch := rePolicy.FindSubmatch(out) - if len(opMatch) != 2 { - t.Fatalf("Failed to find %s FORWARD policy in: %s", iptablesCmd, out) - } - origPolicy := string(opMatch[1]) + origPolicy, err := getChainPolicy(t, exec.Command(iptablesCmd, "-L", "FORWARD")) + assert.NilError(t, err, "failed to get iptables policy") if origPolicy == policy { continue } @@ -50,6 +62,43 @@ func SetFilterForwardPolicies(t *testing.T, policy string) { } } +func setNftablesFFP(t *testing.T, policy string) { + t.Helper() + policy = strings.ToLower(policy) + for _, family := range []string{"ip", "ip6"} { + origPolicy, err := getChainPolicy(t, exec.Command("nft", "list", "chain", family, nftTable, nftFFChain)) + assert.NilError(t, err, "failed to get nftables policy") + if origPolicy == policy { + continue + } + cmd := func(p string) *exec.Cmd { + return exec.Command("nft", "add", "chain", family, nftTable, nftFFChain, "{", "policy", p, ";", "}") + } + if err := cmd(policy).Run(); err != nil { + t.Fatalf("Failed to set %s filter-FORWARD policy: %v", family, err) + } + t.Cleanup(func() { + if err := cmd(origPolicy).Run(); err != nil { + t.Logf("Failed to restore %s filter-FORWARD policy: %v", family, err) + } + }) + } +} + +func getChainPolicy(t *testing.T, cmd *exec.Cmd) (string, error) { + t.Helper() + out, err := cmd.Output() + if err != nil { + return "", fmt.Errorf("getting policy: %w", err) + } + opMatch := rePolicy.FindSubmatch(out) + if len(opMatch) != 2 { + return "", fmt.Errorf("searching for policy: %w", err) + } + return string(opMatch[1]), nil +} + +// FirewalldRunning returns true if "firewall-cmd --state" reports "running". func FirewalldRunning() bool { state, err := exec.Command("firewall-cmd", "--state").CombinedOutput() return err == nil && strings.TrimSpace(string(state)) == "running"