From 04cd6b9f24fec36d005e66470104765aaec2d253 Mon Sep 17 00:00:00 2001 From: serversdown Date: Fri, 12 Jun 2026 02:39:33 +0000 Subject: [PATCH] docs(portal): security hardening backlog for the dedicated pass Consolidates the deferred items (reverse proxy exposing only /portal/*, TLS, SECRET_KEY, PORTAL_OPEN_LINKS off, M4 auth incl. the operator app + currently- unauthenticated operator endpoints, and the smaller code-review items) into an actionable checklist so the hardening session starts from a list, not a recall. Co-Authored-By: Claude Opus 4.8 --- docs/CLIENT_PORTAL.md | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/docs/CLIENT_PORTAL.md b/docs/CLIENT_PORTAL.md index c489567..b654d72 100644 --- a/docs/CLIENT_PORTAL.md +++ b/docs/CLIENT_PORTAL.md @@ -175,3 +175,34 @@ live data, all from cache. - `SECRET_KEY` must be a real secret in prod (env, not committed). - Cookies: `HttpOnly`, `SameSite=Lax`, `Secure` once behind TLS. - Tokens stored hashed; raw shown once. Revocation is immediate. + +## Security hardening backlog ("Fest 2026") + +The to-do for the dedicated hardening pass, roughly highest-impact first. Until +then the portal runs on security-by-obscurity (open port + interim links) — fine +for a not-in-use demo, not for real clients. + +**Exposure (the big one):** port 8001 serves the *entire operator app* (roster, +projects, `/admin/*`, device config, the SLMM proxy) with **zero auth**, so an +open port exposes far more than the read-only portal. +- [ ] Reverse proxy (NPM/Caddy/Nginx) in front, exposing **only `/portal/*`** to + the internet; keep the operator app reachable on the LAN only. +- [ ] TLS everywhere (Let's Encrypt). Then set portal cookies `Secure`. +- [ ] Don't port-forward the raw app; if a quick gate is wanted before M4, an + auth proxy (Authelia / Authentik) can front the portal without writing auth. + +**Config musts:** +- [ ] Set a real `SECRET_KEY` env (signs session cookies; default is public). +- [ ] `PORTAL_OPEN_LINKS=false` in any internet-facing env (it defaults off now). + +**M4 — real auth** (replaces the interim token behind `get_current_client`): +- [ ] Magic-link email and/or accounts; proper sessions + password reset. +- [ ] Authenticate the **operator** app too (it currently has none). +- [ ] Gate the operator-only endpoints that are presently unauthenticated: + `/projects/{id}/portal-preview`, `/projects/{id}/portal-link*`, + `/portal/open/*`. + +**Smaller items from the pre-merge code review:** +- [ ] Keepalive isn't auto-turned-off when the last alert rule on a unit is + deleted (intentional "never auto-off"; revisit if it wastes cellular). +- [ ] Consider rate-limiting the scoped portal endpoints once public.