Episode 10: hardening - firewall rules Docker cannot bypass, Caddy access log and rate limiting, fail2ban in the right chain
- 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
This commit is contained in:
parent
ccbddab46c
commit
bed41cfaf3
12 changed files with 319 additions and 16 deletions
8
hardening/fail2ban/filter.d/caddy-probes.conf
Normal file
8
hardening/fail2ban/filter.d/caddy-probes.conf
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
# /etc/fail2ban/filter.d/caddy-probes.conf (episode 10)
|
||||
# Matches the JSON access log: bots that harvest 401/403/404/429 (probing for
|
||||
# /.env, /wp-login.php, /xmlrpc.php, or hammering a login past the rate limit).
|
||||
# The whole log entry is one line; client_ip sits in the request object and
|
||||
# status at the top level, so client_ip always comes before status.
|
||||
[Definition]
|
||||
failregex = ^.*"client_ip":"<HOST>".*"status":(401|403|404|429)\b.*$
|
||||
ignoreregex =
|
||||
33
hardening/fail2ban/jail.local
Normal file
33
hardening/fail2ban/jail.local
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
# /etc/fail2ban/jail.local (episode 10)
|
||||
[DEFAULT]
|
||||
bantime = 1h
|
||||
findtime = 10m
|
||||
maxretry = 5
|
||||
# localhost and the NetBird mesh range are never banned. Do NOT put your own
|
||||
# home/office IP here - if you never guess passwords you never get banned, and
|
||||
# an ignore entry is one more secret to keep out of a public repo.
|
||||
ignoreip = 127.0.0.1/8 ::1 100.125.0.0/16
|
||||
banaction = iptables-multiport
|
||||
|
||||
[sshd]
|
||||
enabled = true
|
||||
# Debian 13 ships no /var/log/auth.log without rsyslog; read the journal instead.
|
||||
backend = systemd
|
||||
maxretry = 5
|
||||
findtime = 10m
|
||||
bantime = 1h
|
||||
|
||||
[caddy-probes]
|
||||
enabled = true
|
||||
backend = auto
|
||||
logpath = /opt/selfhostedops-stack/logs/caddy/access.log
|
||||
filter = caddy-probes
|
||||
maxretry = 10
|
||||
findtime = 1m
|
||||
bantime = 1h
|
||||
port = http,https
|
||||
# The ban has to land in DOCKER-USER, not INPUT - Caddy's traffic is forwarded,
|
||||
# a ban in INPUT never touches it and the "banned" bot keeps getting answers.
|
||||
# chain must be a plain parameter here: the default action_ template passes
|
||||
# chain="%(chain)s", so putting [chain=...] inside banaction gets overridden.
|
||||
chain = DOCKER-USER
|
||||
Loading…
Add table
Add a link
Reference in a new issue