@tatterdemalion@programming.dev
2026-09-20 05:05 UTC
Secrets
Encrypted secrets file. All repo-managed credentials live in secrets/secrets.yaml, encrypted with SOPS/age.
Encrypted OpenTofu state. State and plans are encrypted client-side (PBKDF2 + AES-GCM) with a passphrase from TF_VAR_state_passphrase.
Network perimeter
Default-drop firewall on the router. I use a hand-written nftables ruleset with policy drop on both input and forward. Only lo, lan0 and wg0 are trusted. WAN accepts only established traffic, WireGuard UDP and HTTP/HTTPS.
No SSH from the WAN. sshd on the router is not exposed to the internet at all.
WireGuard for remote access.
Forced DNS. NAT redirects all LAN and VPN port-53 traffic to the router’s AdGuard. AdGuard uses Quad9 over DoT upstream, with blocklists and safe search.
Second-level ACL in nginx. Private .home vhosts and Grafana carry allow LAN; allow VPN; deny all;.
TLS everywhere. Every public vhost sets forceSSL with recommendedTlsSettings. Certificates come from ACME DNS-01, so issuance needs no inbound port 80. Cloudflare is DNS-only with no proxy, so no third party sees plaintext.
Host access
SSH hardening. Password and keyboard-interactive auth are off, PermitRootLogin = “no”, and logins are key-only.
Dedicated deployer system user. It uses a CI-only keypair, separate from the interactive keys. Everything it does through sudo is logged with LOG_INPUT/LOG_OUTPUT to /var/log/sudo-deployer.log.
Replies (0)
No replies.