73 lines
3.2 KiB
Go
73 lines
3.2 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)
|
|
}
|
|
}
|
|
|
|
// A changed pattern restarts fail2ban (novox/hq issue 191's rollout): the service restarts when the
|
|
// composed jail file changes, and the filter is a file of its own, so the jail file names the
|
|
// filter's digest. Changing only the failregex must change the jail file; the same pattern must not.
|
|
func TestAChangedFilterChangesTheJailFile(t *testing.T) {
|
|
jailFile := func(failregex string) string {
|
|
modules := []Manifest{
|
|
{Module: "fail2ban", Jailing: &Jailing{Into: "/etc/fail2ban/jail.d/mesh.conf", FilterInto: "/etc/fail2ban/filter.d"}},
|
|
{Module: "gitea", Jails: []Jail{{Name: "gitea", Failregex: failregex, Jail: "port = 222"}}},
|
|
}
|
|
for _, f := range jailsInto(modules, modules[0].Jailing) {
|
|
if f["id"] == ComposedJailsID() {
|
|
return f["content"].(string)
|
|
}
|
|
}
|
|
t.Fatal("no composed jail file")
|
|
return ""
|
|
}
|
|
before := jailFile("web login failed from <HOST>")
|
|
if again := jailFile("web login failed from <HOST>"); again != before {
|
|
t.Errorf("the same pattern composed a different jail file, which would restart fail2ban for nothing")
|
|
}
|
|
if after := jailFile("web login failed from <HOST>\n Invalid user .* from <HOST>"); after == before {
|
|
t.Errorf("a changed pattern left the jail file as it was, so fail2ban keeps the old filter:\n%s", after)
|
|
}
|
|
}
|