Viktor got locked out of the break-glass path (forgot the port-knock setup) and deleted the edge-router forwards, then asked to review and redesign it from scratch. Root cause of the lockout: the knock added no real security (key-only SSH is already brute-force-proof) and its only benefit — hiding the port — came at the cost of a circular dependency. The knock sequence lived only in in-cluster Vault, which is unreachable in the exact away/cold scenario break-glass exists for. So the unlock secret was unavailable precisely when needed. New model (self-contained, nothing to remember): plain key-only SSH on the Proxmox host's :52222, openly reachable. The edge router forwards WAN tcp/52222 -> 192.168.1.127:52222 (external port MUST equal internal on the TP-Link AX6000 - it rejects remaps; port 22 itself is reserved). The exposed port trusts only a dedicated break-glass key via `Match LocalPort` (a leak of any other root key does not grant internet access), rate-limited (iptables hashlimit) + fail2ban. - Removed knockd (package + config) and the legacy Synology SSH forward (ext 3333 -> .13:22, a needless WAN exposure the original plan wanted gone). - Fixed the fail2ban jail for Debian 13 (auth logs under sshd-session, not sshd - the stock journalmatch silently never banned). - Versioned the host config in scripts/ (it was applied ad-hoc, never committed) and recorded the deliberate Wave-1 "no public-IP" exception in security.md + .claude/CLAUDE.md. Superseded the 2026-05-30 port-knock design docs. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
18 lines
767 B
Text
18 lines
767 B
Text
# Break-glass SSH fail2ban jail (redesigned 2026-06-11). Source of truth.
|
|
# Deploy to the PVE host with:
|
|
# scp scripts/fail2ban-breakglass-sshd.local root@192.168.1.127:/etc/fail2ban/jail.d/breakglass-sshd.local
|
|
# ssh root@192.168.1.127 'systemctl restart fail2ban'
|
|
#
|
|
# GOTCHA (Debian 13 / OpenSSH 9.x): auth lines are logged under
|
|
# _COMM=sshd-session, NOT _COMM=sshd. The stock Debian jail keys journalmatch on
|
|
# `_SYSTEMD_UNIT=ssh.service + _COMM=sshd` and therefore silently NEVER bans.
|
|
# Match by unit only so both sshd and sshd-session lines are seen. Ban on both
|
|
# SSH ports (the WAN break-glass listener is :52222).
|
|
[sshd]
|
|
enabled = true
|
|
backend = systemd
|
|
journalmatch = _SYSTEMD_UNIT=ssh.service
|
|
port = ssh,52222
|
|
maxretry = 4
|
|
findtime = 10m
|
|
bantime = 1h
|