diff --git a/05_Resources/Frusetik Access Architecture.md b/05_Resources/Frusetik Access Architecture.md new file mode 100644 index 0000000..5915517 --- /dev/null +++ b/05_Resources/Frusetik Access Architecture.md @@ -0,0 +1,161 @@ +# Frusetik Access Architecture + +## Overview + +Access architecture for `frusetik.com`. Public entry point is Cloudflare. Two targets behind the same Cloudflare Tunnel setup on the VPS: **Pangolin / Traefik** for app subdomains, and **Authentik** directly for `auth.frusetik.com`. + +**Critical gotchas that broke the setup during setup:** +1. Pangolin internal TLS broke routing — must use HTTP internally +2. Immich iOS app breaks when Pangolin auth is enabled — must be "Not Protected" +3. `cloudflared` needed to be connected to `authentik_default` Docker network for `auth.frusetik.com` to work +4. Do NOT point Cloudflare web ingress at Gerbil for web app routing — Gerbil is part of Pangolin's tunnel system, not the normal HTTP routing target + +--- + +## Public hostnames + +| Hostname | Path | +|---|---| +| `pangolin.frusetik.com` | Cloudflare → Pangolin/Traefik on VPS | +| `immich.frusetik.com` | Cloudflare → Pangolin → home lab Immich | +| `glance.frusetik.com` | Cloudflare → Pangolin → home lab Glance | +| `baersolar.frusetik.com` | Cloudflare → Pangolin → home lab service | +| `auth.frusetik.com` | Cloudflare → Authentik directly on VPS (NOT Pangolin) | + +--- + +## Architecture diagram + +```mermaid +flowchart TD + U[User / Browser / Mobile App] --> CF[Cloudflare Edge / DNS / Tunnel Routing] + CF -->|auth.frusetik.com| CFL[cloudflared on VPS] + CF -->|*.frusetik.com app routes| CFL + subgraph VPS + CFL -->|http| AK[Authentik] + CFL -->|http| PT[Pangolin + Traefik] + PT --> PG[Pangolin control / resource routing] + end + PG -->|Pangolin tunnel / connector| NW[Newt in home lab] + subgraph Home Lab + NW --> IM[Immich] + NW --> GL[Glance] + NW --> BS[Baersolar] + end +``` + +--- + +## Key design decisions + +### 1. Cloudflare is the public entry point +Browser connects to Cloudflare over HTTPS. Cloudflare forwards through tunnel to VPS. Public TLS terminated at Cloudflare edge. + +### 2. Pangolin and Authentik reached internally by HTTP +Internal TLS in Pangolin broke routing. Cloudflare already handles public HTTPS. Internal hop is HTTP on the VPS. + +### 3. Authentik is NOT behind Pangolin +Authentik is routed directly to `auth.frusetik.com`. Pangolin is for app publishing only. + +### 4. Immich must not be protected by Pangolin auth +iOS app breaks if Pangolin authentication is in front of Immich. Set to **Not Protected** in Pangolin. + +--- + +## Adding a new resource + +### Steps + +1. **Make available on home network** — expose on some IP:PORT in the home lab +2. **Create public resource in Pangolin** — HTTP! If app access requires auth and supports OIDC, use Authentik as OpenID Provider; otherwise fall back to Pangolin's auth +3. **Create Cloudflare Tunnel route** — tunnel type HTTP, origin `http://gerbil:80` (or `https://gerbil:443` with NoTLSHandshake — untested) +4. **Wire up Authentik** — if app supports OIDC, add Authentik as OpenID Provider; else Pangolin auth handles it + +--- + +## Networking + +### Docker networks +- `authentik_default` — Authentik stack +- `pangolin` — Pangolin stack +- `edge` — **future**: shared network for cloudflared + public origins + +### Current network fix (temporary) +```bash +docker network connect authentik_default cloudflared +``` +Required for `cloudflared` to reach Authentik by Docker DNS name. + +### Future cleanup +Create a shared `edge` network and attach `cloudflared`, Authentik server, and Pangolin/Traefik to it permanently. + +--- + +## Authentik access model + +### Groups +- `family` — trusted family members (Immich, Nextcloud access) +- `movie-friends` — limited access (Jellyfin only, no Immich) + +### Access matrix +| Application | family | movie-friends | +|---|---|---:| +| Immich | yes | no | + +### Implementation +- One account per real person +- Application access controlled via Group bindings on the Application in Authentik +- If an application has no bindings, it may be accessible to all authenticated users — always bind explicitly + +### Note on Immich +Current live state: Immich in Pangolin = **Not Protected** (for iOS app compatibility). Authentik access policy is the **desired target model**, not the currently enforced gate on the mobile path. + +--- + +## Operational checklist + +### Pangolin resources stop working +1. Check whether TLS got re-enabled in Pangolin +2. Confirm resource still uses HTTP internally +3. Check resource hostname in Pangolin +4. Check whether auth was re-enabled for Immich +5. Confirm Pangolin/Traefik is still the Cloudflare tunnel target + +### `auth.frusetik.com` breaks +1. Check `cloudflared` is still connected to `authentik_default`: `docker inspect cloudflared` +2. Test connectivity: `docker run --rm --network authentik_default curlimages/curl:8.6.0 curl -i http://server:9000` +3. Check Cloudflare tunnel mapping for `auth.frusetik.com` + +### Immich mobile app breaks +1. Confirm `immich-prod` in Pangolin is still **Not Protected** +2. Confirm resource still uses HTTP inside Pangolin + +--- + +## Error Reference + +| Error | Cause | Fix | +|---|---|---| +| `404 page not found` on `immich.frusetik.com` | Pangolin internal TLS enabled | Disable TLS, enforce HTTP | +| `too_many_redirects` from `http://gerbil:80` | Wrong origin target | Use Pangolin/Traefik, not Gerbil directly | +| Cloudflare 502 on `auth.frusetik.com` | `cloudflared` can't resolve `server` hostname | `docker network connect authentik_default cloudflared` | +| Immich iOS app not working | Pangolin auth in front of Immich | Set Immich to **Not Protected** in Pangolin | + +--- + +## Useful commands + +```bash +# List Docker networks +docker network ls + +# Inspect cloudflared networks +docker inspect cloudflared + +# Inspect authentik network +docker network inspect authentik_default + +# Test auth.frusetik.com from authentik network +docker run --rm --network authentik_default curlimages/curl:8.6.0 \ + curl -i -H "Host: auth.frusetik.com" http://server:9000 +```