lab.cloonar.com: replace Authelia forward-auth with a LAN/WireGuard restriction #285

Closed
opened 2026-08-07 21:46:09 +02:00 by dominik.polakovics · 0 comments

Part of #266. lab.cloonar.com is the last consumer of Authelia's forward-auth. Replace the auth_request gate with a source-address restriction plus a fixed Remote-User, so Authelia can be retired without introducing oauth2-proxy.

Why this instead of oauth2-proxy

ADR-0022's M5 planned oauth2-proxy for the auth_request vhosts. That plan is superseded: Zammad has been retired, typo3's /authelia block was always dead code (internal;, no auth_request consumer), and mcp-forgejo is being dropped. lab.cloonar.com is the only one left, and standing up a new component plus a seventh Keycloak client plus two secrets — one of them duplicated across two hosts' sops files — to gate a single vhost is not worth it.

coding-lab has no OIDC support: its only auth paths are its own web sessions and the Remote-User proxy header from trustedProxies. So it cannot become a Keycloak client directly either.

Scope — hosts/fw/vms/web/lab.nix only

Remove:

  • the location /authelia block in extraConfig
  • auth_request /authelia;
  • auth_request_set $target_url …; and error_page 401 =302 https://auth.cloonar.com/?rd=$target_url;
  • auth_request_set $lab_remote_user $upstream_http_remote_user;

Add, in locations."/":

allow 10.42.96.0/24;   # LAN
allow 10.42.98.0/24;   # WireGuard peers
deny all;

Use config.networkPrefix rather than hardcoding 10.42, matching the file's existing style.

Change: proxy_set_header Remote-User dominik.polakovics@cloonar.com; — a literal, replacing the value previously taken from Authelia's response.

That value is not arbitrary: it must equal coding-lab's admin username exactly, or resolveProxyHeader finds no user and silently falls through to the login form. It is also precisely what Authelia was already sending, since authelia.nix:74 sets username_attribute = "mail".

Preserve

  • The comment forbidding Host / X-Real-IP / X-Forwarded-Proto here. recommendedProxySettings already injects them, nginx accumulates proxy_set_header, and coding-lab's Go server rejects duplicate Host with a bare 400 before any handler runs. This has broken the vhost before.
  • The SSE settings for /api/v1/events — no buffering, no read timeout.
  • Everything else in the vhost.

Why the allow-list is correct, and what would break it

lab.cloonar.com resolves to fw's public address even from inside, so internal clients arrive via the hairpin DNAT at firewall.nix:187. The source address survives it — walking the postrouting chain, traffic to .97.5 matches no masquerade rule: it leaves via server (not wan/wg_cloonar), .97.5 is absent from the iifname { wan, wg_cloonar } ip daddr … list, and iifname "server" oifname "server" excludes clients arriving on lan or wg_cloonar. So nginx sees 10.42.96.x, 10.42.98.x, or the real public address.

Record both dependencies as comments, because each fails silently:

  1. Adding .97.5 to those masquerade destination lists would make every request appear to come from fw.
  2. web-02 configures no set_real_ip_from / real_ip_header, so $remote_addr is the true TCP peer and the allow-list cannot be spoofed via X-Forwarded-For. Enabling the real_ip module on this host later would turn the allow-list into an attacker-controlled header check.

Trust model change — state it in the commit message

The gate moves from "authenticated Authelia session in Administrators/Mitarbeiter" to "anywhere on the LAN or the VPN". Any device on 10.42.96.0/24 — guest laptops, phones, IoT — is now dominik.polakovics@cloonar.com in coding-lab, with no login. coding-lab can launch AFK agents that commit to the repositories, so this is a deliberate trade of authentication for network isolation, not an oversight.

ADR

Amend docs/adr/0022-keycloak-and-scoped-kerberos.md: M5 no longer introduces oauth2-proxy. Record why — Zammad retired, typo3 dead, mcp-forgejo dropped, coding-lab has no OIDC — and the trust-model change above. Note the consequence for the Keycloak-vs-authentik comparison: the oauth2-proxy workstream the ADR counted as Keycloak's price never had to be paid, so that argument reads differently in hindsight.

Out of scope

  • Removing Zammad, typo3's dead block, or mcp-forgejo — separate cleanup.
  • Retiring Authelia (M6) — it keeps running; this only removes its last forward-auth consumer.
  • authelia.nix, including the now-unused lab.cloonar.com access_control rule.

Verification

  • scripts/pre-commit dry-builds the affected hosts.

Human, after deploy:

  1. From a LAN client: https://lab.cloonar.com loads and you are already logged in — no login form. A login form means the Remote-User value doesn't match coding-lab's admin username.
  2. From nb over WireGuard: same.
  3. From a phone on mobile data (no VPN): 403.
  4. Check the access log confirms real client addresses (10.42.96.x / 10.42.98.x), not fw's address — this is the hairpin assumption above, and it is the one thing worth seeing rather than trusting.

Rollback is reverting the commit; Authelia is still running and its lab.cloonar.com rule is untouched.

Part of #266. `lab.cloonar.com` is the last consumer of Authelia's forward-auth. Replace the `auth_request` gate with a source-address restriction plus a fixed `Remote-User`, so Authelia can be retired without introducing oauth2-proxy. ## Why this instead of oauth2-proxy ADR-0022's M5 planned oauth2-proxy for the `auth_request` vhosts. That plan is superseded: Zammad has been retired, `typo3`'s `/authelia` block was always dead code (`internal;`, no `auth_request` consumer), and `mcp-forgejo` is being dropped. `lab.cloonar.com` is the only one left, and standing up a new component plus a seventh Keycloak client plus two secrets — one of them duplicated across two hosts' sops files — to gate a single vhost is not worth it. coding-lab has no OIDC support: its only auth paths are its own web sessions and the `Remote-User` proxy header from `trustedProxies`. So it cannot become a Keycloak client directly either. ## Scope — `hosts/fw/vms/web/lab.nix` only **Remove:** - the `location /authelia` block in `extraConfig` - `auth_request /authelia;` - `auth_request_set $target_url …;` and `error_page 401 =302 https://auth.cloonar.com/?rd=$target_url;` - `auth_request_set $lab_remote_user $upstream_http_remote_user;` **Add, in `locations."/"`:** ```nginx allow 10.42.96.0/24; # LAN allow 10.42.98.0/24; # WireGuard peers deny all; ``` Use `config.networkPrefix` rather than hardcoding `10.42`, matching the file's existing style. **Change:** `proxy_set_header Remote-User dominik.polakovics@cloonar.com;` — a literal, replacing the value previously taken from Authelia's response. That value is not arbitrary: it must equal coding-lab's admin username exactly, or `resolveProxyHeader` finds no user and silently falls through to the login form. It is also precisely what Authelia was already sending, since `authelia.nix:74` sets `username_attribute = "mail"`. ## Preserve - **The comment forbidding `Host` / `X-Real-IP` / `X-Forwarded-Proto` here.** `recommendedProxySettings` already injects them, nginx *accumulates* `proxy_set_header`, and coding-lab's Go server rejects duplicate `Host` with a bare `400` before any handler runs. This has broken the vhost before. - The SSE settings for `/api/v1/events` — no buffering, no read timeout. - Everything else in the vhost. ## Why the allow-list is correct, and what would break it `lab.cloonar.com` resolves to fw's public address even from inside, so internal clients arrive via the hairpin DNAT at `firewall.nix:187`. The source address survives it — walking the postrouting chain, traffic to `.97.5` matches no masquerade rule: it leaves via `server` (not `wan`/`wg_cloonar`), `.97.5` is absent from the `iifname { wan, wg_cloonar } ip daddr …` list, and `iifname "server" oifname "server"` excludes clients arriving on `lan` or `wg_cloonar`. So nginx sees `10.42.96.x`, `10.42.98.x`, or the real public address. **Record both dependencies as comments, because each fails silently:** 1. Adding `.97.5` to those masquerade destination lists would make every request appear to come from fw. 2. web-02 configures no `set_real_ip_from` / `real_ip_header`, so `$remote_addr` is the true TCP peer and the allow-list cannot be spoofed via `X-Forwarded-For`. Enabling the real_ip module on this host later would turn the allow-list into an attacker-controlled header check. ## Trust model change — state it in the commit message The gate moves from "authenticated Authelia session in `Administrators`/`Mitarbeiter`" to "anywhere on the LAN or the VPN". **Any device on `10.42.96.0/24` — guest laptops, phones, IoT — is now `dominik.polakovics@cloonar.com` in coding-lab, with no login.** coding-lab can launch AFK agents that commit to the repositories, so this is a deliberate trade of authentication for network isolation, not an oversight. ## ADR Amend `docs/adr/0022-keycloak-and-scoped-kerberos.md`: M5 no longer introduces oauth2-proxy. Record why — Zammad retired, typo3 dead, mcp-forgejo dropped, coding-lab has no OIDC — and the trust-model change above. Note the consequence for the Keycloak-vs-authentik comparison: the oauth2-proxy workstream the ADR counted as Keycloak's price never had to be paid, so that argument reads differently in hindsight. ## Out of scope - Removing Zammad, typo3's dead block, or mcp-forgejo — separate cleanup. - Retiring Authelia (M6) — it keeps running; this only removes its last forward-auth consumer. - `authelia.nix`, including the now-unused `lab.cloonar.com` `access_control` rule. ## Verification - `scripts/pre-commit` dry-builds the affected hosts. Human, after deploy: 1. From a LAN client: `https://lab.cloonar.com` loads **and you are already logged in** — no login form. A login form means the `Remote-User` value doesn't match coding-lab's admin username. 2. From `nb` over WireGuard: same. 3. From a phone on mobile data (no VPN): `403`. 4. Check the access log confirms real client addresses (`10.42.96.x` / `10.42.98.x`), not fw's address — this is the hairpin assumption above, and it is the one thing worth seeing rather than trusting. Rollback is reverting the commit; Authelia is still running and its `lab.cloonar.com` rule is untouched.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Cloonar/nixos#285
No description provided.