Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow use of the preferred_username OIDC claim. #1287

Closed
wants to merge 5 commits into from

Conversation

meson800
Copy link

@meson800 meson800 commented Mar 26, 2023

Previously, Headscale would only use the email OIDC claim to set the Headscale user. In certain cases
(self-hosted SSO), it may be useful to instead use the preferred_username to set the Headscale username. This also closes the existing issue #938.

This adds a config setting to use this claim instead. The OIDC docs have been updated to include this entry as well. In addition, this adds an Authelia OIDC example to the docs.

I didn't see any existing unit or integration tests for OIDC, so wasn't sure where to add such tests. If I'm wrong, I'm happy to add them!

I did test this against my SSO server, and confirm that the preferred_username grant if you set it works as expected.

  • read the CONTRIBUTING guidelines
  • raised a GitHub issue or discussed it on the projects chat beforehand (one already exists)
  • added unit tests
  • added integration tests
  • updated documentation if needed
  • updated CHANGELOG.md

@kradalby
Copy link
Collaborator

I think this looks sensible, the tests for oidc are located here: https://github.com/juanfont/headscale/blob/main/integration/auth_oidc_test.go

Would be good if we could have one.

@meson800
Copy link
Author

Thanks. It looks like the current integration tests don't explicitly set claims, but the underlying mockoidc does let you queue up successive users with potentially different email/preferred_username claims. I'll try my hand at creating an integration test for this.

@vbrandl
Copy link

vbrandl commented Apr 14, 2023

Were you able to implement a testcase? I'd love to see this feature since it is blocking me from using OIDC for headscale.

@meson800
Copy link
Author

@vbrandl I'm working on it, but I'm new to Go and this codebase, and still figuring out how to more efficiently run the integration tests locally without just pushing to Github. The current OIDC tests don't actually check the claim responses at all, so I also need to thread those checks in.

On my stuff, I'm running from these docker images if you want to try them out: https://github.com/meson800/headscale/pkgs/container/headscale

@vbrandl
Copy link

vbrandl commented Apr 14, 2023

I just tested your image and it does work as expected, but I'd prefer to run the official image for my infrastructure. So I'll wait until this gets merged.

@jonathanspw
Copy link
Contributor

Thank you for this work @meson800

@jonathanspw
Copy link
Contributor

I did some testing on this and it appears to work great. I noticed the email is still cited on the "congrats you've registered your machine" page or whatever it is which might cause some confusion.

That should probably also reflect the username if the setting is enabled.

@meson800
Copy link
Author

I'll look into changing that welcome page template @jonathanspw to respect the setting as well. On my local machine I have the mockoidc up and running with new mock users. this will be mergable hopefully within a week or so.

@meson800
Copy link
Author

@kradalby I added integration tests as requested, so this should be good for review. In sum, this PR includes:

  1. Added the option to use the username claim as above, and updated the documentation.
  2. Updates to the mockoidc runner; now you can give it a series of users in the format username_claim <email_claim@example.org> and have the mockoidc report those users.
  3. Using that functionality, there are now integration tests that register clients under test-user <test-email@example.org> with the different claim options. I do this in a kind of sketchy way, by checking the FQDN string of TailscaleClients (e.g. makes sure they end in test-user.headscale.net or test-email.headscale.net, depending on the setting. I picked this method of validating since in the existing TailscaleClient interface that HSIC implements, there isn't a user field and I didn't want to add them.
  4. As suggested by @jonathanspw, I also updated the post-login HTML template to say "Authenticated as username, you can now close this window", where username is filled in from the preferredUsername or email claim depending on the setting.

Even with a full nix develop on both actual Linux and WSL on Windows, I was unable to run one of the go formatters as suggested in the README, and couldn't figure out the path munging needed to install the tool on my own. If I'm failing some format or lint check, I can try to fix on my end if it's easier.

@@ -63,6 +65,29 @@ func mockOIDC() error {
}
accessTTL = newTTL
}

mockUsers := os.Getenv("MOCKOIDC_USERS")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be great if this is prefixed with "HEADSCALE_INTEGRATION_"

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is (technically) not just useful for integration tests, as this environment variable controls what mockoidc does if you e.g. set the env variable and ran headscale mockoidc yourself. I followed the naming of the other variables in the mockOIDC function (MOCKOIDC_CLIENT_ID, MOCKOIDC_CLIENT_SECRET, and so on). I can still prefix this if desired.

oidcMap := map[string]string{
"HEADSCALE_OIDC_ISSUER": oidcConfig.Issuer,
"HEADSCALE_OIDC_CLIENT_ID": oidcConfig.ClientID,
"CREDENTIALS_DIRECTORY_TEST": "/tmp",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this for Headscale or mockoidc? if its headscale, then prefix would be good.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you referring to CREDENTIALS_DIRECTORY_TEST? I copied this environment map from the TestOIDCAuthenticationPingAll test. For whatever historical reason, that test passes in the client secret with a file instead of directly, which is the reason for the directory. I could simplify if needed (I don't see why CREDENTIALS_DIRECTORY_TEST is a separate env variable).

@kradalby
Copy link
Collaborator

Otherwise, looks great!

@meson800
Copy link
Author

meson800 commented Apr 24, 2023

I fixed the lint issues and updated the gold-standard YAML files to match the added configuration option. (and rebased on main again).

Previously, Headscale would only use the `email` OIDC
claim to set the Headscale user. In certain cases
(self-hosted SSO), it may be useful to instead use the
`preferred_username` to set the Headscale username.
This also closes juanfont#938.

This adds a config setting to use this claim instead.
The OIDC docs have been updated to include this entry as well.
In addition, this adds an Authelia OIDC example to the docs.
Updated the MockOIDC wrapper to take an environment variable that
lets you set the username/email claims to return.

Added two integration tests, TestOIDCEmailGrant and
TestOIDCUsernameGrant, which check the username by checking the FQDN of
clients.

Updated the HTML template shown after OIDC login to show whatever
username is used, based on the Headscale settings.
@loprima-l
Copy link
Contributor

Isn't it better if we add an internal mail domain for this case, i think this would be easier to deal with in the api and al taht stuff :

ex:

preferred_username : loprima would make an email like loprima@headscale.sso

It would be more accurate with the Tailscale way of managing sso I guess

@meson800
Copy link
Author

meson800 commented Apr 27, 2023 via email

@loprima-l
Copy link
Contributor

I agree with that, but aren't we there to implement best practices to follow ? I think using multiple ways of storing a user "id" will be a mess that will complicate the interconnection between Headscale and other pieces of software.

My advice is that we should have one way to identify a user, if we already have email based user distinction, wouldn't it be bad to implement other ways that doesn't fit with the Tailscale way ?

@meson800
Copy link
Author

meson800 commented Apr 27, 2023 via email

@loprima-l
Copy link
Contributor

As you can see here : See here

Tailscale is using the provider domain, I think that's a better way to do it, maybe we must implement this way also in Headscale, but it would be necessary to use a headscale prefix, so we don't break up the compatibility with native Tascale APP, I don't know how they refer to the login methods.

@meson800
Copy link
Author

meson800 commented Apr 27, 2023 via email

@loprima-l
Copy link
Contributor

I think that would be AMAZING <3

Like it will help a lot in the infinite quest of having a standardized system, I'm really busy to work on it but if you need help let me know I could write the docs if you want, I'm already planing to open a PR to add a gitbook.

@meson800
Copy link
Author

I merged against main so this can run tests again.

@kradalby do you want me to change the environment variables as in the review above? I named them to be consistent with the surrounding code.

@juanfont do you want me to update/edit this PR in some way/can you review? I see that there are other OIDC PRs that this might conflict with this (#1435), I can incorporate those changes if needed.

@leeaash
Copy link

leeaash commented Jul 22, 2023

When will this PR be merged to main branch, this feature is acutely I want.

@joachimtingvold
Copy link

Yes, please. Immutable identifier to be used by headscale would be nice to have, as most IDPs allows users to change their emails, and thus break their headscale setup/login.

@meson800
Copy link
Author

I can try to update this branch to get it considered again; a lot has changed since I submitted the PR.

@jonathanspw
Copy link
Contributor

I can try to update this branch to get it considered again; a lot has changed since I submitted the PR.

Please do.

@kradalby
Copy link
Collaborator

Hi! as part of #1473, we have reorganised a lot of the code.

To clear PRs that needs to be rebased or redone, we are closing open PRs that will require significant code change to be merged.

In addition, the issue of the PR might in some cases have been fixed, change or no longer relevant, so it would be great if this is considered as well.

Thank you for your contribution!

If it is still relevant and the PR is reopened, we will aim at getting the changes into the next release after the reorg if accepted.

@meson800
Copy link
Author

meson800 commented Oct 30, 2023

@kradalby I can't reopen the PR myself, but this is ready to be considered again; I rebased and tested meson800:oidc_username_claim on top of the re-orged main.

Summary

  • read the CONTRIBUTING guidelines
  • raised a GitHub issue or discussed it on the projects chat beforehand (one already exists)
  • added unit tests
  • added integration tests
  • updated documentation if needed
  • updated CHANGELOG.md

This PR adds the ability to use the preferred_username claim, closing #938. I added documentation, updated the CHANGELOG.md, and added integration tests; I didn't add a unit test but can if needed.

The short version is that you can now set the use_username_claim config setting inside the oidc category. If set to true, the preferred_username claim is used to set the Headscale username instead of the email claim. I updated the way mockoidc is called to let integration tests set these claims.

Previous review comments

There were some comments on prefixing some of the constants. I've kept it consistent (e.g. not prefixing the mockoidc constants with HEADSCALE_INTEGRATION_ to stay consistent with the other existing constants and tests) for now but could easily rename those if desired.

Integration tests

In particular, the integration tests are implemented by setting the mock OIDC user to test-user <test-email@example.com>, then it checks that the FQDN of the user is test-user.headscale.net or test-email.headscale.net.

Test failures

Of note, I ran all of the integration tests in a stub PR in my repo; some integration tests appear flaky so I had to rerun them, but one still fails despite reruns, TestPingAllByIP. This one also seem to be failing in other PR runs in this repo so I don't think it's related (example 1)

Real tests

I generated a Docker image for this PR and tested it out in my tailnet, and it works, correctly using the preferred_username claim:
image

@LarssonOliver
Copy link

Looks awesome! Would really help my auth setup.

@Helvio88
Copy link

I am using meson800's image and it works great. +1 to merge it!

@Helvio88
Copy link

Any reason this wasn't merged? I see it's closed. I think it's a very welcome improvement.

@meson800
Copy link
Author

I think that one of the concerns is it wasn't generalizable enough. See this comment: #938 (comment)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants