I have read the docs up and down but still can’t figure out the right set of options for hostname/proxy when running behind a reverse proxy.
Setup:
Everything in containers (podman rootless if that matters) in a single podman network
Traefik as reverse proxy
terminates TLS
forwards for host keycloak.example.com
exposes only port 443 to users
applications and keycloak all talk http in internal podman network
for now only by container names (keycloak, app1, app2, etc.)
I could add DNS aliases if needed, but not yet done
The only option I am sure of is KC_HTTP_ENABLED=true.
So what is the right config for
KC_HOSTNAME
https://keycloak.example.com or just keycloak.example.com and why?
KC_HOSTNAME_BACKCHANNEL_DYNAMIC
Is it needed if I use DNS aliases so that applications use keycloak.example.com instead of container names?
Or only if they communicate through container names because otherwise keycloak does not accept requests with hostname keycloak?
Does that option only affect the hostname part or also scheme and port? So that I would need it even when using DNS aliases because keycloak would not treat http on 8080 correctly without setting this to true?
KC_PROXY_HEADERS
I don’t understand why it’s needed when hostname is set to full URL… What does that part regarding origin mean?
I would really appreciate not only a valid config (or multiple versions that would work and how they differ), but also an explanation why. It seems these options relate to the question what requests are answered by keycloak and also how redirect URLs are created and what Frontend/Backend endpoints are made available, but the documentation is a bit short to fully understand the concept. Please note that this question is also more about understanding than just trying things until it works.
KC_HOSTNAME
Keycloak compares the incoming requests against this setting. If you only specify the hostname, without schema and port, Keycloak expects request coming from either http or https on their default ports.
If you want to make sure, that the Keycloak server can only be called via https, then also specify the schema. If you use a different port than the default ones, also specify the port.
KC_HOSTNAME_BACKCHANNEL_DYNAMIC
Your second and third assumption is correct.
KC_PROXY_HEADERS
With this setting, you basically tell Keycloak from where to read the request info (scheme, host, port, originating ip address).
If you run Keycloak behind a proxy (what you do), Keycloak must not read the request info itself, but the info from the originating request, which should (must) be put to the headers from the proxy to the Keycloak node.
Use xforwarded if you forward the request info in the custom X-Forwarded-... headers. If you use the standard Forwarded headers, use forwarded as value.
Does this make the things more clear and understandable to you?
Yes that helps, definitely more clear than anything else I could find on that topic.
So all of these statements should be correct?
When KC_PROXY_HEADERS is set, Keycloak distinguishes backchannel from frontchannel communication by the existence of proxy headers: No proxy header = Backchannel communication
For proxied/frontchannel communication, scheme/port are taken from proxy headers instead of the actual way of access and that is why proxied requests are accepted even when they come in on http/8080 and KC_HOSTNAME actually is set to https/443 (assuming the reverse proxy sets forwarded header correctly)
No KC_HOSTNAME_BACKCHANNEL_DYNAMIC needed for this to work
KC_HOSTNAME_BACKCHANNEL_DYNAMIC must be true for Keycloak to accept any deviation in scheme/hostname/port from the configured KC_HOSTNAME for requests that carry no proxy header and are therefore identified as backchannel communication
Disclaimer: I’m not part of the development team and haven’t looked into the source code that much on this topic. I can just tell you from my experiences/tests.
I don’t know, if Keycloak internally really distinguishes between backchannel and frontchannel communication. But I would assume the logic as you mentioned. So, basically yes.
IMHO it’s more like a “requests with (x-)forwarded headers and requests without”.
If the hostname-backchannel-dynamic switch is not set, then either the headers must point to the configured hostname or the request itself must be made to the configured hostname (with proper host header set).
If it is set, Keycloak would also accept requests to different hostnames (schemes, ports), but all the important redirects, form action urls, etc. will be created using the configured hostname. You can get an idea when calling the .well-known/openid-configuration endpoint.
So I guess my understanding is well enough for that part and getting details would require a look into the code (which I will not do for now).
On the other hand I have some homework to do regarding the different auth flows. For example, I am not even sure if the backchannel config. matters for Standard/Authorization Code flows.
For now I have a working setup with a first client while BACKCHANNEL_DYNAMIC is still false. In my podman network I have now set a DNS alias for keycloak.mydomain.com on the traefik container (not the keycloak container!) so that clients could just use the full frontchannel URI to talk to keycloak (via traefik), which would remove the need for BACKCHANNEL_DYNAMIC anyway and would still keep communication within the container network.
In several aspects I am not even sure if that setup makes a lot of sense: Do clients even directly talk to keycloak when using Authorization Code? If so, is it a sensible approach to run this via frontchannel URI and reverse proxy? I guess I will only find answers when looking into the OIDC Authorization Code message flow, but for now I am content with a working setup.