updates/health.sh: 43 checks across all services (API answers, git refs, a push
message round trip, SSO redirects and token endpoint, mesh state, backup age).
updates/update.sh: per service pull or build, snapshot, recreate, wait for the
health check, put image and data back on red and stop the run.
backup/backup.sh: online dumps for n8n, Pocket ID and tinyauth; BACKUP_TAG so
pre-update snapshots are not removed by the nightly keep-daily rule.
compose.yml and caddy/Dockerfile: exact versions instead of latest, 16, 7-alpine
and caddy:2-alpine.
Measured on the demo box against the same 24h of real traffic.
Episode 12 (detection, blocklist, firewall):
- crowdsec/ with install notes and the traps: port 8080 clash, empty first
blocklist pull, replay forensic-mode dropping events (FIFO workaround),
ssh-time-based-bf shipping remediation:false, the AppSec collections that
refuse to start without extra rules.
- firewall bouncer in nftables mode (input + forward), which blocks
Docker-published ports and survives ufw reload; iptables/DOCKER-USER does not.
- HARDENING.md episode 12 section with the numbers: fail2ban 4 banned IPs vs
CrowdSec 23 detected / 1 blocked, blocklist overlap 50% of IPs but 19% of
attempts, and the mode=normal blind spot for key-only root attempts.
Episode 13 (AppSec WAF):
- caddy/Dockerfile now builds the CrowdSec bouncer and AppSec handler alongside
rate-limit, pinned to v0.14.1.
- Caddyfile imports a crowdsec snippet (bouncer + appsec) on every site except
vpn; compose passes CROWDSEC_BOUNCER_KEY and host.docker.internal.
- Measured: virtual patching blocks known CVEs only (7 of 41 probes, near-zero
false positives); OWASP CRS raises that to 27 of 41 but breaks git push and
wiki editing at default paranoia. Default stays vpatch+generic.
fail2ban config unchanged. No secrets or real domains/IPs in tracked files.
- hardening/: ufw after.rules block for DOCKER-USER (ctorigdstport, not dport),
fail2ban jail.local with sshd (systemd backend) and caddy-probes (chain DOCKER-USER),
filter for the JSON access log, sshd hardening.conf, README with the order and
the two traps measured in the episode
- caddy/Dockerfile: xcaddy build with caddy-ratelimit, compose builds the image
- Caddyfile: common snippet (JSON access log, Server header stripped), vault /admin 404,
rate_limit zones on the vault token endpoint and the git login form
- HARDENING.md: firewall and fail2ban sections rewritten around what the port scan saw
Claude-Session: https://claude.ai/code/session_017iP581CYtPfxdZYPPAyWZA