From 495f4c42a349042a643a97c7bbba9ec7d7b82246 Mon Sep 17 00:00:00 2001 From: chen21019 Date: Sun, 13 Sep 2026 10:55:38 +0800 Subject: [PATCH] test: reuse runtime firewall detection in VM gates --- README.md | 6 ++++++ hostnat/iptables_vm_test.go | 18 +++++++++--------- hostports/iptables_vm_test.go | 17 +++-------------- 3 files changed, 18 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index f609a69..4105243 100644 --- a/README.md +++ b/README.md @@ -128,6 +128,12 @@ VERSION_OVERRIDE=v0.8.15 IMAGE_NAMESPACE=local/pasturestack make package Pull requests and `main` run one non-publishing gate: tests, vet/format checks, govulncheck, a reproducible binary build, one runtime image build, and Trivy scans plus CycloneDX SBOMs for the source, binary, and image. All reported vulnerabilities and secrets fail the gate. Publishing remains a separate, explicitly authorized operation. +The opt-in, root-only host NAT and host-port VM tests reuse the runtime's +read-only Docker firewall detection before changing any rules. This also +recognizes older Docker APIs without `FirewallBackend.Driver` and avoids +probing unloaded legacy tables on nft-only hosts. Run them only on a +disposable VM with a rollback point; ordinary CI does not execute them. + ## Compatibility and security Some legacy API paths, Docker labels, filesystem paths, and dependency namespaces are protocol or data contracts. They are isolated and documented in [COMPATIBILITY.md](COMPATIBILITY.md), rather than exposed as PastureStack branding. diff --git a/hostnat/iptables_vm_test.go b/hostnat/iptables_vm_test.go index f83be06..00c1ad0 100644 --- a/hostnat/iptables_vm_test.go +++ b/hostnat/iptables_vm_test.go @@ -9,6 +9,7 @@ import ( "testing" "github.com/PastureStack/network-plugin-manager/internal/firewall" + "github.com/moby/moby/client" ) // Opt-in root test for an isolated VM. It refuses to run when the production @@ -34,19 +35,18 @@ func testIptablesBatchOnVM(t *testing.T, mode firewall.Mode) { t.Fatal(err) } } - if out, err := exec.Command("docker", "info", "--format", "{{.FirewallBackend.Driver}}").CombinedOutput(); err != nil || strings.TrimSpace(string(out)) != "iptables" { - t.Fatalf("requires Docker iptables backend: %v: %s", err, out) + dc, err := client.New(client.FromEnv) + if err != nil { + t.Fatal(err) + } + defer dc.Close() + detected, err := firewall.Detect(dc, mode) + if err != nil || detected.Mode != mode || detected.Command != command || detected.Restore != restore { + t.Fatalf("refusing mismatched Docker firewall backend %s: detected=%+v err=%v", mode, detected, err) } if out, err := exec.Command(command, "-t", "nat", "-S", "DOCKER").CombinedOutput(); err != nil { t.Fatalf("requires Docker-owned NAT chain in %s: %v: %s", mode, err, out) } - other := "iptables-nft" - if mode == firewall.IptablesNFT { - other = "iptables-legacy" - } - if out, err := exec.Command(other, "-t", "nat", "-S", "DOCKER").CombinedOutput(); err == nil { - t.Fatalf("refusing dual Docker backends; %s also owns NAT: %s", other, out) - } iptables := func(args ...string) ([]byte, error) { return exec.Command(command, args...).CombinedOutput() } diff --git a/hostports/iptables_vm_test.go b/hostports/iptables_vm_test.go index c775e97..40d90d2 100644 --- a/hostports/iptables_vm_test.go +++ b/hostports/iptables_vm_test.go @@ -1,7 +1,6 @@ package hostports import ( - "context" "os" "os/exec" "strings" @@ -47,23 +46,13 @@ func testIptablesOnDisposableVM(t *testing.T, mode firewall.Mode) { t.Fatal(err) } defer dc.Close() - info, err := dc.Info(context.Background(), client.InfoOptions{}) - if err != nil { - t.Fatal(err) - } - if info.Info.FirewallBackend == nil || info.Info.FirewallBackend.Driver != "iptables" { - t.Fatalf("refusing xtables test outside Docker iptables mode: %#v", info.Info.FirewallBackend) + detected, err := firewall.Detect(dc, mode) + if err != nil || detected.Mode != mode || detected.Command != command || detected.Restore != restore { + t.Fatalf("refusing mismatched Docker firewall backend %s: detected=%+v err=%v", mode, detected, err) } if out, err := exec.Command(command, "-t", "nat", "-S", "DOCKER").CombinedOutput(); err != nil { t.Fatalf("requires Docker-owned NAT chain in %s: %v: %s", mode, err, out) } - other := "iptables-nft" - if mode == firewall.IptablesNFT { - other = "iptables-legacy" - } - if out, err := exec.Command(other, "-t", "nat", "-S", "DOCKER").CombinedOutput(); err == nil { - t.Fatalf("refusing dual Docker backends; %s also owns NAT: %s", other, out) - } for _, table := range []string{"nat", "filter"} { out, err := xtVMCommand(command, "-t", table, "-S") if err != nil {