package catalogue import ( "strings" "testing" ) // A node's fail2ban jails are composed from the modules it runs (novox/hq to-be 31): the holder // (jailing) gathers every module's declared jail into one jail file and a filter file per jail. func TestJailsAreComposedFromTheNodesModules(t *testing.T) { modules := []Manifest{ {Module: "fail2ban", Jailing: &Jailing{Into: "/etc/fail2ban/jail.d/mesh-composed.conf", FilterInto: "/etc/fail2ban/filter.d"}}, {Module: "postgres", Jails: []Jail{{Name: "postgres-auth", Failregex: "auth failed from ", Jail: "port = 5432\nmaxretry = 5"}}}, } files := jailsInto(modules, modules[0].Jailing) by := map[string]map[string]any{} for _, f := range files { by[f["id"].(string)] = f } jail := by[ComposedJailsID()] if jail == nil || jail["path"] != "/etc/fail2ban/jail.d/mesh-composed.conf" { t.Fatalf("the composed jail file was not written: %v", jail) } body := jail["content"].(string) if !strings.Contains(body, "[postgres-auth]") || !strings.Contains(body, "filter = postgres-auth") || !strings.Contains(body, "port = 5432") { t.Fatalf("the postgres jail stanza was not composed in:\n%s", body) } filter := by["filter-postgres-auth"] if filter == nil || filter["path"] != "/etc/fail2ban/filter.d/postgres-auth.conf" { t.Fatalf("the jail's filter file was not written: %v", filter) } if !strings.Contains(filter["content"].(string), "failregex = auth failed from ") { t.Fatalf("the failregex was not written: %v", filter["content"]) } } // A holder whose node runs no jail-declaring module still gets the file, empty — so removing the // last jail is a change the service restarts on, not a file that vanishes. func TestTheComposedJailFileIsWrittenEvenWhenEmpty(t *testing.T) { files := jailsInto([]Manifest{{Module: "fail2ban"}}, &Jailing{Into: "/x", FilterInto: "/f"}) if len(files) != 1 || files[0]["id"] != ComposedJailsID() { t.Fatalf("the empty composed jail file was not written alone: %v", files) } }