Skip to content

Security model

Goal

Shrink what the internet can touch, keep credentials out of git/docs, and make abuse noisy but low-impact.

Edge posture

Only three service classes face the world:

PortRoleControls
22/tcpAdministrationKeys, Fail2Ban, optional future Tailscale-only
80/tcpACME + redirectCaddy
443/tcpAll appsCaddy TLS, per-host routing

Everything else listens on loopback. UFW defaults deny incoming; Docker publishes application ports as 127.0.0.1:… so a stray -p 8080:80 on all interfaces is treated as a defect.

Defense in depth (what each layer buys)

  • UFW/nftables — coarse port filter
  • Fail2Ban (sshd) — short bans on password/noise storms (default 10m/5 tries — consider longer bans)
  • AppArmor — host + docker-default confinement
  • Unattended upgrades — security patches without waiting for a human
  • etckeeper — forensic trail for /etc edits
  • TLS at Caddy — no cleartext app exposure

No single layer is perfect; together they force an attacker through SSH or a web app bug, not a random database port.

Identity

  • Human operator: ty with sudo
  • Root SSH: key-only policy in sshd; prefer not logging in as root at all
  • App secrets: .env beside compose, Vaultwarden as organizational source of truth — never paste secrets into MkDocs

Residual risk (documented honestly)

  1. SSH on the public internet — scanners are constant; key-only helps, VPN/Tailscale would help more.
  2. Speculative CPU issues — “Spec rstack overflow” called out as not fully mitigated without host/microcode cooperation.
  3. Concentrated host — one VM holds many apps; backups and stack isolation matter.
  4. Short Fail2Ban window — 10 minutes is mild.
  5. Snapshot-style docs — status tables drift; verify live when it matters (SoloOps: live-verified truth).

Northeast Systems · Aux Server · Infra Server