package catalogue import ( "strings" "testing" ) // A jail whose pattern names twice is refused by name: fail2ban would read it as a duplicate // capture group and refuse its whole configuration, leaving the machine with no bans at all — which // is what happened on the control node the day this was built (novox/hq ADR 0179). func TestAJailsPatternNamesTheHostOncePerShape(t *testing.T) { twice := Manifest{Module: "route-proxy", Jails: []Jail{{ Name: "route-proxy", Failregex: `^.*(?:refused for :\d+|asked from :\d+)$`, Jail: "backend = systemd", }}} problems := twice.jailProblems() if len(problems) != 1 || !strings.Contains(problems[0], "names 2 times") { t.Fatalf("a pattern naming twice was not refused by name: %v", problems) } if !strings.Contains(problems[0], "no bans at all") { t.Errorf("the refusal does not say what it costs: %q", problems[0]) } // The same two shapes, one pattern each, is how it is written — and is accepted. split := Manifest{Module: "route-proxy", Jails: []Jail{{ Name: "route-proxy", Failregex: "^.*refused for :\\d+$\n ^.*asked from :\\d+$", Jail: "backend = systemd", }}} if problems := split.jailProblems(); len(problems) != 0 { t.Fatalf("one per pattern was refused: %v", problems) } } // A jail with no name, no pattern, or a name another jail of the same module already took is refused. func TestAJailIsNamedOnceAndSaysWhatAFailureLooksLike(t *testing.T) { m := Manifest{Module: "mailu", Jails: []Jail{ {Name: "", Failregex: "^.*rip=$"}, {Name: "mailu-front", Failregex: " "}, {Name: "mailu-front", Failregex: "^.*rip=$"}, }} problems := strings.Join(m.jailProblems(), "\n") for _, want := range []string{"a jail with no name", "says nothing a failed attempt looks like", "two jails called"} { if !strings.Contains(problems, want) { t.Errorf("missing %q in:\n%s", want, problems) } } }