Add nftables support to testutil SetFilterForwardPolicies

Because nftables tables/chain aren't fixed, like they are
in iptables - this change makes an assumption about the
bridge driver's naming.

Signed-off-by: Rob Murray <rob.murray@docker.com>
This commit is contained in:
Rob Murray
2025-04-25 15:15:34 +01:00
parent 7ea0e60dde
commit f9f0db0789
3 changed files with 65 additions and 14 deletions

View File

@@ -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) {

View File

@@ -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) {

View File

@@ -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"