Skip to content

Authentik admin setup — infra-services outpost (ADR-002)

Owner checklist for configuring Authentik before enabling forward-auth on *.infra.realemail.app. Repo-side Traefik/outpost wiring lives in authentik-cross-host-sso.md and services/authentik-outpost/README.md.

Time: ~20–30 minutes first time Admin URL: https://auth.realemail.app/if/admin/ Identity server: saltierpoop (Saltbox) — stays where it is New outpost host: infra-services (192.168.6.17) — homelab-managed, not Saltbox


What you are building

Browser → infra Traefik (*.infra.realemail.app)
              ├─ forwardAuth → authentik-outpost container (infra-services :9000)
              │                      │
              │                      └─ API/websocket → auth.realemail.app (saltierpoop)
              └─ after login → upstream app (Grafana, Komodo, …)

You will create three things in Authentik admin:

  1. Proxy provider (domain-level forward auth for the infra zone)
  2. Application bound to that provider
  3. Manual proxy outpost whose token the infra container uses to register

Do not attach infra apps to Saltbox’s embedded outpost — that outpost runs on saltierpoop and serves *.realemail.app. Infra needs its own outpost on infra-services.


Pre-flight

  • [ ] You can log in to Authentik admin at https://auth.realemail.app/if/admin/
  • [ ] Homelab PR with services/authentik-outpost/ and Traefik middleware is merged or ready on the branch you will deploy
  • [ ] You have WSL (or another environment) with sops and the operator age key for encrypting the outpost token
  • [ ] You understand deploy order: Authentik admin → SOPS token → start outpost → redeploy Traefik/app stacks (turning on middleware before the outpost is running breaks every infra UI)

Hostnames in scope (initial rollout)

These infra Traefik routers will use authentik@file once deployed:

Hostname Service
https://traefik.infra.realemail.app Traefik dashboard
https://grafana.infra.realemail.app Grafana
https://prometheus.infra.realemail.app Prometheus
https://alertmanager.infra.realemail.app Alertmanager
https://komodo.infra.realemail.app Komodo (native OIDC — not forward-auth)
https://homepage.infra.realemail.app Homepage
https://ara.infra.realemail.app ARA
https://adguard.infra.realemail.app AdGuard Home
https://wazuh.infra.realemail.app Wazuh dashboard

Explicitly out of scope: https://plex.realemail.app (ADR-002 exception — no Authentik at the reverse proxy).

Approach When to use
Forward auth (domain level) — recommended One provider covers all *.infra.realemail.app; least admin overhead
Forward auth (single application) Different users/policies per app (e.g. only ops sees Prometheus)

This runbook uses domain level. See Alternative: per-app providers at the end if you need stricter per-app authorization.


Step 1 — Create application + domain-level proxy provider

Authentik’s combined wizard is the easiest path.

  1. Open Applications → Applications
  2. Click Create with provider (or New application → wizard with provider)
  3. Application step
  4. Name: Infra homelab (domain) (any clear label)
  5. Slug: infra-homelab-domain (auto-filled is fine)
  6. Launch URL: https://homepage.infra.realemail.app (convenience link only)
  7. Click Next
  8. Choose provider type
  9. Select Proxy Provider
  10. Click Next
  11. Configure Proxy Provider
  12. Name: infra-realemail-app-domain
  13. Authorization flow: default-provider-authorization-implicit-consent (or your standard “login + consent” flow — match what Saltbox apps use if unsure)
  14. Authentication flow: leave default / same as other Saltbox forward-auth apps
  15. Mode: Forward auth (domain level)
  16. Authentication URL: https://homepage.infra.realemail.app (must be a hostname on the infra zone served by infra Traefik + infra outpost — see Step 2b. Do not use https://auth.realemail.app here; that splits OAuth session from callback outpost.)
  17. Cookie domain: infra.realemail.app Parent domain shared by all protected infra hostnames (no leading *. — Authentik expects the registrable parent, e.g. infra.realemail.app not grafana.infra.realemail.app).
  18. External host: leave empty for domain-level mode (not used the same way as single-app mode)
  19. Internal host: leave empty (Traefik forwards to real apps; outpost only checks auth)
  20. Unauthenticated paths / URLs: leave empty unless you later need scrape/webhook exceptions (Prometheus scrapes are server-side — they do not hit Traefik forward-auth in the browser path)
  21. Click Next
  22. ReviewSubmit

You should land on the new Application page with the provider attached.

Sanity-check the provider

  1. Applications → Providers → open infra-realemail-app-domain
  2. Confirm Mode = Forward auth (domain level)
  3. Open the Authentication tab — note the Client ID (useful for log correlation later)

Official reference:


Step 2 — Create a manual proxy outpost (infra-services)

Saltbox already manages Docker on saltierpoop. The infra outpost is homelab compose on another host, so use manual integration — Authentik must not try to spawn the container on saltierpoop.

  1. Applications → Outposts
  2. Click Create
  3. Set fields:
Field Value
Name infra-services
Type Proxy
Integration ---- / None / manual (wording varies by version — pick the option that does not deploy via Docker/K8s)
Applications Select Infra homelab (domain) (the app from Step 1)
Configuration → authentik_host https://auth.realemail.app (usually inherited; verify in Advanced settings if present)
Configuration → authentik_host_insecure false
  1. Click Create

Copy the outpost token

Immediately after creation:

  1. Open the new infra-services outpost
  2. Find Token / View deployment info / Outpost service connection (label varies by Authentik version)
  3. Copy the token — it is shown once. Store it in your password manager until SOPS is done.

If you lose it: delete and recreate the outpost, or rotate the service-account token in Authentik’s service account settings (more steps — recreating the outpost is usually faster for a greenfield setup).

Do not use Saltbox Docker integration

If you pick Docker integration on the saltierpoop-hosted Authentik, it will attempt to manage containers on saltierpoop’s Docker socket. The infra outpost must run on infra-services via services/authentik-outpost/compose.yml.

Step 2b — Authentication URL must be on the infra zone (required)

When Authentication URL is https://auth.realemail.app, OAuth callbacks land on saltierpoop:

https://auth.realemail.app/outpost.goauthentik.io/callback?X-authentik-auth-callback=true&code=...

That hits the Embedded Outpost, but the OAuth session was started by the infra-services outpost when you first opened komodo.infra.realemail.app. Outposts do not share session state. Saltierpoop logs show the failure clearly:

mismatched session ID … should=""
invalid state
status: 400

Do not assign the infra app to the Embedded Outpost as a workaround — that breaks Saltbox *.realemail.app forward-auth (404 storms on sonarr, tdarr, etc.) and still will not share sessions with the remote outpost.

Fix: edit the proxy provider (infra-realemail-app-domain):

Field Wrong Correct
Authentication URL https://auth.realemail.app https://homepage.infra.realemail.app (any stable infra hostname)
Cookie domain infra.realemail.app unchanged

After save, OAuth callback becomes:

https://homepage.infra.realemail.app/outpost.goauthentik.io/callback?...

Infra Traefik routes that to the same infra-services outpost that started the flow. Users still log in at auth.realemail.app for credentials; only the callback host moves to the infra zone.

Embedded Outpost: leave it with Saltbox apps only — remove Infra homelab (domain) if you added it during troubleshooting.

Wait ~30 seconds after provider save, then test incognito → https://komodo.infra.realemail.app.


Step 3 — Optional access control (policies)

Domain-level forward auth applies one authorization policy to all infra hostnames. You cannot assign different Authentik apps/policies per hostname in this mode.

For a homelab where every infra UI is ops-only, bind the application to a group:

  1. Applications → ApplicationsInfra homelab (domain)
  2. Policy / Group / User BindingsBind existing policy or Create binding
  3. Example: require membership in group homelab-admins (create the group under Directory → Groups if needed)
  4. Policy engine mode: any or all per your usual pattern

Skip this step if “any logged-in Authentik user may access infra UIs” is acceptable for now.


Step 4 — Save the token (SOPS)

The outpost container reads AUTHENTIK_TOKEN from a decrypted .env on infra-services.

On your dev machine (WSL):

cd ~/path/to/homelab   # or /mnt/c/Users/.../homelab
cp services/authentik-outpost/.env.sops.yaml.example \
   services/authentik-outpost/.env.sops.yaml

Edit .env.sops.yaml — replace the placeholder:

AUTHENTIK_TOKEN: "<paste token from Step 2>"

Encrypt and commit:

sops -e -i services/authentik-outpost/.env.sops.yaml
git add services/authentik-outpost/.env.sops.yaml
git commit -m "Add Authentik infra outpost token"
git push

Details: secrets.md § Decrypting SOPS YAML to dotenv

Never commit plaintext tokens

Only the encrypted .env.sops.yaml belongs in git. .env on the host is gitignored.


Step 5 — Deploy on infra-services (after git pull)

SSH to infra-services and start the outpost before rolling Traefik middleware to apps.

ssh infra-services-cursor   # or someone@192.168.6.17
cd /opt/homelab/services/authentik-outpost
git pull   # if not already current
SOPS_AGE_KEY_FILE=/etc/homelab/age-key.txt sops -d .env.sops.yaml \
  | sed 's/: /=/' > .env
docker compose up -d
docker logs authentik-outpost --tail 30

Healthy logs mention successful connection to Authentik and provider config loaded. Errors about invalid token → re-check Step 2 token and SOPS file.

Then redeploy Traefik and stacks (Komodo deploy-infra or manual docker compose up -d per stack).


Step 6 — Verify in Authentik admin

  1. Applications → Outposts → infra-services
  2. Status should show healthy / recent websocket ping (may take 1–2 minutes after container start)
  3. If unhealthy:
  4. docker logs authentik-outpost on infra-services
  5. Search saltierpoop Authentik server logs for the provider Client ID from Step 1
  6. Confirm infra-services can reach https://auth.realemail.app (no firewall block from VLAN 6 → saltierpoop)

Step 7 — Verify in the browser

Use a private/incognito window for each check.

# URL Expected
1 https://grafana.infra.realemail.app Redirect to Authentik login → after login, Grafana loads
2 https://komodo.infra.realemail.app Same SSO session (cookie on infra.realemail.app) — should not ask to “authorize app” again
3 https://homepage.infra.realemail.app Loads after auth
4 https://plex.realemail.app No Authentik redirect — Plex loads as before

Logout (domain-level): https://auth.realemail.app/outpost.goauthentik.io/sign_out (invalidates sessions for providers using that authentication URL — see Authentik proxy docs)


Troubleshooting

Symptom Check
All infra URLs → 502 Bad Gateway docker ps on infra-services — is authentik-outpost running and on network traefik?
401, no redirect to login Provider not assigned to infra-services outpost; or middleware deployed before outpost
Redirect loop Cookie domain must be infra.realemail.app; clock skew between hosts; clear cookies for *.infra.realemail.app
Login works for one hostname only You used single-app mode by mistake, or multiple conflicting providers
400 on auth.realemail.app/outpost.goauthentik.io/callback?... after login Authentication URL on auth.realemail.app — session on infra outpost, callback on embedded. Fix: Step 2b; remove infra app from Embedded Outpost
Saltbox *.realemail.app apps broken (404 on forward-auth) Infra app was added to Embedded Outpost — remove it; restart Saltbox authentik/traefik if needed
Outpost unhealthy in admin Wrong token; AUTHENTIK_HOST must be https://auth.realemail.app; TLS interception
SSO works on saltierpoop but not infra Expected separate cookie zones unless you deliberately unify — see ADR-002
Grafana loads but “login” still shown inside app Configure Grafana auth proxy (repo: services/monitoring/compose.yml)
Komodo asks for username/password after Authentik Komodo uses native OIDC, not forward-auth — see komodo-authentik-oidc.md

Outpost metrics (optional): port 9300/metrics inside the container — not published to the host by default.


Adding a new *.infra.realemail.app service later

Domain-level provider (this runbook):

  1. Add Traefik labels including traefik.http.routers.<name>.middlewares=authentik@file
  2. Deploy the stack — no Authentik admin change required
  3. Verify in incognito

If you switched to per-app providers: repeat Step 1 for each new hostname and add the app to the infra-services outpost.

Checklist also in adding-a-service.md.


Alternative: per-app providers

Use when Prometheus should be restricted to a subset of users while Grafana is wider, etc.

For each hostname:

  1. Applications → Create with provider
  2. Mode: Forward auth (single application)
  3. External host: https://<app>.infra.realemail.app (exact URL)
  4. Create matching Application
  5. Outposts → infra-services → Applications: add each new application
  6. Traefik callback router in services/traefik/config/dynamic/authentik.yml already covers all *.infra.realemail.app paths under /outpost.goauthentik.io/

More admin work; finer-grained policies per app.


Token rotation

  1. Applications → Outposts → infra-services — regenerate token (or recreate outpost)
  2. Update services/authentik-outpost/.env.sops.yaml, re-encrypt, commit, push
  3. On infra-services: decrypt .env again, docker compose up -d --force-recreate
  4. Confirm outpost healthy in admin