Read fail2ban's bans as no firewall, and an iptables-nft reject as a refusal, from rulesets captured on a lab machine (hq ADR 0100)

This commit is contained in:
2026-09-22 18:04:48 +02:00
parent 8e2f75454d
commit da65f84c45
5 changed files with 448 additions and 21 deletions
+50
View File
@@ -500,3 +500,53 @@ func TestRetiringUfwOnAMachineWithoutIptablesStillRetiresIt(t *testing.T) {
t.Fatalf("disable: %v, active %v", err, f.active)
}
}
// Captured on a lab machine with fail2ban banning one documentation address in its sshd jail,
// once with its nftables backend and once with its iptables backend (iptables-nft), ufw inactive.
func captured(t *testing.T, name string) string {
t.Helper()
raw, err := os.ReadFile("testdata/" + name)
if err != nil {
t.Fatal(err)
}
return string(raw)
}
func TestFail2bansBansAreNotAFirewall(t *testing.T) {
for _, name := range []string{"fail2ban-nftables.nft", "fail2ban-iptables.nft"} {
ruleset := captured(t, name)
if !strings.Contains(ruleset, "192.0.2.55") {
t.Fatalf("%s holds no ban", name)
}
if got := Refusing(ruleset, false); len(got) != 0 {
t.Errorf("%s: fail2ban's bans read as a firewall: %v", name, got)
}
kind, what, err := Detect(context.Background(), (&fakeUFW{ruleset: ruleset}).run)
if err != nil || kind != None {
t.Errorf("%s: a machine with only fail2ban detected as %s (%s) %v", name, kind, what, err)
}
}
if got := RefusingLegacy(captured(t, "fail2ban-iptables-S.txt")); len(got) != 0 {
t.Errorf("fail2ban's iptables bans read as a firewall: %v", got)
}
}
func TestARefusalOfEveryoneButSomeIsStillAFirewall(t *testing.T) {
// A ban names the sources it refuses. A table that refuses every source but some, or every
// port but some, closes what the mesh would open, whatever its policy says.
for name, table := range map[string]string{
"all but a range": "table inet own {\n\tchain input {\n\t\ttype filter hook input priority filter; policy accept;\n\t\tip saddr != 10.0.0.0/8 drop\n\t}\n}\n",
"all but ssh": "table inet own {\n\tchain input {\n\t\ttype filter hook input priority filter; policy accept;\n\t\ttcp dport != 22 drop\n\t}\n}\n",
"iptables reject": "# Warning: table ip filter is managed by iptables-nft, do not touch!\ntable ip filter {\n\tchain INPUT {\n\t\ttype filter hook input priority filter; policy accept;\n\t\tcounter packets 0 bytes 0 xt target \"REJECT\"\n\t}\n}\n",
"ban beside a dropping policy": "table inet own {\n\tchain input {\n\t\ttype filter hook input priority filter; policy drop;\n\t\tip saddr 192.0.2.9 drop\n\t}\n}\n",
} {
if got := Refusing(dockerOnly(t)+table, false); len(got) == 0 {
t.Errorf("%s: not counted as a firewall", name)
}
}
legacy := "-P INPUT ACCEPT\n-N own\n-A INPUT -j own\n-A own ! -s 10.0.0.0/8 -j DROP\n"
if got := RefusingLegacy(legacy); len(got) == 0 {
t.Error("a legacy refusal of all but a range was not counted")
}
}