# Security Execution Plan (Dual Caddy: VPS Public + NAS Internal) ## Confirmed Architecture 1. `caddy-vps` is public ingress only. 2. `nas-caddy` is internal ingress only (LAN/Tailscale/internal service routing). 3. `server2` is for VLESS+REALITY and Tailscale exit-node use, not app ingress/CI. 4. CI/CD is Gitea push-triggered deploy on NAS. ## Public Host Responsibility Matrix 1. `auth.jimmygan.com` -> VPS Caddy -> NAS Authelia (`100.78.131.124:9092`). 2. `nas.jimmygan.com` -> VPS Caddy -> NAS dashboard (`100.78.131.124:4000`). 3. `music.jimmygan.com` -> VPS Caddy -> NAS navidrome (`100.78.131.124:4533`). 4. `photos.jimmygan.com` -> VPS Caddy -> NAS immich (`100.78.131.124:2283`). 5. `photos-app.jimmygan.com` -> VPS Caddy -> NAS immich (`100.78.131.124:2283`). All other service domains remain internal-only unless explicitly approved. ## Security Objectives 1. Keep your public access goals (`nas/music/photos/photos-app`) working. 2. Enforce centralized auth + policy at public edge (`caddy-vps` + Authelia). 3. Remove secret leakage risk and tighten trust boundaries. 4. Preserve internal NAS workflows and current service availability. ## Phase 0 - Pre-Change Safety 1. Snapshot current configs: - VPS: `/etc/caddy/Caddyfile` - NAS: `/volume1/docker/caddy/Caddyfile` - NAS: `/volume1/docker/authelia/config/configuration.yml` 2. Export backups for dashboard/gitea/immich/auth config + DB. 3. Define maintenance window (expect auth/session resets). ## Phase 1 - Secret Rotation and Hygiene 1. Rotate exposed secrets immediately: - Authelia `session.secret`, storage encryption key, reset JWT secret. - LDAP bind password. - SMTP app password. - Gitea DB password and runner registration token. - Telegram bot token and any API tokens. 2. Move secrets to non-git env/secret files. 3. Keep `.env` and local assistant memory files untracked. 4. Verify no plaintext secrets remain in tracked files. ## Phase 2 - VPS Caddy as Single Public Auth Edge 1. Update VPS Caddyfile to include all public hosts: - `auth`, `nas`, `music`, `photos`, `photos-app`. 2. Ensure `forward_auth` is applied on `nas/music/photos/photos-app`. 3. Keep `auth.jimmygan.com` without `forward_auth` (Authelia portal itself). 4. Keep TLS + security headers on VPS Caddy. 5. Reload Caddy and test all hostnames from public internet. ## Phase 3 - Authelia Policy Enforcement 1. Keep Authelia + LDAP as unified identity source. 2. Enforce mandatory 2FA for public apps: - `nas`, `music`, `photos`, `photos-app`. 3. Use group-based access rules where needed (family/media/admin groups). 4. Keep documented break-glass admin recovery procedure. ## Phase 4 - NAS Internal Plane Hardening 1. Keep NAS Caddy for internal-only domains/routes. 2. Remove overlap for public host policies from NAS Caddy (or clearly isolate). 3. Reduce direct host port exposure where possible. 4. Restrict NAS firewall/source ACL so public app ports are intended-path only. ## Phase 5 - Dashboard Hardening 1. Tighten trusted proxy logic in dashboard backend: - Only explicit proxy IPs/subnets. - No broad trust defaults. 2. Harden `/api/auth/proxy` against header spoofing. 3. Make SSH terminal fail-closed when `known_hosts` is missing. 4. Move dashboard auth from `localStorage` tokens to secure httpOnly cookies. 5. Add CSRF defenses for state-changing endpoints after cookie migration. ## Phase 6 - Jellyfin/LLDAP Persistence Fix 1. Persist network attachment in compose (current runtime `docker network connect` is not durable). 2. Avoid exposing LDAP bind ports publicly if not required. 3. Keep Jellyfin auth path consistent with Authelia/LDAP strategy. ## Phase 7 - Validation Checklist 1. Public access works for: - `https://nas.jimmygan.com` - `https://music.jimmygan.com` - `https://photos.jimmygan.com` - `https://photos-app.jimmygan.com` 2. All above require Authelia login and 2FA. 3. `https://auth.jimmygan.com` is reachable and stable. 4. No app access from internet via raw NAS ports. 5. Internal-only routes still work from LAN/Tailscale. 6. Secret scan returns no tracked plaintext credentials. ## Rollback Plan 1. Restore previous VPS Caddyfile and reload Caddy. 2. Restore previous NAS Caddyfile/Authelia config. 3. Revert to prior container image tags if app auth breaks. 4. Keep old+new secrets during transition window, then remove old. ## New Conversation Handoff Prompt Use this to resume execution in a new chat: "Execute `SECURITY_EXECUTION_PLAN.md` phase-by-phase. Start with Phase 0 and Phase 1 only, show diffs before apply, and validate after each phase. Preserve dual-Caddy architecture: VPS public ingress, NAS internal ingress."