Purpose: Keycloak is an open source identity and access management system for modern applications and services.
- Original Reference Compose File
- Original Reference Deployment Video
- Theme Customization Documentation
Keycloak Authentication Sequence¶
sequenceDiagram
participant User
participant Traefik as Traefik Reverse Proxy
participant Keycloak
participant Services
User->>Traefik: Access service URL
Traefik->>Keycloak: Redirect to Keycloak for authentication
User->>Keycloak: Provide credentials for authentication
Keycloak->>User: Return authorization token/cookie
User->>Traefik: Send request with authorization token/cookie
Traefik->>Keycloak: Validate token/cookie
Keycloak->>Traefik: Token/cookie is valid
Traefik->>Services: Forward request to services
Services->>Traefik: Response back to Traefik
Traefik->>User: Return service response
Docker Configuration¶
version: '3.7'
services:
postgres:
image: postgres:16.2
volumes:
- /srv/containers/keycloak/db:/var/lib/postgresql/data
environment:
POSTGRES_DB: ${POSTGRES_DB}
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
healthcheck:
test: ["CMD-SHELL", "pg_isready -U keycloak"]
interval: 10s
timeout: 5s
retries: 5
networks:
keycloak_internal_network: # Network for internal communication
ipv4_address: 172.16.238.3 # Static IP for PostgreSQL in internal network
keycloak:
image: quay.io/keycloak/keycloak:23.0.6
command: start
volumes:
- /srv/containers/keycloak/themes:/opt/keycloak/themes
- /srv/containers/keycloak/base-theme:/opt/keycloak/themes/base
environment:
TZ: America/Denver # (1)
KC_PROXY_ADDRESS_FORWARDING: true # (2)
KC_HOSTNAME_STRICT: false
KC_HOSTNAME: auth.bunny-lab.io # (3)
KC_PROXY: edge # (4)
KC_HTTP_ENABLED: true
KC_DB: postgres
KC_DB_USERNAME: ${POSTGRES_USER}
KC_DB_PASSWORD: ${POSTGRES_PASSWORD}
KC_DB_URL_HOST: postgres
KC_DB_URL_PORT: 5432
KC_DB_URL_DATABASE: ${POSTGRES_DB}
KC_TRANSACTION_RECOVERY: true
KEYCLOAK_ADMIN: ${KEYCLOAK_ADMIN}
KEYCLOAK_ADMIN_PASSWORD: ${KEYCLOAK_ADMIN_PASSWORD}
KC_HEALTH_ENABLED: true
DB_POOL_MAX_SIZE: 20 # (5)
DB_POOL_MIN_SIZE: 5 # (6)
DB_POOL_ACQUISITION_TIMEOUT: 30 # (7)
DB_POOL_IDLE_TIMEOUT: 300 # (8)
JDBC_PARAMS: "connectTimeout=30"
KC_HOSTNAME_DEBUG: false # (9)
ports:
- 8080:8080
restart: always
depends_on:
postgres:
condition: service_healthy
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/auth"] # Health check for Keycloak
interval: 30s # Health check interval
timeout: 10s # Health check timeout
retries: 3 # Health check retries
networks:
docker_network:
ipv4_address: 192.168.5.2
keycloak_internal_network: # Network for internal communication
ipv4_address: 172.16.238.2 # Static IP for Keycloak in internal network
networks:
default:
external:
name: docker_network
docker_network:
external: true
keycloak_internal_network: # Internal network for private communication
driver: bridge # Network driver
ipam: # IP address management
config:
- subnet: 172.16.238.0/24 # Subnet for internal network
- This sets the timezone of the Keycloak server to your timezone. This is not really necessary according to the official documentation, however I just like to add it to all of my containers as a baseline environment variable to add
- This assumes you are running Keycloak behind a reverse proxy, in my particular case, Traefik
- Set this to the FQDN that you are expecting to reach the Keycloak server at behind your reverse proxy
- This assumes you are running Keycloak behind a reverse proxy, in my particular case, Traefik
- Maximum connections in the database pool
- Minimum idle connections in the database pool
- Timeout for acquiring a connection from the database pool
- Timeout for closing idle connections to the database
- If this is enabled, Navigate to https://auth.bunny-lab.io/realms/master/hostname-debug to troubleshoot issues with the deployment if you experience any issues logging into the web portal or admin UI
POSTGRES_DB=keycloak
POSTGRES_USER=keycloak
POSTGRES_PASSWORD=SomethingSecure # (1)
KEYCLOAK_ADMIN=admin
KEYCLOAK_ADMIN_PASSWORD=SomethingSuperSecureToLoginAsAdmin # (2)
- This is used internally by Keycloak to interact with the PostgreSQL database server
- This is used to log into the web admin portal at https://auth.bunny-lab.io
Traefik Reverse Proxy Configuration¶
If the container does not run on the same host as Traefik, you will need to manually add configuration to Traefik's dynamic config file, outlined below.
http:
routers:
auth:
entryPoints:
- websecure
tls:
certResolver: letsencrypt
service: auth
rule: Host(`auth.bunny-lab.io`)
middlewares:
- auth-headers
services:
auth:
loadBalancer:
servers:
- url: http://192.168.5.2:8080
passHostHeader: true
middlewares:
auth-headers:
headers:
sslRedirect: true
stsSeconds: 31536000
stsIncludeSubdomains: true
stsPreload: true
forceSTSHeader: true
customRequestHeaders:
X-Forwarded-Proto: https
X-Forwarded-Port: "443"
Traefik Keycloak Middleware¶
At this point, we need to add the official Keycloak plugin to Traefik's main configuration. In this example, it will be assumed you need to configure this in Portainer/Docker Compose, and not via a static yml/toml file. Assume you follow the Docker Compose based Traefik Deployment.
Install Keycloak Plugin¶
If you do not already have the following added to the end of your command:
section of the docker-compose.yml file in Portainer, go ahead and add it:
# Keycloak plugin configuration
- "--experimental.plugins.keycloakopenid.moduleName=github.com/Gwojda/keycloakopenid"
- "--experimental.plugins.keycloakopenid.version=v0.1.34"
Add Middleware to Traefik Dynamic Configuration¶
You will want to ensure the following exists in the dynamically-loaded config file folder, you can name the file whatever you want, but it will be a one-all middleware for any services you want to have communicating as a specific OAuth2 Client ID
. For example, you might want to have some services exist in a particular realm of Keycloak, or to have different client rules apply to certain services. If this is the case, you can create multiple middlewares in this single yaml file, each handling a different service / realm. It can get pretty complicated if you want to handle a multi-tenant environment, such as one seen in an enterprise environment.
http:
middlewares:
auth-bunny-lab-io:
plugin:
keycloakopenid:
KeycloakURL: "https://auth.bunny-lab.io" # <- Also supports complete URL, e.g. https://my-keycloak-url.com/auth
ClientID: "traefik-reverse-proxy"
ClientSecret: "https://auth.bunny-lab.io > Clients > traefik-reverse-proxy > Credentials > Client Secret"
KeycloakRealm: "master"
Scope: "openid profile email"
TokenCookieName: "AUTH_TOKEN"
UseAuthHeader: "false"
# IgnorePathPrefixes: "/api,/favicon.ico [comma deliminated] (optional)"
Configure Valid Redirect URLs¶
At this point, within Keycloak, you need to configure domains that you are allowed to visit after authenticating. You can do this with wildcards, but generally you navigate to "https://auth.bunny-lab.io > Clients > traefik-reverse-proxy > Valid redirect URIs" A simple example is adding https://tools.bunny-lab.io/*
to the list of valid redirect URLs. If the site is not in this list, even if it has the middleware configured in Traefik, it will fail to authenticate and not let the user proceed to the website being protected behind Keycloak.
Adding Middleware to Dynamic Traefik Service Config Files¶
At this point, you are in the final stretch, you just need to add the middleware to the Traefik dynamic config files to ensure that it routes the traffic to Keycloak when someone attempts to access that service. Put the following middleware section under the routers:
section of the config file.
A full example config file would look like the following:
http:
routers:
example:
entryPoints:
- websecure
tls:
certResolver: letsencrypt
service: example
rule: Host(`example.bunny-lab.io`)
middlewares:
- auth-bunny-lab-io # Referencing the Keycloak Server Traefik Middleware
services:
example:
loadBalancer:
servers:
- url: http://192.168.5.16:80
passHostHeader: true