The mechanism, mirroring Filtering: a module declares Jails (name, failregex, jail stanza) naming no node/path (ADR 0112); the intrusion-prevention holder declares Jailing (where composed jails go); the mesh gathers every assigned module's jails into one jail.d file (a fixed id the fail2ban service restarts on) plus a filter.d file per jail. A node not running a module has none of its jails. Tested. Behaviour-neutral until a service module declares a jail — the per-service content (postgres/mssql/mailu failregex+logpath) is authored next, against how each container actually logs.
47 lines
2.0 KiB
Go
47 lines
2.0 KiB
Go
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 <HOST>", 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 <HOST>") {
|
|
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)
|
|
}
|
|
}
|