docs/deployment: record security hardening pass + webapp + APNs
Backend CI / Test (push) Has been cancelled
Backend CI / Contract Tests (push) Has been cancelled
Backend CI / Build (push) Has been cancelled
Backend CI / Lint (push) Has been cancelled
Backend CI / Secret Scanning (push) Has been cancelled

Mark roadmap items done (network policies, Traefik middleware, CF Full
strict, CF IP UFW restriction, webapp deploy, APNs wired up, admin
URL-baking fix, admin probe bug). Update Chapter 4 (firewall rule
inventory now shows CF-only :443, no :80), Chapter 6 (request flow
walks through TLS on :443 and middleware hops), Chapter 13 (CF SSL
mode is Full strict, not Flexible; documents the origin cert
install), Chapter 7 (adds the web service section — proxy pattern,
3 replicas, PostHog build-args), and Appendix C (web manifests, CF
origin cert paths on disk, APNs .p8 path, updated network-policies
applied status).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Trey t
2026-04-24 15:50:59 -05:00
parent ace03d2340
commit 7e77e3bbab
6 changed files with 198 additions and 124 deletions
+18 -14
View File
@@ -27,23 +27,27 @@ that every legitimate port be enumerated in a rule.
Run `sudo ufw status verbose` on any node to see the live ruleset. The
canonical ruleset below, grouped by purpose.
### Public-facing (anywhere)
### Public-facing
| Port | Protocol | From | Purpose | Comment |
|---|---|---|---|---|
| 22 | TCP | Anywhere | SSH | |
| 80 | TCP | Anywhere | HTTP (Cloudflare → Traefik) | |
| 443 | TCP | Anywhere | HTTPS (future, currently unused at origin) | |
| Port | Protocol | From | Purpose |
|---|---|---|---|
| 22 | TCP | Anywhere | SSH (key-only) |
| 443 | TCP | Cloudflare ranges (15 IPv4 + 7 IPv6) | HTTPS (CF → Traefik, TLS-terminated at Traefik) |
**Why 443 is open but unused**: We're on Cloudflare SSL=Flexible, so
Cloudflare talks to origin over plain HTTP:80. Port 443 on origin is
only hit by misconfigured clients (who bypass CF DNS and hit node IPs
directly). Traefik's config accepts it but we don't require it. Keeping
it open smooths a future switch to Full (strict) SSL mode.
**Port :80 is closed** on all three nodes. CF is in Full (strict) mode
and initiates every request on :443 to the origin. Cloudflare's
"Always Use HTTPS" turns any plaintext client request into HTTPS at
the edge, so the origin never needs to accept :80.
**Future hardening**: Restrict 80 and 443 to Cloudflare's published IP
ranges (15 IPv4 CIDRs, 7 IPv6 CIDRs). See [Chapter 13](./13-cloudflare.md)
for the ranges and the UFW rule format. Today they're open to anyone.
**Port :443 is restricted to Cloudflare** via 22 UFW allow rules per
node (one per CF CIDR). Direct-connect from any non-CF IP is dropped
at the kernel. This closes the "node IP leak = bypass CF WAF/DDoS"
hole entirely. See [Chapter 13](./13-cloudflare.md#cloudflare-ip-ranges-used-in-traefik-trustedips)
for the exact ranges and UFW rule format.
**Refresh cadence**: CF updates its IP ranges rarely. A monthly
`curl https://www.cloudflare.com/ips-v4` diff and UFW re-apply is
enough. Automation TODO (Chapter 20).
### SSH (operator access)