neptun: serve Headplane at vpn.mgaction.town/admin, auth via Zitadel OIDC

Path-route Headplane under /admin on the same vhost as headscale instead of
its own subdomain - Caddy handle blocks split on the prefix, headscale gets
everything else. base_url drops to the site root since Headplane appends
/admin (and the OIDC callback path) itself.

Wire Zitadel as the OIDC provider. client_id/client_secret/the headscale
API key can't be real until Zitadel and headscale are actually deployed and
an application/key exist, so those are REPLACE_ME placeholders for now
(documented in services/headplane.nix) - direct API-key login stays enabled
as a fallback so this can't lock anyone out in the meantime.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-19 22:42:03 +02:00
co-authored by Claude Sonnet 5
parent 5507dfac0a
commit c4702b577c
4 changed files with 51 additions and 15 deletions
+15 -8
View File
@@ -60,16 +60,23 @@
'';
# ---- Headscale + Headplane (tailnet control server + its web UI) ----
# Both run locally on neptun (see services/{headscale,headplane}.nix).
# headscale's node-update endpoint is a long-poll, hence `flush_interval
# -1` — without it Caddy buffers the response and clients see stale state.
# Both run locally on neptun (see services/{headscale,headplane}.nix), path
# -routed on the same vhost: Headplane owns /admin* (its whole app,
# including static assets and the OIDC callback, lives under that prefix —
# `handle` keeps the prefix in the forwarded path, unlike `handle_path`,
# since Headplane needs to see it), everything else goes to headscale
# itself (tailnet client traffic). headscale's node-update endpoint is a
# long-poll, hence `flush_interval -1` — without it Caddy buffers the
# response and clients see stale state.
services.caddy.virtualHosts."vpn.mgaction.town".extraConfig = ''
reverse_proxy http://localhost:8082 {
flush_interval -1
handle /admin* {
reverse_proxy http://localhost:3000
}
handle {
reverse_proxy http://localhost:8082 {
flush_interval -1
}
}
'';
services.caddy.virtualHosts."headplane.mgaction.town".extraConfig = ''
reverse_proxy http://localhost:3000
'';
# ---- Gitea SSH forward ----