Traefik ForwardAuth with Keycloak in 5 Easy Steps

Traefik ForwardAuth with Keycloak in 5 Easy Steps

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

💡
Major thanks to Brian Turchyn for the /_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.

  1. In your realm of choice, click on clients.
  2. Click on Create client.
    2a. Leave Client type as OpenID Connect.
    2b. Enter in a client ID. Such as whoami-auth.
    2c. Enter in a friendly name like Whoami Client.
    2d. Enter in description of choice.
  3. Click Next.
  4. Enable Client authentication, and leave Standard flow and Direct access grants untouched.
  5. Click Next.
  6. Fill out Login settings information
    6a. For Home URL, enter in the IP or FQHN where your whoami application is. For example, whoami.example.org.
    6b. For Valid redirect URIs, enter in the FQHN where your whoami 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.
  7. Click Save.
  8. On the following screen, click on the Credentials tab.
  9. 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.

  1. In your Realm, click on Client scopes.
  2. Click on Create client scope.
  3. For Name, enter in groups.
  4. For Description, enter in Groups client scope.
  5. Select Default for Type.
  6. Ensure Include in token scope is checked.
  7. Click Save.
  8. After clicking on save, click on Mappers when the page refreshes.
  9. Click on Add mapper, then on By configuration.
  10. 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.

💡
The one thing that may not work for you copy-paste directly is the 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.