Hardening and exposure
Home racks rarely get breached; they get exposed. So start with the ports. Below is every public port across the published stack — at the moment that is the reverse proxy and the VPN endpoint only. Everything else listens on the LAN or is reached through WireGuard.
Facing the internet
| service | port | used for |
|---|---|---|
| WireGuard (wg-easy) | :51820 | the tunnel itself; the only UDP port forwarded at the router |
| Caddy | :80 | ACME HTTP-01 challenge and the permanent redirect to https |
| Caddy | :443 | every web front end on the rack, one hostname each |
Rules that keep showing up
- 1×registration is disabled and accounts are created by the admin only
- 1×the SSH port for git is 2222, not 22, so a blocked or forwarded port 22 can never reach the repository service by accident
- 1×two-factor is required for the admin account, and the API tokens in use are scoped read-only except the one the backup job uses
- 1×LFS is enabled but object storage stays local; large binaries are discouraged in the contributing notes rather than silently filling the SSD
- 1×anonymous access is off and the admin password comes from the environment on first boot
- 1×the data source uses a read-only Prometheus user, so a compromised dashboard cannot write metrics
- 1×the panel is behind the proxy's LAN-only rule, never forwarded
- 1×the public dashboards feature is disabled, since there is nothing here to share publicly
- 1×registration is closed after the household accounts are created, so the instance cannot be joined by anyone who finds the URL
- 1×upload access is over the LAN or through WireGuard, never a forwarded port
- 1×the database listens on the container network only and has no published port at all
- 1×the machine learning container has no ports and no internet access beyond the model download at first start
- 1×the UI is on the LAN only and additionally behind basic auth at the proxy; it is never forwarded
- 1×one client per device, named, so a lost phone can be revoked by deleting exactly one profile
Three rules that do not live in a compose file
- Admin panels bind to 127.0.0.1 by default; reach them over an SSH port-forward, never publish them on 0.0.0.0
- Passwords and API keys live in /srv/homelab/.env, mode 600, read through env_file — never committed
- Quarterly drill: kill one container at random and restore it from backup onto a fresh SD card