Impersonate does not work with separate admin hostname
Describe the bug
Admin console impersonate feature does not properly work on cross-domain setup when hostname-admin does not match realm hostname.
Impersonate feature invokes admin endpoint on admin hostname which sets cookies in a response, but they are set for a wrong domain:
POST https://admin-host/admin/realms/example/users/99c1caa0-9f15-11ec-bfac-7446a03ae071/impersonation
set-cookie: KEYCLOAK_IDENTITY=snip; Version=1; Path=/realms/example/; SameSite=None; Secure; HttpOnly
set-cookie: KEYCLOAK_SESSION=example/99c1caa0-9f15-11ec-bfac-7446a03ae071/snip; Version=1; Expires=Wed, 09-Mar-2022 04:33:24 GMT; Max-Age=36000; Path=/realms/example/; SameSite=None; Secure
It then opens account endpoint on admin hostname which redirects to hostname for the realm, where session is not present since cookies are not set for that domain.
Version
17.0.0
Expected behavior
Impersonation flow is aware of different admin hostname and performs redirects to end up starting session on realm hostname
Actual behavior
Impersonation flow starts session on admin hostname which is then unusable.
How to Reproduce?
Setup keycloak with separate domains for hostname and hostname-admin. Attempt to use impersonation.
Anything else?
This is not a new issue https://keycloak.discourse.group/t/cross-domain-impersonate/10302
I think I'm encountering the same issue on 16.1.1
is there a workaround for this ?
by the way, this is also true when using the impersonate button in the admin interface
redirection does not open the session
replacing the public address with the private address will work
EDIT:
was able to circumvent the issue by:
- remove
frontendURLfor the realm - forcing the usage of https on port
8443in the loadbalancer
impersonate button now works, but not as I'd expected as it impersonates over the private IP address, not the public URL
We are currently using the version 15.0.2 and are having the same problem. Is this fixed in newer versions?
Just tried it out with 20.0.0 and it seems to be still not working :(
It works for me in 20.0.1 when there is only KC_HOSTNAME_ADMIN set, while KC_HOSTNAME is unset.
I just tested it with 20.0.1 and I have the exact same issue. @proffase What do you mean with KY_HOSTNAME is unset? That produces an error for me when I start Keycloak. (Strict hostname resolution configured but no hostname setting provided)
I have hostname-strict=false in the config, and KC_HOSTNAME_ADMIN value passed through environment variable.
KC_HOSTNAME or --hostname is not specified in the configuration I use.
And it works for me with that setup.
@proffase Interesting. How do you define the domain for each realm? With the “Frontend URL” setting in the realm?
@korridor For my use case I don't need separate domains for realms, so no "Frontend URL" defined. I didn't test it with separate realm domains.
Thanks for the report, but unfortunately due the amount of other reported issues and other priorities, Keycloak team does not have time to properly triage this bug. So preliminary added to Backlog for now. It will be helpful if:
- You can verify if still applicable in latest Keycloak released version. If not, then it is welcome to close this issue.
- If you figure that this may not be a valid bug (for example just a mistake in configuration etc), it will be also welcome to close this issue
- If still applicable in latest version, it will be welcome to add the comment as well, that this was still reproduced with latest Keycloak version as it is very valuable info. Anyone is welcome to comment with this or add other relevant comments to this issue.
@mposolda This is still a problem in the most current Keycloak version (tested with 21.1.0).
I looked into the code behind the issue and I think that the way the “Impersonate” button in the Administration Console currently works, this can not really work.
The XHR request to /auth/admin/realms/some-realm/users/some-id/impersonation sets the cookie for the impersonated session. But if the admin console and the some-realm in this example have a different hostname, the cookie is set on the wrong hostname. With the Set-Cookie header that this request uses to set the session cookie, this cannot be achieved (at least not in this request) because the domains could be entirely different.
It is really sad to hear, that this bug does not get any priority, as for me and most likely many others here, this is an ultimate feature for troubleshooting and fixing issues of non technical users, without having to do a screenshare session everytime, or for user provisioning and permission configuration, on apps which do not support user auto creation or are poorly integrated. I really hope, that this issue will see some love in the near future, to make a great software even better.
Im curious how this is planned to be fixed, if there is a plan, and if we can help in any way??? I'm casually brainstorming solutions and it doesn't seem like an easy problem to solve.
We have run into the same issue in our deployment of Keycloak (w/ phase two extension). We have Keycloak sitting behind Isitio (open to internet) and a reverse proxy (company vpn). Istio has virtual services allow traffic only on the /realms/, /resources/, or robots.txt routes (as recommended here) so the end user can log into the client. The reverse proxy allows us full access to the master or any client admin console over tailscale.
Our only viable workaround was to configure Istio to allow access only to the admin console of the client in which users are logging into and not master. It's really not ideal as we don't want to expose the admin console like this, but we see no other option.
Keycloak, please allow us to setup specific URLs in which the client is hosted at and then navigate to these URLs when creating the impersonated sessions! It would also be nice to be able to navigate to another page other than account post impersonation.
if anyone is curious on the setup, here is the http match code to reject access to anything with master in it:
- name: keycloak-master-protect
match:
# Reject requests that contain 'master' after the prefix '/keycloak/admin/'
- uri:
regex: "^/keycloak/admin/.*master.*"
ignoreUriCase: true
directResponse:
status: 503
body:
string: '{"error": "unknown error"}'
The only solution I can think now to solve this problem is to allow configuring the domain attribute of cookies to allow sharing cookies across subdomains.
However, that will only work if you are using https and both frontend and admin URLs share the same domain. Is that a problem for any of the use cases described so far?
It should work because the KEYCLOAK_IDENTITY cookie is set with SameSite=None and Secure making it possible to use it across different subdomains. Not sure if 100% reliable due to more strict policies on certain browsers (and user browsers).
However, that will only work if you are using
httpsand both frontend and admin URLs share the same domain. Is that a problem for any of the use cases described so far?
That would work my end, assuming Chrome would allow sharing across subdomains.
However, that will only work if you are using
httpsand both frontend and admin URLs share the same domain. Is that a problem for any of the use cases described so far?
In our use-case that would not work, because we use different domains. Wouldn't it work to set the cookie based on the configured Frontend-URL on the realm?
IMO the only proper way to resolve this is to implement a new impersonation feature where admin side would act as an identity provider for frontend side.
May be IdP initiated SAML sign on flow? I do not remember if OIDC has something for this case.
@Xerkus That is the thing ... If we can sort out a solution that leverages the existing mechanisms we will be forced to come up with a new solution and implementation to admin impersonation. I would like to know if we can avoid that somehow.
Can you also elaborate more how a IdP-initiated login would work? Even for SAML? We will end up with the same issue, no?
@pedroigor one way or another it has to a be a redirect flow establishing new session on the realm domain. No cookie trickery can solve the cross-domain boundary.
Redirect could be with a simple opaque single use token with everything stored server side. It could be JWT with assertions. Or it could be SAML assertions.
With the caveat that I did not look at SAML features in keycloak or keycloak internals in general, I suggest to look at existing login flows via identity providers. This would utilize existing maintained features over having to introduce more code to maintain. I imagine existing back-channel logout features could also be utilized to terminate impersonated session when the admin session ends.
Admin interface for SAML 2.0 identity providers configures only SP-initiated login flow as far as I can tell. The problem with SP-initiated login flow is that it requires several redirects back and forth and must begin with the frontend side. Admin side would then need to redirect to frontend first. That initial endpoint would be exposed to the open internet. Besides complicating impersonation feature quite a bit it would also expose admin interface location. Admin interface would also require extra IdP related endpoints. Not ideal.
However, if existing SAML implementation could be setup to accept unsolicited SAML authentication responses for IdP-initiated login it would be a perfect fit for the impersonation feature. SAML 2.0, 5.1.4 IdP-Initiated SSO: POST Binding. From the admin interface it becomes a matter of preparing a web form with saml assertions that can be submitted to the frontend side.
I see, thanks.
@pedroigor one way or another it has to a be a redirect flow establishing new session on the realm domain. No cookie trickery can solve the cross-domain boundary.
The SameSite=None Secure behavior can work if both URLs share the same domain (e.g.: admin.mykc.org and auth.mykc.org). And I think it is backed by the specs.
There is a constraint though (subdomains) and I'm trying to understand if we can agree on it rather than introduce something else.
About IdP-Initiated SSO, I'm still not sure how it would work if after clicking on Impersonate the user is automatically authenticated by forging a KEYCLOAK_IDENTITY cookie that maps to the session created for the impersonation.
admin.mykc.org and auth.mykc.org
This is a very dangerous way to share high privilege admin session with unconstrained subdomains. I don't think this should be considered at all.
About IdP-Initiated SSO, I'm still not sure how it would work if after clicking on
Impersonatethe user is automatically authenticated by forging aKEYCLOAK_IDENTITYcookie that maps to the session created for the impersonation.
It won't.
Clicking impersonate in admin interface would require admin frontend app to request from the backend a signed short living SAML authentication response. Admin interface then sets authentication response to a field in http form targeting the realm's saml endpoint and submits it. That will cause redirect.
From realm point of view it is like a regular login via identity provider. A new user session is created as usual.
The difference is that SAML endpoint must be configured to accept SAML authentication response without sending authentication request first. That includes not checking inResponseTo field which should not be present as there was no authentication request.
Hm... Actually. SAML authentication request can be created and recorded internally when admin ui requests the authentication response. At this point the redirect from admin web form will just finish the second half normal SAML SP initiated flow without actually making the original request to IdP since it is already recorded for both parties which is actually the same party.
There might be timing problem with propagation of recorded request through clustered deployments.
If we look at the problem like that then OIDC login flow might be used the same way where only second part actually happens. JWT or saml response would need to have additional claims/assertions and extra login logic to tie the new user session to admin session.
Let's see if diagrams work.
sequenceDiagram
Admin client->>+Admin client: Impersonate John
Admin client->>+Admin server: Get SAML authn response
Admin server->>Admin server: Internally ask realm's configured identity provider to generate and record SAML authn request
Admin server->>Admin server: Create SAML authn response for generated request
Admin server->>-Admin client: SAML authn response
Admin client->>Admin client: Set SAML authn response to form
Admin client->>-Realm domain: Submit form to /realms/master/broker/impersonate/endpoint
Realm domain->>browser: Login OK. Start user session.
I see, basically what we do today using the KEYCLOAK_IDENTITY cookie but leveraging the authentication protocols to avoid re-authenticating.
The question is can login via specific identity provider be configured to bypass 2FA? Also it would need to be hidden from api/UI as a valid login via option.
Not sure ... In this case, there is no identity provider (if you mean broker) involved.
Still, for me, the best for now is to at least support sub-domains in cookies.
I should point out security implications of such approach.
Sitting ducks and subdomain takeover attacks are a threat if cookies are set to be insecurely shared with subdomains. https://developer.mozilla.org/en-US/docs/Web/Security/Subdomain_takeovers
For example subdomain is setup for some github pages site. It was convenient but domain verification feature was not used:
Verifying your domain stops other GitHub users from taking over your custom domain and using it to publish their own GitHub Pages site. Domain takeovers can happen when you delete your repository, when your billing plan is downgraded, or after any other change which unlinks the custom domain or disables GitHub Pages while the domain remains configured for GitHub Pages and is not verified.
Sitting Duck waiting to happen and when it does how will it affect the proposed approach?
I should point out security implications of such approach.
You would opt-in for the subdomain support. If you choose to use it there is an assumption that you manage the lifecycle of your domain(s) and hosts. See, I'm not pushing a proposal but trying to understand if we can solve the problem without introducing big changes or workarounds. Using subdomains and respecting the rules around SameSite seems like leveraging an existing mechanism. With limitations ... And that is what I want to check, how positively this proposal will impact people? Looks like we have a draw so far...
Sharing a domain between the frontend and the admin URLs is a common case? How much leveraging the cookies spec will solve for those here looking for a fix?
About your proposal, I don't like very much the idea of forcing the authentication using a standard protocol. Let's hear from others too.
@pedroigor in our case, we don't share domains. We use Keycloak as backing our multi-tenancy application and create a realm for each tenant. Each tenant can potentially be on a different domain (whereby in fact we use about 5 domains atm).
Anyway: If sharing the domain makes the implementation easier, more stable, sticks to more standards, better maintainable, ... this would already provide a massive improvement for us.