keycloak/louketo gatekeeper -- doesn't automatically redirect to keycloak login - kubernetes

I am setting up gatekeeper/louketo as a reverse proxy for a browser app. I have the proxy deployed as a sidecar in a kubernetes pod, with keycloak elsewhere in the same cluster (but accessed by a public URL). Gatekeeper is behind an nginx ingress, which does tls termination.
[I have tried both the most current louketo version and also the fork oneconcern/keycloak-gatekeeper. Some differences, but the issue is the same, so I think its a problem in my configuration.]
Gatekeeper, no matter how I set up the config, reads the discovery url of my realm, but then doesn't redirect on login there. Rather it redirects to my upstream app, using the /oauth/authorize path. I can manually force my app to redirect again to keycloak, but on return from keycloak, gatekeeper doesn't recognize the cookie, and catches me in a redirect loop.
It would seem I am making some simple config error, but I've been working on this for two days, and am at my wit's ends. (Even hacked in extra debugging into the go code, but haven't studied it enough to really know what it is doing.)
My config (best guess of many different variants tried):
- --config=/var/secrets/auth-proxy-keycloak-config.yaml
- --discovery-url=https://auth.my-domain.com/auth/realms/my-realm
- --listen=:4000
- --upstream-url=http://127.0.0.1:3000
- --redirection-url=https://dev.my-domain.com/
- --enable-refresh-tokens=true
- --enable-default-deny=true
- --resources=uri=/*|roles=developer
- --resources=uri=/about|white-listed=true
- --resources=uri=/oauth/*|white-listed=true
The ingress serves https://dev.my-domain.com and routes to port 4000, which is the auth proxy sidecar. It is setup with a lets-encrypt certificate, and terminates tls. I don't use tls in the proxy (should I?). Upstream app at port 3000. Keycloak is at auth.my-domain.com. In auth-proxy-keycloak-config.yaml I have encryption key, and client_id. The keycloak client is setup for public access and standard flow (hence no client_secret needed, I presume). I have fiddled with the various uri settings, and also put in web origins "*" for CORS for testing.
When I try a protected url in the browser, I see:
no session found in request, redirecting for authorization {"error": "authentication session not found"}
in the proxy logs, and it redirects me to /oauth/authorize, not to https://auth.my-domain.com/auth/realms/my-realm/protocol/openid-connect/auth where I think it should redirect me.
UPDATE -- as #jan-garaj noted in comment to answer, /oauth/* shouldn't have been whitelisted. (I got that from a possibly mistaken interpretation of someone else's answer.) I then had to make the cookie not http-only, and finally hit on this issue - Keycloak-gatekeeper: 'aud' claim and 'client_id' do not match ... after that it works!

From the Louketo-proxy doc:
/oauth/authorize is authentication endpoint which will generate the OpenID redirect to the provider
So that redirect is correct. It is louketo-proxy endpoint. It is not request for your app, it will be processed by louketo-proxy. It will generate another redirect to your IDP, where user needs to login.
Off topic:
you really need confidential client and client secret for authorization code flow
web origins "*" for CORS is correct only for http protocol, explicit origin specification is needed for https

Related

What is the purpose of redirection-url in keycloak gatekeeper?

I'm trying to set up Keycloak Gatekeeper as a reverse proxy in front of my service, but I don't know what the purpose is of the redirection-url. I figured I could just put Gatekeeper in front of my service, without my service needing to know anything about the authn/z. This doesn't seem possible if it needs a callback endpoint.
I was able to deploy the Keycloak Server, Keycloak Gatekeeper, and a simple node service (all deployed locally in docker containers). With a bit of hacking to my hosts file, I was able to send a request to Gatekeeper, which properly authenticated me and redirected me back to my node service. However, my node service needed to add a /oauth/callback endpoint, since Gatekeeper adds that suffix to whatever is specified in the redirection-url.
Here is what my Gatekeeper config looks like:
discovery-url: http://keycloak-server:8080/auth/realms/master
client-id: proxy-node-server
client-secret: abcabcab-abca-abca-abca-abcabcabcabc
listen: ':3001'
enable-refresh-tokens: true
redirection-url: http://node-server:3000
encryption-key: <ENCRYPTION_KEY>
upstream-url: http://node-server:3000
resources:
- uri: /*
secure-cookie: false
I expect to be able to put Gatekeeper in front of a service that has no knowledge of authentication, authorization, OIDC, etc, but with the redirection-url that doesn't seem possible.
Is there any way to get Gatekeeper to do authnz when needed and just act as a simple proxy when the user is properly authenticated? Or is there something I don't understand where this is not possible?
Gatekeeper help:
--redirection-url value redirection url for the oauth callback url, defaults to host header is absent [$PROXY_REDIRECTION_URL]
You will need to use redirection-url, only when gatekeeper sits behind loadbalancer or another reverse proxy. That redirection-url is required for Open ID Connect flow. When user is authenticated, then redirect-url is not applied of course.

Running User Interfaces and APIs behind keycloak gatekeeper

New to keycloak, and authentication in general, so sorry for missing something obvious, and not using accurate terminology.
I'm trying to run a simple Angular UI that talks to a Java (dropwizard) API. I'd like both of those to need auth. I'm (almost) able to get them running fine behind keycloak and keycloak gatekeeper using a single realm and a confidential client. In this case gatekeeper has an upstream-url that is a traefik instance, that then routes to either the UI or API docker container. Something like:
Gatekeeper upstream-url ----> Traefik (my.domain/*) ----> UI (my.domain/ui/*)
\---> API (my.domain/api/*)
This works fine until the session times out, and when the user on the (already loaded) UI page clicks a button that tries to send an ajax request to hit the API (eg https://my.domain/api/getstuff), then Gatekeeper redirects (ie 301) that to the the keycloak login page. This redirect is a little nonsensical for an API request...
At this point both my UI and API projects are auth agnostic (ie they are not running any of the adapters etc just yet - I'm relying on the docker setup to prevent "direct" access to UI and API for now. I'll add the adapters once I need to know something about the user). I can see in https://www.keycloak.org/docs/latest/securing_apps/index.html#configuration-options the autodetect-bearer-only option which seems to describe my issue, ie
It allows you to redirect unauthenticated users of the web application to the Keycloak login page, but send an HTTP 401 status code to unauthenticated SOAP or REST clients instead as they would not understand a redirect to the login page
but seems to apply at the adapter layer, ie after gatekeeper in my scenario.
this seems similar too.
I think I want unauthenticated (eg never logged in, or timed out) access requests to https://my.domain/ui/* to be redirected to the keycloak login page, but https://my.domain/api/* to 401.
And from https://my.domain/ui/somepage the ajax request to https://my.domain/api/getstuff to use the JWT/token/cookie that the browser has from the login (which is working now).
How do I do this? What stupidly obvious step have I missed!?
Unfortunately, you cannot tell Gatekeeper to return 401(403) response codes instead of redirect. There is similar issue: https://issues.jboss.org/browse/KEYCLOAK-11082
What you can do is to remove Gatekeeper completely and implement public client authentication on frontend (JS adapter) and bearer-only client on backend (Java Adapter). If your Java application serves frontend you can implement only confidential client authentication and return 401(403) response for /api/* requests.

Protect my RESTful services by a Keycloak Oauth2 Provider Using NGINX

We have some Restful services on a certain URI and we wanted to publish our services on the web to use them in our mobile app(written in java),
Our services was on a server which cannot handle too much requests at a same time and used it's proxy_pass functionality for this,
So I used Nginx on an intermediate server to control access to our REST server,
Now we want to protect our services by Oauth2 with Password or Client Credentials(as our mobile users should not login into our servers we cannot display any login page to them),
I setup a Keycloak server which is working and I could get token for my client. I'm going to give my auth/token URI to our mobile developers to get Oauth2 token at first and use it in their requests.
The problem is I don't know how to configure Nginx to authorize incoming REST requests with provided token in request header.
Should I config Keycloak to Handle requests and forward authorized ones to NGINX?
Thanks for your help
After some tries I found this solution:
1- you have to add njs module to nginx this is necessary, you have to compile it first(so in windows it will be much trouble, I tried mingw and stopped at a dependency called expect which is not written for mingw and it wasted a lot of time from me, actually I moved our IAM to ubuntu and compiling njs and nginx in there was done in few minutes!)
2- Introspection is the key subject here, and keycloak supports it, its URI is the same as token URI plus introspect, using Basic Authorization in header and token in body
3- nginx also supports introspection after adding njs module to it, which will let nginx support js code inculde inside config file, a great exaple is NGINX Demoes- Oauth2 Introspection OSS, just copy config file and oauth2.js file and its done. I added api directive at location tag in nginx config file to let callers know it is protected.
4- create one client for nginx in keycloak to do the introspection operation, it should be in confidential mode and Service Accounts should be enabled for it.
5- nginx should forward(proxy pass) auth/token request to IAM, so a location for this should be added in config file.
6- [for ubuntu] I have an error in nginx which tells me it cannot resolve localhost! and installing Bind 9 fixes this for me(another time wasting effort was done here).
7- So any one wants to use our service should request token first and then sends its request attached with token to nginx, nginx introspects token and if token was ok and {"active": true} were received forwards request to resource and passws reply to requester.
All done.

How to make exceptions for url securing app with Keycloak?

We have an app on Wildfly Server, secured with keycloak, everything works fine, but now we have to make an access for an app, deployed on the same server. I don't want to write a "complex" code with all certificates for https and OAuth2, we use for it, but I would like to grant an access for the second app from localhost, to make request on the first one (via rest-api) and get an response. When I check with curl from terminal of the server - I get also an Unathorized-Error, so I need to do all the same operations as for public access (take Token for access from OAuth provider). How could I set up Keycloak, to grant an access from localhost, for example. I haven't found smth. equal neither in Keycloak nor in standalone-full.xml (there are no any properties for this), where I could put exclusions.
I appreciate your help!
Add localhost to Valid Redirect Uris and Web Origins of Keycloak's client or you can simply use * for development purposes so that all Uris are valid.

For a nginx server , how to protect static resources with REST login?

I have a nginx server running uwsgi and a python backend. I have a REST-API which is protected by login which returns a session cookie.
Now I can see a problem in that some resources are free to download even if not authenticated. Resources which are simply returned as static content by nginx.
I would like to protect the site so that all resources require a valid session cookie to be set.
Is there a way to hookup nginx to check with the python backend before returning the static resource?
You will most likely need the python backend to serve the file after authentication has been verified. Take a look here for question very similar to yours:
https://serverfault.com/questions/332631/how-can-i-protect-files-on-my-nginx-server