fix(fail2ban): pin polling backend so jail actually reads Caddy access log (#503) #504

Merged
marcel merged 1 commits from fix/issue-503-fail2ban-polling-backend into main 2026-05-11 15:08:59 +02:00
Owner

Summary

Closes #503.

Discovered during the production-deploy bootstrap for #497. fail2ban came up cleanly, the familienarchiv-auth jail loaded, but fail2ban-client get familienarchiv-auth logpath returned No file is currently monitored. The jail had silently inherited Debian's [DEFAULT] backend = systemd from /etc/fail2ban/jail.d/defaults-debian.conf and was reading from journald, not /var/log/caddy/access.log. A real login brute-force would never have been banned.

Fix

  • infra/fail2ban/jail.d/familienarchiv.conf — add backend = polling with a comment explaining why (override Debian default, no inotify dependency, fine for one rotated log).
  • .gitea/workflows/ci.yml — new step in the fail2ban-regex job that symlinks the jail+filter into /etc/fail2ban/ and asserts fail2ban-client -d resolves familienarchiv-auth to polling, not the inherited systemd. Catches a regression of the same shape at PR time.

Why the existing CI test did not catch this

fail2ban-regex runs the filter against a sample line in isolation. It validates regex correctness, not backend resolution. The new step instantiates the jail through fail2ban-client -d so defaults-debian.conf participates in the resolution.

Test plan

  • fail2ban-client -d on a freshly-bootstrapped server confirms the broken state without this fix (['add', 'familienarchiv-auth', 'systemd']).
  • fail2ban-regex CI job — Jail resolves with polling backend step passes (asserts the fix).
  • After merge: on the production server, cd /opt/familienarchiv && git pull && systemctl reload fail2ban, then fail2ban-client get familienarchiv-auth logpath returns /var/log/caddy/access.log.

🤖 Generated with Claude Code

## Summary Closes #503. Discovered during the production-deploy bootstrap for #497. fail2ban came up cleanly, the `familienarchiv-auth` jail loaded, but `fail2ban-client get familienarchiv-auth logpath` returned `No file is currently monitored`. The jail had silently inherited Debian's `[DEFAULT] backend = systemd` from `/etc/fail2ban/jail.d/defaults-debian.conf` and was reading from journald, not `/var/log/caddy/access.log`. A real login brute-force would never have been banned. ## Fix - `infra/fail2ban/jail.d/familienarchiv.conf` — add `backend = polling` with a comment explaining *why* (override Debian default, no inotify dependency, fine for one rotated log). - `.gitea/workflows/ci.yml` — new step in the `fail2ban-regex` job that symlinks the jail+filter into `/etc/fail2ban/` and asserts `fail2ban-client -d` resolves `familienarchiv-auth` to `polling`, not the inherited `systemd`. Catches a regression of the same shape at PR time. ## Why the existing CI test did not catch this `fail2ban-regex` runs the filter against a sample line in isolation. It validates regex correctness, not backend resolution. The new step instantiates the jail through `fail2ban-client -d` so `defaults-debian.conf` participates in the resolution. ## Test plan - [x] `fail2ban-client -d` on a freshly-bootstrapped server confirms the broken state without this fix (`['add', 'familienarchiv-auth', 'systemd']`). - [ ] `fail2ban-regex` CI job — `Jail resolves with polling backend` step passes (asserts the fix). - [ ] After merge: on the production server, `cd /opt/familienarchiv && git pull && systemctl reload fail2ban`, then `fail2ban-client get familienarchiv-auth logpath` returns `/var/log/caddy/access.log`. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
marcel added 1 commit 2026-05-11 15:00:31 +02:00
fix(fail2ban): pin polling backend so jail actually reads Caddy access log
Some checks failed
CI / Unit & Component Tests (push) Failing after 2m49s
CI / OCR Service Tests (push) Successful in 16s
CI / Backend Unit Tests (push) Successful in 4m8s
CI / fail2ban Regex (push) Successful in 37s
CI / Compose Bucket Idempotency (push) Failing after 53s
CI / Unit & Component Tests (pull_request) Failing after 2m46s
CI / OCR Service Tests (pull_request) Successful in 15s
CI / Backend Unit Tests (pull_request) Successful in 4m14s
CI / fail2ban Regex (pull_request) Successful in 37s
CI / Compose Bucket Idempotency (pull_request) Failing after 50s
e5363913ec
Closes #503.

Debian's fail2ban package ships defaults-debian.conf with
`[DEFAULT] backend = systemd`. Without an explicit override, our
familienarchiv-auth jail inherits the systemd backend at runtime,
reads from journald, and never inspects /var/log/caddy/access.log.
A live login brute-force would not be banned.

Add `backend = polling` to the jail and a CI step that links the jail
into /etc/fail2ban/ and asserts `fail2ban-client -d` resolves it to
the polling backend, not the inherited systemd backend.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
marcel merged commit c3c1efe5f1 into main 2026-05-11 15:08:59 +02:00
marcel deleted branch fix/issue-503-fail2ban-polling-backend 2026-05-11 15:08:59 +02:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: marcel/familienarchiv#504