v0.24.0
juanfont/headscalev0.24.0Jan 17, 2025by github-actions[bot]
AI Summary
Major security and OIDC overhaul switching user identification to `iss` and `sub` claims, removing legacy username matching, and introducing significant API changes.
Key Highlights
- Security fix: OIDC users now identified by `iss` and `sub` claims to prevent account takeover
- Automatic migration enabled by default (`map_legacy_users: true`)
- Remove `dns.use_username_in_magic_dns` configuration option
- API changes: `GetUser` removed, `RenameUser`/`DeleteUser` now require IDs instead of names
Breaking Changes
- Remove `dns.use_username_in_magic_dns` configuration option
- Remove versions older than 1.56
- API changes: `GET /api/v1/user/{name}` removed, `RenameUser` and `DeleteUser` now require ID
New Features
- SSH agent forwarding support
- PKCE Verifier for OIDC
- Client verify for DERP
- Automatic profile updates with OIDC claims
Full Release Notes
### Security fix: OIDC changes in Headscale 0.24.0
The following issue _only_ affects Headscale installations which authenticate
with OIDC.
_Headscale v0.23.0 and earlier_ identified OIDC users by the "username" part of
their email address (when `strip_email_domain: true`, the default) or whole
email address (when `strip_email_domain: false`).
Depending on how Headscale and your Identity Provider (IdP) were configured,
only using the `email` claim could allow a malicious user with an IdP account to
take over another Headscale user's account, even when
`strip_email_domain: false`.
This would also cause a user to lose access to their Headscale account if they
changed their email address.
_Headscale v0.24.0_ now identifies OIDC users by the `iss` and `sub` claims.
[These are guaranteed by the OIDC specification to be stable and unique](https://openid.net/specs/openid-connect-core-1_0.html#ClaimStability),
even if a user changes email address. A well-designed IdP will typically set
`sub` to an opaque identifier like a UUID or numeric ID, which has no relation
to the user's name or email address.
Headscale v0.24.0 and later will also automatically update profile fields with
OIDC data on login. This means that users can change those details in your IdP,
and have it populate to Headscale automatically the next time they log in.
However, this may affect the way you reference users in policies.
Headscale v0.23.0 and earlier never recorded the `iss` and `sub` fields, so all
legacy (existing) OIDC accounts _need to be migrated_ to be properly
secured.
#### What do I need to do to migrate?
Headscale v0.24.0 has an automatic migration feature, which is enabled by
default (`map_legacy_users: true`). **This will be disabled by default in a
future version of Headscale – any unmigrated users will get new accounts.**
The migration will mostly be done automatically, with one exception. If your
OIDC does not provide an `email_verified` claim, Headscale will ignore the
`email`. This means that either the administrator will have to mark the user
emails as verified, or ensure the users verify their emails. Any unverified
emails will be ignored, meaning that the users will get new accounts instead
of being migrated.
After this exception is ensured, make all users log into Headscale with their
account, and Headscale will automatically update the account record. This will
be transparent to the users.
When all users have logged in, you can disable the automatic migration by
setting `map_legacy_users: false` in your configuration file.
Please note that `map_legacy_users` will be set to `false` by default in v0.25.0
and the migration mechanism will be removed in v0.26.0.
<details>
<summary>What does automatic migration do?</summary>
##### What does automatic migration do?
When automatic migration is enabled (`map_legacy_users: true`), Headscale will
first match an OIDC account to a Headscale account by `iss` and `sub`, and then
fall back to matching OIDC users similarly to how Headscale v0.23.0 did:
- If `strip_email_domain: true` (the default): the Headscale username matches
the "username" part of their email address.
- If `strip_email_domain: false`: the Headscale username matches the _whole_
email address.
On migration, Headscale will change the account's username to their
`preferred_username`. **This could break any ACLs or policies which are
configured to match by username.**
Like with Headscale v0.23.0 and earlier, this migration only works for users who
haven't changed their email address since their last Headscale login.
A _successful_ automated migration should otherwise be transparent to users.
Once a Headscale account has been migrated, it will be _unavailable_ to be
matched by the legacy process. An OIDC login with a matching username, but
_non-matching_ `iss` and `sub` will instead get a _new_ Headscale account.
Because of the way OIDC works, Headscale's automated migration process can
_only_ work when a user tries to log in after the update.
Legacy account migration should have no effect on new installations where all
users have a recorded `sub` and `iss`.
</details>
<details>
<summary>What happens when automatic migration is disabled?</summary>
##### What happens when automatic migration is disabled?
When automatic migration is disabled (`map_legacy_users: false`), Headscale will
only try to match an OIDC account to a Headscale account by `iss` and `sub`.
If there is no match, it will get a _new_ Headscale account – even if there was
a legacy account which _could_ have matched and migrated.
We recommend new Headscale users explicitly disable automatic migration – but it
should otherwise have no effect if every account has a recorded `iss` and `sub`.
When automatic migration is disabled, the `strip_email_domain` setting will have
no effect.
</details>
Special thanks to @micolous for reviewing, proposing and working with us on
these changes.
#### Other OIDC changes
Headscale now uses
[the standard OIDC claims](https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims)
to populate and update user information every time they log in:
| Headscale profile field | OIDC claim | Notes / examples |
| ----------------------- | -------------------- | --------------------------------------------------------------------------------------------------------- |
| email address | `email` | Only used when `"email_verified": true` |
| display name | `name` | eg: `Sam Smith` |
| username | `preferred_username` | Varies depending on IdP and configuration, eg: `ssmith`, `ssmith@idp.example.com`, `\\example.com\ssmith` |
| profile picture | `picture` | URL to a profile picture or avatar |
These should show up nicely in the Tailscale client.
This will also affect the way you
[reference users in policies](https://github.com/juanfont/headscale/pull/2205).
### BREAKING
- Remove `dns.use_username_in_magic_dns` configuration option
[#2020](https://github.com/juanfont/headscale/pull/2020),
[#2279](https://github.com/juanfont/headscale/pull/2279)
- Having usernames in magic DNS is no longer possible.
- Remove versions older than 1.56
[#2149](https://github.com/juanfont/headscale/pull/2149)
- Clean up old code required by old versions
- User gRPC/API [#2261](https://github.com/juanfont/headscale/pull/2261):
- If you depend on a Headscale Web UI, you should wait with this update until
the UI have been updated to match the new API.
- `GET /api/v1/user/{name}` and `GetUser` have been removed in favour of `ListUsers` with an ID parameter
- `RenameUser` and `DeleteUser` now require an ID instead of a name.
### Changes
- Improved compatibility of built-in DERP server with clients connecting over
WebSocket [#2132](https://github.com/juanfont/headscale/pull/2132)
- Allow nodes to use SSH agent forwarding
[#2145](https://github.com/juanfont/headscale/pull/2145)
- Fixed processing of fields in post request in MoveNode rpc
[#2179](https://github.com/juanfont/headscale/pull/2179)
- Added conversion of 'Hostname' to 'givenName' in a node with FQDN rules
applied [#2198](https://github.com/juanfont/headscale/pull/2198)
- Fixed updating of hostname and givenName when it is updated in HostInfo
[#2199](https://github.com/juanfont/headscale/pull/2199)
- Fixed missing `stable-debug` container tag
[#2232](https://github.com/juanfont/headscale/pull/2232)
- Loosened up `server_url` and `base_domain` check. It was overly strict in some
cases. [#2248](https://github.com/juanfont/headscale/pull/2248)
- CLI for managing users now accepts `--identifier` in addition to `--name`,
usage of `--identifier` is recommended
[#2261](https://github.com/juanfont/headscale/pull/2261)
- Add `dns.extra_records_path` configuration option [#2262](https://github.com/juanfont/headscale/issues/2262)
- Support client verify for DERP [#2046](https://github.com/juanfont/headscale/pull/2046)
- Add PKCE Verifier for OIDC [#2314](https://github.com/juanfont/headscale/pull/2314)
### Changelog
* ede4f97a16b0b2d357d3584431e9feb34d43fc89 Fix typos
* fa641e38b8a62ad665e15370a2b29a48c6486060 Set CSRF cookies for OIDC (#2328)
* 1ab7b315a2707434b67cd83f28c6322a3c24b4c4 Update apple.md for latest version of iOS (#2321)
* 38aef77e5419f7cc28f525226ed9567fe16dd166 allow @ and Log if OIDC username is not consider valid (#2340)
* b81420bef1e8d042f5bc126565c70b58f7321900 feat: Add PKCE Verifier for OIDC (#2314)
* caad5c613d90a11de1db4b93da69fa352a35c226 fix nil pointer deref (#2339)
* 9313e5b058cb161927708e47622ef2bdef0ab9dc flake.lock: Update (#2313)
* f9bbfa5eabcbb0dc10866aada42f02a3602e1a8e flake.lock: Update (#2320)
* 41bad2b9fdac7171e68babce0b6b045316d298f3 flake.lock: Update (#2324)
* 610597bfb74145626a9169a11bc4ad7145246660 flake.lock: Update (#2342)
* e88406e8379691cae00fa82afbf52ecfacd5d292 set changelog date (#2347)
* e4a3dcc3b884bc5f37775df04389cba04bb5990b use headscale server url as domain instead of base_domain (#2338)