- 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 |
||
|---|---|---|
| .. | ||
| Dockerfile | ||
| README.md | ||
caddy/ (episode 10)
Custom Caddy image with the rate limiting plugin compiled in.
The stock caddy:2-alpine has no rate_limit directive; it is not a runtime
flag, the module must be built into the binary with xcaddy. compose.yml
carries both image: and build: ./caddy, so docker compose build caddy
produces selfhostedops/caddy:ratelimit locally and docker compose up -d
uses it.
Do not build on a small box while the stack is running - a Go build can push a 2 GB machine into OOM and take the live services down with it. Build it elsewhere (a bigger machine, or a throwaway host) and move it over:
docker build -t selfhostedops/caddy:ratelimit ./caddy
docker save selfhostedops/caddy:ratelimit | ssh root@box docker load