3fd045aacb
Deploy Dashboard / deploy (push) Successful in 1m28s
- Narrow trusted proxies, remove overly broad 10.0.0.0/8 - Fail closed on missing SSH known_hosts in terminal proxy - Add Navidrome reverse proxy headers for Authelia SSO - Add Gitea docker-compose definition - Add Caddy configs for NAS and VPS edge proxies - Add dnsmasq split-horizon DNS config - Add security execution plan document
114 lines
4.6 KiB
Markdown
114 lines
4.6 KiB
Markdown
# 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."
|
|
|