Files
mesh-catalog/modules/fail2ban/module.json
T
jschoubben 7c18cdbd39 fail2ban: declare the log its own recidive jail reads
The recidive jail bans whoever keeps coming back by reading fail2ban's own
log, and fail2ban checks every jail's log file while it configures itself --
before it has created that log. On a machine where the file is not there
already, no jail is found for recidive, configuration fails, and the whole
service refuses to start, taking the sshd jail with it. Two machines assigned
this module today came up failed for exactly that reason; the two where it
worked had a log from years of the service running.

Declared create-once: the mesh puts an empty file there when it is absent and
never touches it again, because what grows in it is fail2ban's, and the
logrotate file this module already ships is what keeps it small.

This also reverts the previous two commits' fail2ban.local. It declared a
logtarget that the package already sets to the same path on every machine
here -- pacman reports the config pristine -- so it fixed nothing and said
something untrue about why.
2026-09-28 20:45:32 +02:00

89 lines
4.1 KiB
JSON

{
"module": "fail2ban",
"version": "1",
"capabilities": [
"firewall"
],
"claims": [
{
"name": "node-intrusion-prevention",
"scope": "node"
}
],
"resources": [
{
"id": "package",
"type": "package",
"package": "fail2ban"
},
{
"id": "jail-d",
"type": "directory",
"path": "/etc/fail2ban/jail.d",
"mode": "0755"
},
{
"id": "action-d",
"type": "directory",
"path": "/etc/fail2ban/action.d",
"mode": "0755"
},
{
"id": "jail-local",
"type": "file",
"path": "/etc/fail2ban/jail.local",
"mode": "0644",
"content": "[INCLUDES]\n\nbefore = paths-arch.conf\n\n[DEFAULT]\n\n# Never act on the machine itself or on a tunnel peer: the mesh's private range is\n# ${machine:mesh-range}, named here rather than written as a value the module cannot\n# know (novox/hq ADR 0112). Without this, fail2ban could ban the mesh's own nodes.\nignoreip = 127.0.0.1/8 ::1 ${machine:mesh-range}\n\nbantime = 10m\nfindtime = 10m\nmaxretry = 5\n\nbanaction = ufw\nbanaction_allports = iptables-allports\n\n[sshd]\nenabled = true\nport = ssh\nlogpath = %(sshd_log)s\nbackend = %(sshd_backend)s\n"
},
{
"id": "jail-sshd",
"type": "file",
"path": "/etc/fail2ban/jail.d/sshd.conf",
"mode": "0644",
"content": "[sshd]\nenabled = true\nport = ssh\nlogpath = %(sshd_log)s\nbackend = %(sshd_backend)s\nmaxretry = 5\n"
},
{
"id": "log",
"type": "file",
"path": "/var/log/fail2ban.log",
"mode": "0640",
"create-once": true,
"content": ""
},
{
"id": "jail-recidive",
"type": "file",
"path": "/etc/fail2ban/jail.d/recidive.conf",
"mode": "0644",
"content": "[recidive]\nenabled = true\nlogpath = /var/log/fail2ban.log\n# Ban in both INPUT (host services like SSH) and DOCKER-USER (container services)\nbanaction = iptables-allports-dualchain\nbantime = 1w\nfindtime = 1d\n"
},
{
"id": "action-dualchain",
"type": "file",
"path": "/etc/fail2ban/action.d/iptables-allports-dualchain.conf",
"mode": "0644",
"content": "# Fail2Ban action: ban in both INPUT and DOCKER-USER chains\n# Used by recidive to block repeat offenders from both host and Docker services\n\n[INCLUDES]\n\nbefore = iptables.conf\n\n[Definition]\n\ntype = allports\n\nactionstart = { <iptables> -C f2b-<name> -j <returntype> >/dev/null 2>&1; } || { <iptables> -N f2b-<name> || true; <iptables> -A f2b-<name> -j <returntype>; }\n { <iptables> -C INPUT -p <protocol> -j f2b-<name> >/dev/null 2>&1; } || { <iptables> -I INPUT -p <protocol> -j f2b-<name>; }\n { <iptables> -C DOCKER-USER -p <protocol> -j f2b-<name> >/dev/null 2>&1; } || { <iptables> -I DOCKER-USER -p <protocol> -j f2b-<name>; }\n\nactionstop = <iptables> -D INPUT -p <protocol> -j f2b-<name> 2>/dev/null || true\n <iptables> -D DOCKER-USER -p <protocol> -j f2b-<name> 2>/dev/null || true\n <iptables> -F f2b-<name>\n <iptables> -X f2b-<name>\n\nactioncheck = <iptables> -n -L f2b-<name> >/dev/null\n\nactionban = <iptables> -I f2b-<name> 1 -s <ip> -j <blocktype>\n\nactionunban = <iptables> -D f2b-<name> -s <ip> -j <blocktype>\n\n[Init]\n\nchain = INPUT\nname = default\nprotocol = tcp\nblocktype = REJECT --reject-with icmp-port-unreachable\nreturntype = RETURN\nlockingopt = -w\niptables = iptables <lockingopt>\n\n[Init?family=inet6]\n\nblocktype = REJECT --reject-with icmp6-port-unreachable\niptables = ip6tables <lockingopt>\n"
},
{
"id": "logrotate",
"type": "file",
"path": "/etc/logrotate.d/fail2ban",
"mode": "0644",
"content": "/var/log/fail2ban.log {\n missingok\n notifempty\n postrotate\n /usr/bin/fail2ban-client flushlogs >/dev/null || true\n endscript\n}\n"
},
{
"id": "run",
"type": "service",
"unit": "fail2ban.service",
"state": "running",
"boot": "enabled",
"restart-on": [
"jail-local",
"jail-sshd",
"jail-recidive",
"action-dualchain"
]
}
]
}