From c93128d82fc3fc1a5f17c0c7a598feb9b12a5e06 Mon Sep 17 00:00:00 2001 From: jochen Date: Tue, 22 Sep 2026 17:33:09 +0200 Subject: [PATCH] Hold the catalogue's store, broker and filter manifests to what adoption needs of them (hq ADR 0100) --- internal/catalogue/adoption.go | 2 +- .../catalogue/foundation_manifests_test.go | 66 +++++++++++++++++++ 2 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 internal/catalogue/foundation_manifests_test.go diff --git a/internal/catalogue/adoption.go b/internal/catalogue/adoption.go index 5e31ef0..c165944 100644 --- a/internal/catalogue/adoption.go +++ b/internal/catalogue/adoption.go @@ -186,7 +186,7 @@ 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" + - "After=network-pre.target\n" + + "Before=network-pre.target\n" + "Wants=network-pre.target\n" + "\n" + "[Service]\n" + diff --git a/internal/catalogue/foundation_manifests_test.go b/internal/catalogue/foundation_manifests_test.go new file mode 100644 index 0000000..08886c4 --- /dev/null +++ b/internal/catalogue/foundation_manifests_test.go @@ -0,0 +1,66 @@ +package catalogue + +import ( + "os" + "reflect" + "strings" + "testing" +) + +// The catalogue's foundation modules as they are, parsed by the real parser (novox/hq ADR 0100): +// the store and the broker say which of their ports the mesh guards on an adopted node, and the +// filter module loads its table through a unit of its own whose stop deletes only that table. +func catalogueManifest(t *testing.T, module string) Manifest { + t.Helper() + raw, err := os.ReadFile("../../../mesh-catalog/modules/" + module + "/module.json") + if err != nil { + t.Skipf("the catalogue is not beside this checkout: %v", err) + } + m, err := ParseManifest(raw) + if err != nil { + t.Fatalf("%s does not parse:\n%v", module, err) + } + return m +} + +func TestTheStoreAndTheBrokerSayWhatTheMeshGuards(t *testing.T) { + if got := catalogueManifest(t, "postgres").Guards; !reflect.DeepEqual(got, []int{5432}) { + t.Errorf("postgres guards %v; the store's port must be refused from outside", got) + } + if got := catalogueManifest(t, "lavinmq").Guards; !reflect.DeepEqual(got, []int{15672}) { + t.Errorf("lavinmq guards %v; the management port must be refused from outside", got) + } +} + +func TestTheFilterModuleNeverFlushesTheRuleset(t *testing.T) { + m := catalogueManifest(t, "nftables") + var unit map[string]any + var load map[string]any + for _, r := range m.Resources { + switch r["id"] { + case "unit": + unit = r + case "load": + load = r + } + } + if load == nil || load["unit"] != "mesh-filter.service" { + t.Fatalf("the filter is not loaded by its own unit: %v", load) + } + content, _ := unit["content"].(string) + if unit == nil || unit["path"] != "/etc/systemd/system/mesh-filter.service" { + t.Fatalf("the filter's unit is not written: %v", unit) + } + if strings.Contains(content, "flush") { + t.Fatalf("stopping the filter flushes the whole ruleset — the runtime's and the found "+ + "firewall's with it:\n%s", content) + } + if !strings.Contains(content, "ExecStop=nft delete table inet mesh\n") || + !strings.Contains(content, "ExecStart=nft -f "+m.Filtering.Into+"\n") { + t.Fatalf("the unit does not load the computed rule set and delete only its own table:\n%s", + content) + } + if !reflect.DeepEqual(load["restart-on"], []any{"filtering", "unit"}) { + t.Fatalf("the filter is not reloaded when its rules or its unit change: %v", load["restart-on"]) + } +}