diff --git a/internal/catalogue/adoption.go b/internal/catalogue/adoption.go index 7b8c827..04dc85d 100644 --- a/internal/catalogue/adoption.go +++ b/internal/catalogue/adoption.go @@ -146,8 +146,9 @@ func Published(resources []map[string]any) map[string]map[int]int { // // It passes everything by default and holds nothing but a refusal, so it cannot close anything // the machine serves; and it is the mesh's own table, so the found firewall reloading does not -// touch it. It refuses the ports except from the machine itself — its loopback and the container -// runtime's own networks — and from the private network, known by the interface a packet arrives +// touch it. It refuses only packets addressed to this machine, and the ports except from the +// machine itself — its loopback and the container runtime's own networks — and from the private +// network, known by the interface a packet arrives // on and never by its source address. At prerouting, ahead of the runtime's destination // translation, so it matches the port the packet was sent to; in the inet family, so both address // families. @@ -166,8 +167,11 @@ func AsGuard(ports []int) string { b.WriteString("table inet mesh_guard {\n") b.WriteString("\tchain prerouting {\n") b.WriteString("\t\ttype filter hook prerouting priority raw; policy accept;\n") - fmt.Fprintf(&b, "\t\tiifname != \"lo\" iifname != \"docker0\" iifname != \"br-*\" "+ - "iifname != \"mesh0\" tcp dport { %s } drop\n", strings.Join(listed, ", ")) + // Only packets addressed to this machine: traffic it routes for others — a predecessor's hub, + // say — is never the guard's business (novox/hq ADR 0103). + fmt.Fprintf(&b, "\t\tfib daddr type local iifname != \"lo\" iifname != \"docker0\" "+ + "iifname != \"br-*\" iifname != \"mesh0\" tcp dport { %s } drop\n", + strings.Join(listed, ", ")) b.WriteString("\t}\n") b.WriteString("}\n") return b.String() @@ -178,8 +182,12 @@ func AsGuard(ports []int) string { func GuardUnitText() string { return "[Unit]\n" + "Description=The mesh's guard: refuses its own ports from outside (novox/hq ADR 0100)\n" + - "Before=network-pre.target\n" + + // Early, before the network is up, and without the default dependencies that would + // order it after the network; stopped at shutdown like any unit. + "DefaultDependencies=no\n" + "Wants=network-pre.target\n" + + "Before=network-pre.target shutdown.target\n" + + "Conflicts=shutdown.target\n" + "\n" + "[Service]\n" + "Type=oneshot\n" + diff --git a/internal/catalogue/adoption_test.go b/internal/catalogue/adoption_test.go index da2dc78..89ccc37 100644 --- a/internal/catalogue/adoption_test.go +++ b/internal/catalogue/adoption_test.go @@ -200,13 +200,33 @@ delete table inet mesh_guard table inet mesh_guard { chain prerouting { type filter hook prerouting priority raw; policy accept; - iifname != "lo" iifname != "docker0" iifname != "br-*" iifname != "mesh0" tcp dport { 5432, 15672 } drop + fib daddr type local iifname != "lo" iifname != "docker0" iifname != "br-*" iifname != "mesh0" tcp dport { 5432, 15672 } drop } } ` if got := AsGuard([]int{15672, 5432}); got != golden { t.Fatalf("the guard changed:\n%s", got) } + const unit = `[Unit] +Description=The mesh's guard: refuses its own ports from outside (novox/hq ADR 0100) +DefaultDependencies=no +Wants=network-pre.target +Before=network-pre.target shutdown.target +Conflicts=shutdown.target + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=nft -f /etc/mesh/guard.nft +ExecReload=nft -f /etc/mesh/guard.nft +ExecStop=nft delete table inet mesh_guard + +[Install] +WantedBy=multi-user.target +` + if got := GuardUnitText(); got != unit { + t.Fatalf("the guard's unit changed:\n%s", got) + } if GuardResources(nil) != nil { t.Fatal("a guard with nothing to guard is an empty set nft refuses to load") }