I am attempting to setup OIDC authentication against Okta by starting with the documented Keyclock configuration and making the needed changes. I am running Foreman 3.1.1.2 [Satellite 6.11.1.1].
# hammer setting list --search oidc
---------------|----------------|---------------------------------------------------|---------------------------------------------------------------------------------
NAME | FULL NAME | VALUE | DESCRIPTION
---------------|----------------|---------------------------------------------------|---------------------------------------------------------------------------------
oidc_jwks_url | OIDC JWKs URL | https://<redacted>.okta.com/oauth2/default/v1/keys | OpenID Connect JSON Web Key Set(JWKS) URL. Typically https://keycloak.example...
oidc_audience | OIDC Audience | ["api://default"] | Name of the OpenID Connect Audience that is being used for Authentication. In...
oidc_issuer | OIDC Issuer | https://<redacted>.okta.com/oauth2/default | The iss (issuer) claim identifies the principal that issued the JWT, which ex...
oidc_algorithm | OIDC Algorithm | RS256 | The algorithm used to encode the JWT in the OpenID provider.
---------------|----------------|---------------------------------------------------|---------------------------------------------------------------------------------
# hammer setting list --search delegation
-------------------------------------------------------|--------------------------------------------------------|----------------------------------------------------|---------------------------------------------------------------------------------
NAME | FULL NAME | VALUE | DESCRIPTION
-------------------------------------------------------|--------------------------------------------------------|----------------------------------------------------|---------------------------------------------------------------------------------
login_delegation_logout_url | Login delegation logout URL | https://<redacted>/users/extlogout | Redirect your users to this url on logout (authorize_login_delegation should ...
authorize_login_delegation_auth_source_user_autocreate | Authorize login delegation auth source user autocreate | External | Name of the external auth source where unknown externally authentication user...
authorize_login_delegation | Authorize login delegation | true | Authorize login delegation with REMOTE_USER HTTP header
authorize_login_delegation_api | Authorize login delegation API | true | Authorize login delegation with REMOTE_USER HTTP header for API calls too
-------------------------------------------------------|--------------------------------------------------------|----------------------------------------------------|---------------------------------------------------------------------------------
I started with the documented instructions to setup Keycloak, but since I don’t have a Keycloak server the keycloak-httpd-client-install failed. However, this got me the initial /etc/httpd/conf.d/foreman-openidc_oidc_keycloak_ssl-realm.conf. I then massaged the configuration from there to get where I am now.
I have tested it with each of the following three directives individually and in different combinations, without success.
OIDCRemoteUserClaim email
OIDCOAuthRemoteUserClaim email
RequestHeader set REMOTE_USER %{OIDC_CLAIM_email}e
This is from the auth_openidc debug logs:
[Tue Aug 23 13:36:13.681073 2022] [auth_openidc:debug] [pid 310166] src/mod_auth_openidc.c(1751): [client 10.1.250.187:53058] oidc_set_request_user: set remote_user to "jwinder@wcbradley.com" based on claim: "email"
...
[Tue Aug 23 13:36:13.735446 2022] [auth_openidc:debug] [pid 310166] src/mod_auth_openidc.c(1489): [client 10.1.250.187:53058] oidc_handle_existing_session: set remote_user to "jwinder@wcbradley.com"
...
I am not sure what else out of the httpd logs would be helpful. If desired, I could capture an entire authentication session and attempt to scrub the log of private details.
I have tried it with and without that directive, same result.
Also, I got Okta support to verify the URIs and such that I am using, and they are correct.
Is there a way I can see what variables are being passed when the process attempts to create the user? I’m not a developer, obviously, or I would hopefully know how to do that in a debugger.
Yeah, I’m having a similar issue here with Kanidm.
Auth works fine and I can see the OIDC_CLAIM stuff in the apache logs but for some reason Foreman is not seeing it, so either the RequestHeader isn’t actually being set (unfortunately the Foreman logs don’t actually tell me anything useful about what it’s receiving even at debug level) or it’s not being observed by Foreman for some reason…
If the auth code is in raw ruby I might embroider it with some extra log statements (I’ve had to do that before for another issue :/) and see if I can get some clarity there.
OK, so after peppering various files in the app/services/sso hierarchy with debug statements I’ve found the following.
In this scenario Foreman is NOT using the HTTP_REMOTE_USER and associated headers for the purpose of retrieving the username (they are arriving as expected though). It’s instead trying to extract them from a JWT, however the JWT it’s attempting to extract them from is an ACCESS token not an ID token and as such it doesn’t contain any user information ergo the user creation fails because there’s no username in the ACCESS token.
I’ll do some further rummaging around to see if I can find a workaround, but this might be a “wait for the proper generic OpenID implementation” problem.
Looks like the Apache stuff is being overridden by the OpenidConnect class so the Apache SSO class receives the values as expected but then Foreman goes on to use the OpenidConnect class which clobbers the authentication before the stack gets to the point of actually trying to create the user (there’s no reason the “find_or_create_external_user” method in the Apache class shouldn’t work but it’s NOT being called at all, instead we’re calling “find_or_create_external_user_from_jwt” on OpenidConnect.)
I’m not nearly experienced enough with Ruby and Rails to understand why they’re being called in the order they’re being called.
But the bottom line here appears to be that OpenidConnect DOESN’T actually implement OpenID Connect (certainly not in a compliant fashion anyway) it implements something that looks kinda-sorta like OpenID Connect based on the behaviour of Keycloak and it’s pure happenstance that it works with SOME other providers.
TL;DR: Add “RequestHeaders unset OIDC_ACCESS_TOKEN” to your Location stanza.
OK, so it IS possible to get it to work.
I’ve answered the question vis-a-vis calling order, looks like it’s based on the METHODS list in sso.rb which makes sense, in order to disable the OpenidConnect class outright I removed it from the list.
That doesn’t resolve the issue though due to this (from app/services/sso/apache.rb):
def available?
return false unless Setting['authorize_login_delegation']
return false if controller.api_request? && !(Setting['authorize_login_delegation_api'])
return false if http_token.present?
return false if controller.api_request? && request.env[CAS_USERNAME].blank?
true
end
Most particularly the check for the presence of http_token, basically the module bails out if it sees that HTTP_OIDC_AUTH_TOKEN is set (assuming that it should hand over to the next module down the chain).
Commenting that line out gets me functional Apache-based SSO.
I’ve also reverted the changes I made to sso.rb and it’s still working so presumably if it doesn’t bail out of the Apache::available? method it considers that as a successful authentication.
By eliminating the OIDC_ACCESS_TOKEN header this function will return true and the auth will work as expected.