Traefik and Keycloak - clients, scopes, oh my

For my first attempt at authorization I tried using Authelia. That worked out ok, but the limited ability of managing users was rudimentary file based.

I am not trying Keycloak which if much more functional.

I created my docker compose files with Traefik and Keycloak and I can access each dashboard. I am also using Lets Encrypt as a TLS.

So far, so good...

version: '3.3'
networks:
  transit_idp:
    driver: bridge
  transit_whoami:
    driver: bridge
    
services:
  #################### Traefik Service ####################
  traefik:
    image: 'traefik:v2.9.6'
    container_name: traefik
    volumes:
      - '/var/lib/docker/volumes/traefik/_data:/etc/traefik'
      - '/var/lib/docker/volumes/traefik/_logs:/var/log'
      - '/var/run/docker.sock:/var/run/docker.sock'
    environment:
      - TZ="America/New_York"
    networks:
      - transit_idp
      - transit_whoami
    labels:
      - traefik.enable=true
      - traefik.http.routers.api.rule=Host(`traefik.mydomain.org`)
      - traefik.http.middlewares.testheader.headers.contentSecurityPolicy=upgrade-insecure-requests
      - traefik.http.routers.api.entrypoints=https
      - traefik.http.routers.api.service=api@internal
      - traefik.http.routers.api.tls=true
      - traefik.http.routers.api.tls.certresolver=letsencrypt
    ports:
      - '80:80'
      - '443:443'
    command:
      - '--api.insecure=true'
      - '--providers.docker=true'
      - '--providers.docker.exposedByDefault=false'
      - '--providers.file.directory=/etc/traefik'
      - '--providers.file.watch=true'
      - '--entrypoints.http=true'
      - '--entrypoints.http.address=:80'
      - '--entrypoints.http.http.redirections.entrypoint.to=https'
      - '--entrypoints.http.http.redirections.entrypoint.scheme=https'
      - '--entrypoints.https=true'
      - '--entrypoints.https.address=:443'
      - '--certificatesResolvers.letsencrypt.acme.email=me@outlook.com'
      - '--certificatesResolvers.letsencrypt.acme.storage=/etc/traefik/acme.json'
      - '--certificatesResolvers.letsencrypt.acme.httpChallenge.entryPoint=http'
      - '--log=true'
      - '--log.filePath=/var/log/traefik/traefik.log'
      - '--log.level=DEBUG'
      
  #################### Keycloak Services ####################
  keycloak_db:
    image: postgres:11.2-alpine
    container_name: keycloak_db
    environment:
        - POSTGRES_DB=keycloak
        - POSTGRES_USER=keycloak
        - POSTGRES_PASSWORD=change-me-postgres
        - POSTGRES_ROOT_PASSWORD=change-me-rootpw
    networks:
      transit_idp:
    volumes:
      - /var/lib/docker/volumes/postgresql/_data:/var/lib/postgresql/data
    labels:
      - "traefik.enable=false"

  keycloak:
    image: jboss/keycloak:latest
    container_name: keycloak
    hostname: keycloak
    environment:
      - DB_VENDOR=POSTGRES
      - DB_ADDR=keycloak_db
      - DB_DATABASE=keycloak
      - DB_PORT=5432
      - DB_USER=keycloak
      - DB_SCHEMA=public
      - DB_PASSWORD=change-me-postgres
      - PROXY_ADDRESS_FORWARDING=true
      - KEYCLOAK_LOGLEVEL=INFO
      - KEYCLOAK_USER=admin
      - KEYCLOAK_PASSWORD=change-me-keycloak
    networks:
      transit_idp:
    labels:
      - traefik.enable=true
      - traefik.http.routers.keycloak.rule=Host(`auth.mydomain.org`)
      - traefik.http.routers.keycloak.entrypoints=https
      - traefik.http.routers.keycloak.tls=true
      - traefik.http.routers.keycloak.tls.certresolver=letsencrypt
    command: ["-b", "0.0.0.0", "-Dkeycloak.profile.feature.docker=enabled"]

Then I read about using the forward auth middleware and I created that middleware.

  forwardauth:
    image: mesosphere/traefik-forward-auth
    container_name: forward-auth
    depends_on:
      - keycloak_db
      - keycloak
    networks:
      - transit_idp
    environment:
      - SECRET=xxx
      - PROVIDER_URI=https://auth.mydomain.org/auth/realms/MyRealm
      - CLIENT_ID=bi-auth
      - CLIENT_SECRET=xxx
    labels:
      - "traefik.enable=true"
      - "traefik.docker.network=transit_idp"
      - "traefik.http.services.forwardauth.loadbalancer.server.port=4181"
      - "traefik.http.routers.forwardauth.entrypoints=https"
      - "traefik.http.routers.forwardauth.rule=Path(`/_oauth`)"
      - "traefik.http.routers.forwardauth.middlewares=traefik-forward-auth"
      - "traefik.http.middlewares.traefik-forward-auth.forwardauth.address=http://forwardauth:4181"
      - "traefik.http.middlewares.traefik-forward-auth.forwardauth.authResponseHeaders=X-Forwarded-User"
      - "traefik.http.middlewares.traefik-forward-auth.forwardauth.trustForwardHeader=true"
#      - traefik.http.routers.keycloak.tls=true
#      - traefik.http.routers.keycloak.tls.certresolver=letsencrypt

I am using a dummy whoami container and added the forward auth middleware to it.

  #################### Dummy WhoAmI Service ####################
  secure:
    image: traefik/whoami
    container_name: secure
    networks:
      - transit_whoami
    labels:
      - traefik.enable=true
      - traefik.http.routers.secure.rule=Host(`secure.mydomain.org`)
      - traefik.http.routers.secure.entrypoints=https
      - traefik.http.routers.secure.tls=true
      - traefik.http.routers.secure.tls.certresolver=letsencrypt
      - traefik.http.routers.secure.middlewares=traefik-forward-auth
    expose:
      - 80
    restart: unless-stopped

So now when I try to access my whoami, I get a bad gateway error and the url changes to indicate that there are invalid scope errors.

I think I am really more into a Keycloak discussion now rather than a traefik discussion but the two topics are very interwoven.

Has anyone done this lately and willing to help a noob understand?

In keycloak, I have my realm, I created my client. Never really understood the scopes issue and how they relate to authorizing users to access the service. I know I have research to do there.

I'd be very grateful for advice on where to look.
Thanks

My first step of success was getting access to the traefik and keycloak dashboards.

Then I had access to the unprotected whoami server.

Then I added the forward auth middleware to the whoami and got scope errors coming in the displayed url and a bad gateway page content.

I think my error is in my realm client configuration.

I'm missing how to connect my Keycloak users to be authenitcated to the realm client.

When I create a regular user, I can log into keycloak as that regular user but that regular user doesn't show any applications they have access to.

Applications? Scopes? Roles? Clients?

Argh... I'm seeing hundreds of blogs and tutorials on setting up the client in keycloak and configuring the client with the secrets etc but nothing about how to create users and enable them to access the client application.

Making some progress...

I read that the scope "groups" is required but not present by default in keycloak so one would need to create a client scope called "groups" (ref Traefik ForwardAuth with Keycloak in 5 Easy Steps )

That gets me to where now any user in the realm is authorized to view my whoami web service when authenticated. Great, but now I want to use some attribute in keycloak to limit who is authorized. Like only members of group x.

This is a Traefik forum, so for specific keycloak configuration questions you probably get better answers at a dedicated keycloak forum.

Please be aware that keycloak is an enterprise tool, so configuration is complicated by default :wink:

Maybe authentik has more features then authelia, but is easier to handle than keycloak.

As another option: Instead of using a forwardauth middlware you could use oauth2-proxy and chain it between Traefik and the target service. This works well for me and you can easily configure roles that may get access to the service.

But I really really would avoid KeyCloak if I had any choice. Yes, it works. But it's complex and new major releases come out very often which may contain breaking changes. Not keeping Keycloak up-to-date is no good Idea because of potential security problems. Keycloak costs me a lot of time and nerves.