Traefik ForwardAuth with Keycloak in 5 Easy Steps
/_oauth
path to your list of valid redirect URIs.This guide assumes that you have Keycloak already installed with a realm ready to go! In this tutorial we'll be securing traefik/whoami
with Keycloak and mesosphere/traefik-forward-auth
. In this example, Keycloak is acting as an Identity Provider (IdP) and whoami
is acting as a Service Provider (SP).
This tutorial uses the following:
- Keycloak v21 or greater (21.0.1 is used in this tutorial)
- Traefik v2
- whoami mini container provided by Traefik.
whoami
is secured by Traefik in this example in a subdomain, whoami.example.org
.Step 1: Prepare your OpenID Connect Client in Keycloak
/_oauth
portion below! That's what had stumped me for a while.We'll be creating an OpenID Connect client to secure the whoami
web application.
- In your realm of choice, click on clients.
- Click on Create client.
2a. Leave Client type as OpenID Connect.
2b. Enter in a client ID. Such aswhoami-auth
.
2c. Enter in a friendly name likeWhoami Client
.
2d. Enter in description of choice. - Click Next.
- Enable Client authentication, and leave Standard flow and Direct access grants untouched.
- Click Next.
- Fill out Login settings information
6a. For Home URL, enter in the IP or FQHN where yourwhoami
application is. For example,whoami.example.org
.
6b. For Valid redirect URIs, enter in the FQHN where yourwhoami
application is, including the protocol. Append/_oauth
to the end of it. For example,https://whoami.example.org/_oauth
. In the next steps, you'll configure traefik-forward-auth to listen to Paths ending on/_oauth
. - Click Save.
- On the following screen, click on the Credentials tab.
- Copy the Client secret and put it aside. You'll need to provide this to traefik-forward-auth in a .env file or within a compose file.
Step 2: Create a new Client Scope
The groups
scope does not seem to come default in Keycloak 21.0.1. You may need to create a new client scope, then assign this scope to your OpenID Connect Client.
- In your Realm, click on Client scopes.
- Click on Create client scope.
- For Name, enter in
groups
. - For Description, enter in
Groups client scope
. - Select
Default
for Type. - Ensure
Include in token scope
is checked. - Click Save.
- After clicking on save, click on Mappers when the page refreshes.
- Click on Add mapper, then on By configuration.
- In the pop-up box, select Group Membership. This will let you map group membership to the token claim
groups
that we just created.
Step 3: Prepare your docker-compose file
Prepare your docker-compose file. This contains Traefik, whoami, and the forward authentication app services in one file.
websecure
entrypoint. This is where you define your TLS termination, middlewares, and etc. for HTTPs in your Traefik config.version: "3"
services:
traefik:
container_name: traefik
restart: unless-stopped
image: 'traefik:latest'
ports:
- '80:80'
- '8080:8080'
- '443:443'
volumes:
- './traefik.yml:/etc/traefik/traefik.yml'
- './configurations:/configurations'
- './letsencrypt:/letsencrypt'
- '/var/run/docker.sock:/var/run/docker.sock:ro'
networks:
- proxy
whoami:
image: "traefik/whoami"
container_name: "simple-service"
labels:
- "traefik.enable=true"
- "traefik.docker.network=proxy"
- "traefik.http.routers.whoami.rule=Host(`whoami.example.foo`)"
- "traefik.http.routers.whoami.entrypoints=websecure"
- "traefik.http.routers.whoami.middlewares=traefik-auth@file"
networks:
- proxy
traefik-forward-auth:
image: "mesosphere/traefik-forward-auth:3.1.0"
container_name: "traefik-forward-auth"
environment:
#LOG_LEVEL: "debug"
CLIENT_ID: "<your client ID from Keycloak here>"
CLIENT_SECRET: "<your client secret from Keycloak"
ENCRYPTION_KEY: "<32 character string of numbers and letters>"
PROVIDER_URI: "<realm URL e.g. https://idam.example.org/realms/yourRealm>"
SECRET: "<random string of numbers and letters here too>"
AUTH_HOST: "keycloak"
COOKIE_DOMAIN: "example.foo"
networks:
- proxy
depends_on:
- traefik
labels:
- "traefik.port=4181"
- "traefik.docker.network=proxy"
- "traefik.http.routers.traefik-forward-auth.entrypoints=websecure"
- "traefik.http.routers.traefik-forward-auth.middlewares=traefik-auth@file"
- "traefik.http.routers.traefik-forward-auth.rule=Path(`/_oauth`)"
Step 4: Create forwardAuth middleware
In your Traefik dynamic configuration, define a new middleware piece named traefik-auth
that will handle forwardAuth
components.
http:
middlewares:
traefik-auth:
forwardAuth:
address: "http://traefik-forward-auth:4181"
trustForwardHeader: true
authResponseHeaders:
- "X-Forwarded-For"
Step 5: Start up the services
This is the time to either restart your containers or start them, depending on your setup.
A short recap:
In this tutorial, we walked through the setup of an OpenID Connect client, traefik-forward-auth, and Traefik middleware. By defining and assigning traefik-auth
middleware to the whoami
application, that tells Traefik to first send us over to traefik-forward-auth
. This piece of middleware will then redirect us to Keycloak's SSO page for us to authenticate against. Once that is done, we're given (for simplicity's sake) a cookie with all of the user info and sent back to traefik-forward-auth
. Because traefik-forward-auth
knew where we were referred from originally, we are then finally redirected back to whoami
. You should see this Cookie pop up now:
You may be asking "where's the authorization piece of all this?" In the case of SSO, it will be up to the application to parse through the user information and make authorization decisions based on authentication (though you can really twist things in a different way to get Traefik/Keycloak to do that for you, but I won't cover that here).
Securing other applications
To secure other resources, assign your traefik-auth
middleware as a label.
- "traefik.http.routers.traefik-forward-auth.middlewares=traefik-auth@file"
Troubleshooting
Bad Gateway
traefik-forward-auth
is expecting the following scopes: email
, groups
, and profile
. For Keycloak v21 and above, the groups
scope does not come pre-enabled as part of your OpenID Connect client, and you may need to create a Client Scope at the top level of the realm, and then add the subsequent scope to your client. Your groups
attribute should not be a part of the "dedicated" scope and mappers in Keycloak configuration.
You can debug this by navigating to your OpenID Client in the Keycloak administration console, clicking on Client scopes in the tab menu, then on Evaluate in the sub tabs. Select the user you want to debug, then on the right side, click on Generated access token. Scroll to where you'll see scopes in the resulting JSON, and you should see something like "scope": "openid groups profile email"
. If groups
, profile
, or email
is missing, then you need to make sure those scopes are not optional
, and/or are defined and assigned to the client itself and not as a sub-scope.