Configuring Keycloak in 2022

Guide

1. Set up docker-compose.yml

version: '3'

volumes:
  postgres_data:
      driver: local

services:
  postgres:
      image: postgres:latest
      networks:
        - db
      volumes:
        - postgres_data:/var/lib/postgresql/data
      environment:
        POSTGRES_DB: keycloak
        POSTGRES_USER: keycloak
        POSTGRES_PASSWORD: <postgres_password>
  keycloak:
      image: jboss/keycloak:latest
      deploy:
        restart_policy:
          condition: always
          delay: 5s
          window: 120s
      networks:
        - web
        - db
      environment:
        DB_VENDOR: POSTGRES
        DB_ADDR: postgres
        DB_DATABASE: keycloak
        DB_USER: keycloak
        DB_SCHEMA: public
        DB_PASSWORD: <postgres_password>
        KEYCLOAK_USER: admin
        KEYCLOAK_PASSWORD: <keycloak_password>
        KC_HOSTNAME: <idp-hostname>
        PROXY_ADDRESS_FORWARDING: true
        KEYCLOAK_FRONTEND_URL: https://<idp-hostname>/auth
      depends_on:
        - postgres
  web:
    image: nginx
    networks:
      - web
    depends_on:
      - keycloak
    volumes:
     - ./web/nginx.conf:/etc/nginx/nginx.conf
     - ./certbot/www:/var/www/certbot/:ro
     - ./certbot/conf/:/etc/nginx/ssl/:ro
    ports:
     - "80:80"
     - "443:443"
    environment:
     - NGINX_PORT=80
  certbot:
    image: certbot/certbot:latest
    volumes:
     - ./certbot/www/:/var/www/certbot/:rw
     - ./certbot/conf/:/etc/letsencrypt/:rw

networks:
  web: {}
  db: {}

Replace <idp-hostname>, <keycloak-password> and <postgres_password>. Preferably store them securely.

2. Run initial nginx server

This step is needed in order to generate Let’s encrypt certificate with certbot before running actual Keycloak server.

Place this file in web/nginx.conf (relative to your docker-compose.yml file).

worker_processes  5;  ## Default: 1
worker_rlimit_nofile 8192;

events {
  worker_connections  4096;  ## Default: 1024
}

http {
  index    index.html index.htm index.php;
  default_type application/octet-stream;
  log_format   main '$remote_addr - $remote_user [$time_local]  $status '
    '"$request" $body_bytes_sent "$http_referer" '
    '"$http_user_agent" "$http_x_forwarded_for"';
  sendfile     on;
  tcp_nopush   on;
  server_names_hash_bucket_size 128; # this seems to be required for some vhosts

  server { # Required for certbot
    server_name idp;
    listen       80;

    location /.well-known/acme-challenge/ {
        root /var/www/certbot;
    }

  }
}

3. Run certbot

docker-compose up -d web
docker-compose run --rm  certbot certonly --webroot --webroot-path /var/www/certbot/ -d <idp-hostname>

4. Configure Nginx

Now add Keycloak behind Nginx. Add this to your web/nginx.conf file. Again, replace <idp-hostname> with your own.

...

server {
    listen 443 default_server ssl http2;
    listen [::]:443 ssl http2;

    server_name <idp-hostname>;

    ssl_certificate /etc/nginx/ssl/live/<idp-hostname>/fullchain.pem;
    ssl_certificate_key /etc/nginx/ssl/live/<idp-hostname>/privkey.pem;
    location /auth/admin/ {
      allow 127.0.0.1; # Only allow localhost to access admin, or change this to your IP-address
      deny all;
      proxy_pass      http://keycloak:8080/auth/admin/;
      proxy_set_header Host $host;
      proxy_set_header X-Real-IP $remote_addr;
    }
    location /auth/js/ {
      proxy_pass      http://keycloak:8080/auth/js/;
      proxy_set_header Host $host;
      proxy_set_header X-Real-IP $remote_addr;
    }
    location /auth/realms/ {
      proxy_pass      http://keycloak:8080/auth/realms/;
      proxy_set_header Host $host;
      proxy_set_header X-Real-IP $remote_addr;
      proxy_buffer_size          128k;
      proxy_buffers              4 256k;
      proxy_busy_buffers_size    256k;
    }
    location /auth/resources/ {
      proxy_pass      http://keycloak:8080/auth/resources/;
      proxy_set_header Host $host;
      proxy_set_header X-Real-IP $remote_addr;
    }
    location /auth/robots.txt {
      proxy_pass      http://keycloak:8080/auth/robots.txt;
      proxy_set_header Host $host;
      proxy_set_header X-Real-IP $remote_addr;
    }
}

5. Start everything

docker-compose restart web
docker-compose up -d postgres keycloak

Now you should be able to access Keycloak at https://<idp-hostname>/auth


Author | Markus Lehtonen

InfoSec consultant and defensive security architect.