mirror of
https://github.com/juanfont/headscale.git
synced 2026-01-11 20:00:28 +01:00
[Feature] Support using groups from OIDC in ACLs #917
Open
opened 2025-12-29 02:25:56 +01:00 by adam
·
12 comments
No Branch/Tag Specified
main
update_flake_lock_action
gh-pages
kradalby/release-v0.27.2
dependabot/go_modules/golang.org/x/crypto-0.45.0
dependabot/go_modules/github.com/opencontainers/runc-1.3.3
copilot/investigate-headscale-issue-2788
copilot/investigate-visibility-issue-2788
copilot/investigate-issue-2833
copilot/debug-issue-2846
copilot/fix-issue-2847
dependabot/go_modules/github.com/go-viper/mapstructure/v2-2.4.0
dependabot/go_modules/github.com/docker/docker-28.3.3incompatible
kradalby/cli-experiement3
doc/0.26.1
doc/0.25.1
doc/0.25.0
doc/0.24.3
doc/0.24.2
doc/0.24.1
doc/0.24.0
kradalby/build-docker-on-pr
topic/docu-versioning
topic/docker-kos
juanfont/fix-crash-node-id
juanfont/better-disclaimer
update-contributors
topic/prettier
revert-1893-add-test-stage-to-docs
add-test-stage-to-docs
remove-node-check-interval
fix-empty-prefix
fix-ephemeral-reusable
bug_report-debuginfo
autogroups
logs-to-stderr
revert-1414-topic/fix_unix_socket
rename-machine-node
port-embedded-derp-tests-v2
port-derp-tests
duplicate-word-linter
update-tailscale-1.36
warn-against-apache
ko-fi-link
more-acl-tests
fix-typo-standalone
parallel-nolint
tparallel-fix
rerouting
ssh-changelog-docs
oidc-cleanup
web-auth-flow-tests
kradalby-gh-runner
fix-proto-lint
remove-funding-links
go-1.19
enable-1.30-in-tests
0.16.x
cosmetic-changes-integration
tmp-fix-integration-docker
fix-integration-docker
configurable-update-interval
show-nodes-online
hs2021
acl-syntax-fixes
ts2021-implementation
fix-spurious-updates
unstable-integration-tests
mandatory-stun
embedded-derp
prtemplate-fix
v0.28.0-beta.1
v0.27.2-rc.1
v0.27.1
v0.27.0
v0.27.0-beta.2
v0.27.0-beta.1
v0.26.1
v0.26.0
v0.26.0-beta.2
v0.26.0-beta.1
v0.25.1
v0.25.0
v0.25.0-beta.2
v0.24.3
v0.25.0-beta.1
v0.24.2
v0.24.1
v0.24.0
v0.24.0-beta.2
v0.24.0-beta.1
v0.23.0
v0.23.0-rc.1
v0.23.0-beta.5
v0.23.0-beta.4
v0.23.0-beta3
v0.23.0-beta2
v0.23.0-beta1
v0.23.0-alpha12
v0.23.0-alpha11
v0.23.0-alpha10
v0.23.0-alpha9
v0.23.0-alpha8
v0.23.0-alpha7
v0.23.0-alpha6
v0.23.0-alpha5
v0.23.0-alpha4
v0.23.0-alpha4-docker-ko-test9
v0.23.0-alpha4-docker-ko-test8
v0.23.0-alpha4-docker-ko-test7
v0.23.0-alpha4-docker-ko-test6
v0.23.0-alpha4-docker-ko-test5
v0.23.0-alpha-docker-release-test-debug2
v0.23.0-alpha-docker-release-test-debug
v0.23.0-alpha4-docker-ko-test4
v0.23.0-alpha4-docker-ko-test3
v0.23.0-alpha4-docker-ko-test2
v0.23.0-alpha4-docker-ko-test
v0.23.0-alpha3
v0.23.0-alpha2
v0.23.0-alpha1
v0.22.3
v0.22.2
v0.23.0-alpha-docker-release-test
v0.22.1
v0.22.0
v0.22.0-alpha3
v0.22.0-alpha2
v0.22.0-alpha1
v0.22.0-nfpmtest
v0.21.0
v0.20.0
v0.19.0
v0.19.0-beta2
v0.19.0-beta1
v0.18.0
v0.18.0-beta4
v0.18.0-beta3
v0.18.0-beta2
v0.18.0-beta1
v0.17.1
v0.17.0
v0.17.0-beta5
v0.17.0-beta4
v0.17.0-beta3
v0.17.0-beta2
v0.17.0-beta1
v0.17.0-alpha4
v0.17.0-alpha3
v0.17.0-alpha2
v0.17.0-alpha1
v0.16.4
v0.16.3
v0.16.2
v0.16.1
v0.16.0
v0.16.0-beta7
v0.16.0-beta6
v0.16.0-beta5
v0.16.0-beta4
v0.16.0-beta3
v0.16.0-beta2
v0.16.0-beta1
v0.15.0
v0.15.0-beta6
v0.15.0-beta5
v0.15.0-beta4
v0.15.0-beta3
v0.15.0-beta2
v0.15.0-beta1
v0.14.0
v0.14.0-beta2
v0.14.0-beta1
v0.13.0
v0.13.0-beta3
v0.13.0-beta2
v0.13.0-beta1
upstream/v0.12.4
v0.12.4
v0.12.3
v0.12.2
v0.12.2-beta1
v0.12.1
v0.12.0-beta2
v0.12.0-beta1
v0.11.0
v0.10.8
v0.10.7
v0.10.6
v0.10.5
v0.10.4
v0.10.3
v0.10.2
v0.10.1
v0.10.0
v0.9.3
v0.9.2
v0.9.1
v0.9.0
v0.8.1
v0.8.0
v0.7.1
v0.7.0
v0.6.1
v0.6.0
v0.5.2
v0.5.1
v0.5.0
v0.4.0
v0.3.6
v0.3.5
v0.3.4
v0.3.3
v0.3.2
v0.3.1
v0.3.0
v0.2.2
v0.2.1
v0.2.0
v0.1.1
v0.1.0
Labels
Clear labels
CLI
DERP
DNS
Nix
OIDC
SSH
bug
database
documentation
duplicate
enhancement
faq
good first issue
grants
help wanted
might-come
needs design doc
needs investigation
no-stale-bot
out of scope
performance
policy 📝
pull-request
question
regression
routes
stale
tags
tailscale-feature-gap
well described ❤️
wontfix
Mirrored from GitHub Pull Request
Milestone
No items
No Milestone
Projects
Clear projects
No project
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: starred/headscale#917
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
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?
Originally created by @joachimtingvold on GitHub (Jan 22, 2025).
Use case
Fetch/use groups received via OIDC property/scope in ACLs. This way we don't have to manually maintain the group memberships in the ACL.
Description
This is a continuation from the discussion in #1121.
The feature does not need to implement full support for all OIDC providers (which would complicate things). This feature can simply be done by having a setting in headscale where you can specify the name of the OIDC property that contains the array of groups the user is a member of. If that is a non-empty list of groups, headscale can use that list of groups to evaluate what permissions the user will have. If a group received from OIDC is not present in headscale ACL config, it can simply be ignored.
Since headscale already have a field to define the OIDC scope in the configuration, no further changes on that aspect is needed. Simply adding an option like
group_property: groupsor similar in theoidcsection ofconfig.yamlwould be sufficient from the configuration perspective.Example config and JSON blobs using Authentik added below. Given these examples, the expectation would be that my user will have access to the host 10.1.1.1 (server1), even if there is no statically configured group "server1-admins" in
acls.hujson.OIDC Discover response:
OIDC userinfo response:
OIDC JWT payload:
oidc-section of config.yaml:
Relevant parts of acls.hujson:
@Nathanael-Mtd commented on GitHub (Jan 23, 2025):
Useful feature for many of us !
About the implementation idea, it's more a user-group import and store to ACL file which be needed, when OIDC (re-)login to headscale is done (first time login or on expiration).
Because that's only during this step OIDC user info is handled by Headscale, not at every Tailscale client startup.
Like that we can dynamically fill the ACL policy with user mappings to groups, and be handle manual user groups changes when expiration is too far.
But maybe it can have an impact on Headscale if there are many ACL reloads.
Overall there are some limitations with OpenID provisioning because we can't handle automatic groups changes and account lock/deletion before node expiration.
@joachimtingvold commented on GitHub (Jan 23, 2025):
Fair enough. Would we need to really write/update
acls.hujsonfor every login? How does Headscale handle node expirations during a reload/restart of the headscale service itself? Does node expire due to that (i.e. trigger a relogin), or would it actually maintain already cached nodes? If they expire, the group(s) could then technically also be held in memory? (rather than writing/alteringacls.hujsondirectly). If nodes are not expired due to a service restart, then you would of course need to do so.There might be a potential scaling issue in play here, depending on how many users there are on a given headscale instance. I would wager that this would not be the case for the majority of the headscale instances out there (in which case it would probably make more sense for them to have scripts that would populate
acls.hujsongroups at regular intervals that fetches group membership from whatever IdP they're using).That would also be fine for most usecases for this, I would say.
@kradalby commented on GitHub (Jan 23, 2025):
It sounds like @Nathanael-Mtd is essentially describing SCIM v2.
I think that would be an unrealistic undertaking, and out of scope.
A reasonable compromise sounds like we are able to sync in groups, and they can be used. The admin can then decide the "tolerance" they how for groups being out of sync by setting a short or long expiry time.
@IamTaoChen commented on GitHub (Jan 24, 2025):
I don’t think it’s a good idea to sync groups directly from OIDC. Headscale cannot determine when a user is removed from a group. For example:
Additionally, we cannot disable users from OIDC during this period.
Perhaps we could create an API to manage groups, storing group information in a separate table. The ACL module can maintain its own groups, but we would need an additional step to generate the real ACL (by combining the group information from the database with the ACL file).
When updating the real ACL to the client, we could define a policy, such as on update, every 24 hours, etc.
With this functionality, users could create their own scripts to sync users and groups from their backend systems. For example, I would use LDAP to align users’ groups. we only need to give some examples, and we don't need to maintain the oidc groups
@Nathanael-Mtd commented on GitHub (Jan 24, 2025):
About how Headscale works now, it require adding users on groups by hand. For a little improvment, before integrating API or SCIMv2 (maybe one day or never), we can start by only importing users into ACL groups if the mapping with OIDC groups recieved match.
It doesn't change the need for administrators to handle group changes and removals by hand, but we can accelerate user onboarding in a simple way.
@joachimtingvold commented on GitHub (Jan 24, 2025):
It's the onboarding that is the most "cumbersome" today for the installations that I manage. Offboarding is a completely different beast.
It should probably ignore groups received from OIDC not added in
acls.hujson. If you getgroup1,group2,group3from OIDC, but only havegroup1,group3specified inacls.hujson, the user should be added to group1 and group3.Would another option be to store the "array of groups" received from OIDC with the "node/machine" when logging in/refreshing the node? (rather than having to write to
acls.hujsonevery time a user logs in). If you need to offboard a user/group, you could just invalidate the node/machine (which you would want to anyways, probably).@Nathanael-Mtd commented on GitHub (Jan 24, 2025):
Yeah I find that better ignoring groups recieved from OIDC and not defined in headscale ACLs, because it can be massive.
Another idea is a mapping file (or section in headscale config) to associate groups from OIDC to the ones in ACLs, to handle group names differences from OIDC.
@github-actions[bot] commented on GitHub (Apr 25, 2025):
This issue is stale because it has been open for 90 days with no activity.
@lambdaupb commented on GitHub (Jun 8, 2025):
Could this not be implemented by using the offline-access token in OIDC and polling the UserInfo endpoint periodically?
https://openid.net/specs/openid-connect-core-1_0.html#OfflineAccess
https://www.keycloak.org/docs/latest/server_admin/index.html#_offline-access
Seems to be this is exactly for this use-case.
@fschrempf commented on GitHub (Jul 31, 2025):
Sounds interesting, but I guess it wouldn't really scale for instances with a large number of users. You would need to periodically query the endpoint for each user.
Probably that's one of the reasons why some services (e.g. OpenCloud) allow to combine OIDC for SSO with LDAP for user information and group memberships (with the IdP using the same LDAP as user backend).
@lambdaupb commented on GitHub (Aug 1, 2025):
What would be different with LDAP? You would also need to periodically query the LDAP Server for updates as well.
@Nathanael-Mtd commented on GitHub (Aug 1, 2025):
When you query LDAP server to get users info, you don't send one request per user, but one for whole users.
Also, offline tokens and whole OIDC sessions can be revoked, how will you handle that, except by forcing tailscale users to re-login to headscale to refresh them ?
That's not something very reliable, better try to integrate SCIM instead.