access: tailnet ACL, Headscale login and admin tools behind Authelia (tracking PRs #246-#250) #251
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Tracking issue for the tailnet and admin-tool access work of 2026-10-16/17. Prompted by: how do I add another user's PC, and which tools should be behind Authelia?
Findings
one_factor), and eight ACL rules usedsrc: "*", so any enrolled device reached SSH on both hosts, the LLDAP admin page, Alertmanager (no login), Forgejo git-SSH and web port, and the CI cache. The onboarding runbook claimedweb:443only./adminand Grafana were already behind Authelia (verified live); Traefik's dashboard is unreachable; Loki is disabled and bound to 127.0.0.1 (no UI, stays unrouted).manage healthshowed Prometheus as stopped while it ran (Compose container names vs an exact-name match).headscale policy check(syntax only; it accepts a nonexistent user) is the only automated check.Pull requests (merge and deploy in this order)
group:admin(headscale_admin_users) + tagged infrastructure for the admin ports; everyone elseweb:443only; Alertmanager rule removed--tags headscale --limit servicesheadscale: onlyadminandclient-<site>-admin/-employeecan sign in--tags authelia --limit servicesalertmanager.<domain>, admin only), no published port--tags traefik,authelia,alertmanager --limit servicesmanage health: Compose names recognised; Prometheus and Alertmanager monitored--tags manage_climanage user roles/access/grant/revoke--tags manage_cliVerify after the deploys
nc -z 100.64.0.3 17170,3000,2222,8089open. If not: the other form of the user name inheadscale_admin_users(user name vs the OIDC email) is the fix; public SSH still works; roll back by redeploying the previous commit.python3 scripts/checks/check-exposure.py: all login checks OK (Alertmanager fails until #250 is deployed, which is the gap).manage user access jfraeys: Tailnet reach should say admin devices (before #246 it correctly says every device reaches the admin ports).Follow-ups (not done)
tag:webwith the public web host, so a CI job's node can reach the admin ports through the tag rule. Give runners their own tag (tag:ci: add totagOwners, mint the runner key with it, set the ephemeral play's advertised tag) with onlyservices:3000,:2222,:8089; then narrowtag:webtoservices:25.tag:operatorfor the specific admin machines (a lost phone is currently an admin device).headscale policy checkcannot prove).headscale nodes delete, notmanage vpn revoke(that only expires). Do it before the OVH name swap.allowed_*filters if Authelia policy is ever not enough.alertmanager_webhook_urldefaults tohttp://localhost:9093/webhookin the hardening scripts, which was never reachable (the port was bound to the tailnet address); either point them at the container or remove.Status 2026-10-17: ALL FIVE PRs ARE MERGED, PROMOTED AND DEPLOYED, and the checks from this issue pass.
Done: #246 (ACL: admin group + tagged infrastructure, everyone else web:443; Alertmanager rule removed), #247 (Authelia policy 'headscale'), #250 (Alertmanager behind Traefik + Authelia), #249 (manage health: Compose names, Prometheus and Alertmanager monitored), #248 (manage user roles/access/grant/revoke). Promotion #252 approved; prod == main.
Verified on production: Headscale loaded policy contains group:admin and no 9093 rule; Authelia has the headscale policy; Traefik has the alertmanager router and the container publishes no port; manage user_access.sh and the new health.sh are installed; scripts/checks/check-exposure.py reports all three login checks OK (prometheus, alertmanager, vault /admin -> Authelia 401 + login Location) and 'exposure matches expectations'; from the operator's Mac over the tailnet ports 22, 17170, 3000, 2222, 8089 are open and 9093 is closed; 'manage user access jfraeys' says role admin, Headscale login yes, tailnet reach admin devices; 'manage user roles' lists admin and the coaching client-admin / client groups (no members yet).
Still open (follow-ups in this issue's list): CI runners share tag:web with the public web host (plan: tag:ci); tag:operator for specific admin machines; tailnet reach smoke script; delete 6 stale Headscale nodes (21, 47, 48, 49, 63, 64) before the OVH name swap; the unreachable localhost:9093 webhook default in the hardening scripts. Small fix pending: the 'Tailnet reach' line in manage user access still lists Alertmanager among the admin-device ports. Not yet confirmed which form of the admin user (name or OIDC email) Headscale matches; both are listed, drop the unused one later.
Update: Headscale stale nodes 21, 47, 48, 49, 63, 64 were REVOKED (expired 2026-10-17 01:33 UTC, so they cannot connect) but not DELETED; their entries and names remain, on the live Headscale and on the OVH copy. Deleting is still needed before the OVH name swap: headscale nodes delete --identifier (manage vpn revoke only expires). Also done: firewall tag on ovh-services verified (#233 closed).
Status 2026-10-17: the follow-up 'runners share tag:web' is in PR #274 (tag:ci: Forgejo :3000/:2222 and the cache :8089 only; the CI authkey is minted with tag:ci). Checked that coaching-site's refresh_demo SSH target is a public address, so it is unaffected. Rollout is two deploys in order (headscale ACL first, then the provisioner key). Still open here: narrowing tag:web (needs web's real needs verified) and tag:operator.