1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
|
package networking
import (
"fmt"
"os/exec"
"strings"
"testing"
"github.com/docker/docker/internal/lazyregexp"
"github.com/docker/docker/testutil/daemon"
"gotest.tools/v3/assert"
"gotest.tools/v3/icmd"
"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-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
// using t.Cleanup().
//
// 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, 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"} {
origPolicy, err := getChainPolicy(t, exec.Command(iptablesCmd, "-L", "FORWARD"))
assert.NilError(t, err, "failed to get iptables policy")
if origPolicy == policy {
continue
}
if err := exec.Command(iptablesCmd, "-P", "FORWARD", policy).Run(); err != nil {
t.Fatalf("Failed to set %s FORWARD policy: %v", iptablesCmd, err)
}
t.Cleanup(func() {
if err := exec.Command(iptablesCmd, "-P", "FORWARD", origPolicy).Run(); err != nil {
t.Logf("Failed to restore %s FORWARD policy: %v", iptablesCmd, err)
}
})
}
}
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"
}
// FirewalldReload reloads firewalld and waits for the daemon to re-create its rules.
// It's a no-op if firewalld is not running, and the test fails if the reload does
// not complete.
func FirewalldReload(t *testing.T, d *daemon.Daemon) {
t.Helper()
if !FirewalldRunning() {
return
}
lastReload := d.FirewallReloadedAt(t)
res := icmd.RunCommand("firewall-cmd", "--reload")
assert.NilError(t, res.Error)
poll.WaitOn(t, func(_ poll.LogT) poll.Result {
latestReload := d.FirewallReloadedAt(t)
if latestReload != "" && latestReload != lastReload {
t.Log("Firewalld reload completed at", latestReload)
return poll.Success()
}
return poll.Continue("firewalld reload not complete")
})
}
|