fix(authelia): restrict the Headscale login to admin and client groups #247

Merged
jfraeys merged 1 commit from jfraeys/fix/headscale-oidc-groups into main 2026-10-16 19:11:00 -04:00
Owner

Second half of the Headscale access change (the ACL is #246). The headscale OIDC client used the default one_factor policy, so any LLDAP user could sign in and enroll a device. New named policy 'headscale' (default deny) allowing group:admin, authelia_headscale_extra_groups (default empty), and client--admin / client--employee for every client_sites entry with auth: authelia, the same groups the admin-panel access rules already use, so the coaching multi-instance work needs no extra step.

Validated locally with the real Authelia image (4.39.28): the template rendered through Ansible with dummy secrets passes 'authelia validate-config' (only the existing deprecation warnings), the validator lists 'headscale' among the valid policy names, and it rejects a client naming an undefined one. The baseline template renders and validates the same way. Operator check: LLDAP shows user jfraeys in group admin; no client users exist yet.

Effect when deployed: only NEW sign-ins are checked; existing devices keep working until their node key expires. A user outside these groups gets Authelia's access-denied page at Headscale login.

Risk: Authelia serves SSO for Forgejo, Vaultwarden, Grafana and Headscale. A bad config would stop it, so this goes through staging (the Authelia role runs there and must come up healthy) before promotion. Deploy: ./infra deploy --tags authelia --limit services; then check that sso.jfraeys.com / auth login still works and sign-in to Forgejo succeeds.

Order with #246: independent. Do #246 first (ACL), this second.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Ge5tovcwdEUBtPwUktsZZZ

Second half of the Headscale access change (the ACL is #246). The headscale OIDC client used the default one_factor policy, so any LLDAP user could sign in and enroll a device. New named policy 'headscale' (default deny) allowing group:admin, authelia_headscale_extra_groups (default empty), and client-<slug>-admin / client-<slug>-employee for every client_sites entry with auth: authelia, the same groups the admin-panel access rules already use, so the coaching multi-instance work needs no extra step. Validated locally with the real Authelia image (4.39.28): the template rendered through Ansible with dummy secrets passes 'authelia validate-config' (only the existing deprecation warnings), the validator lists 'headscale' among the valid policy names, and it rejects a client naming an undefined one. The baseline template renders and validates the same way. Operator check: LLDAP shows user jfraeys in group admin; no client users exist yet. Effect when deployed: only NEW sign-ins are checked; existing devices keep working until their node key expires. A user outside these groups gets Authelia's access-denied page at Headscale login. Risk: Authelia serves SSO for Forgejo, Vaultwarden, Grafana and Headscale. A bad config would stop it, so this goes through staging (the Authelia role runs there and must come up healthy) before promotion. Deploy: ./infra deploy --tags authelia --limit services; then check that sso.jfraeys.com / auth login still works and sign-in to Forgejo succeeds. Order with #246: independent. Do #246 first (ACL), this second. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01Ge5tovcwdEUBtPwUktsZZZ
fix(authelia): only admins and client users can sign in to Headscale (and so enroll a tailnet device)
All checks were successful
Lint / lint (pull_request) Successful in 2m29s
Secret scan / scan (pull_request) Successful in 16s
cb5d288f01
The headscale OIDC client used the default one_factor policy, which lets any LLDAP user in. New authorization policy 'headscale': default deny;
allow group admin, authelia_headscale_extra_groups, and client-<slug>-admin / client-<slug>-employee for every authelia-gated client_sites entry
(the same groups the admin-panel rules already use). Existing devices keep working; only new logins are checked.
Validated with the real image: authelia/authelia:4.39.28 'validate-config' accepts the rendered config (only the existing deprecation warnings),
lists 'headscale' among the valid policy names, and rejects a client naming an undefined policy.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ge5tovcwdEUBtPwUktsZZZ
Sign in to join this conversation.
No reviewers
No labels
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
jfraeys/infra!247
No description provided.