Merge pull request 'A changed jail filter restarts fail2ban' (#231) from jschoubben/jail-filter-restart into main
This commit was merged in pull request #231.
This commit is contained in:
@@ -1,6 +1,8 @@
|
|||||||
package catalogue
|
package catalogue
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -43,8 +45,16 @@ func jailsInto(modules []Manifest, j *Jailing) []map[string]any {
|
|||||||
|
|
||||||
out := make([]map[string]any, 0, len(jails)+1)
|
out := make([]map[string]any, 0, len(jails)+1)
|
||||||
for _, d := range jails {
|
for _, d := range jails {
|
||||||
fmt.Fprintf(&composed, "\n# from %s\n[%s]\nenabled = true\nfilter = %s\n%s\n",
|
// **The filter's digest rides in the jail file.** fail2ban is restarted when this file
|
||||||
d.module, d.jail.Name, d.jail.Name, strings.TrimRight(d.jail.Jail, "\n"))
|
// changes, and the filter is a file of its own: a module that changed only what a failure
|
||||||
|
// looks like rewrote the filter on disk and left the running jail on the old pattern, with
|
||||||
|
// nothing said (novox/hq issue 191's rollout found it on gitea's sshd). Naming the filter's
|
||||||
|
// digest here makes a changed pattern a changed jail file, so the restart the service
|
||||||
|
// already takes on it covers the filter too.
|
||||||
|
sum := sha256.Sum256([]byte(d.jail.Failregex))
|
||||||
|
fmt.Fprintf(&composed, "\n# from %s, filter %s\n[%s]\nenabled = true\nfilter = %s\n%s\n",
|
||||||
|
d.module, hex.EncodeToString(sum[:])[:12], d.jail.Name, d.jail.Name,
|
||||||
|
strings.TrimRight(d.jail.Jail, "\n"))
|
||||||
// The filter is a file of its own, named as the jail's filter= references it.
|
// The filter is a file of its own, named as the jail's filter= references it.
|
||||||
out = append(out, map[string]any{
|
out = append(out, map[string]any{
|
||||||
"id": "filter-" + d.jail.Name,
|
"id": "filter-" + d.jail.Name,
|
||||||
|
|||||||
@@ -44,3 +44,29 @@ func TestTheComposedJailFileIsWrittenEvenWhenEmpty(t *testing.T) {
|
|||||||
t.Fatalf("the empty composed jail file was not written alone: %v", files)
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user