Keycloak Docker setup and reverse proxy from nginx

Keycloak is an open source Identity and Access Management software that is part of Red Hat project. It provided OAuth and SSO support for your application and software. It is easy to set up, but you need to download the dependency and set up in the configuration file. We can use Docker to set this up easily.

From github repository, we can find the information about Keycloak for Docker. One of the simple setups is docker run -e KEYCLOAK_USER=<USERNAME> -e KEYCLOAK_PASSWORD=<PASSWORD> jboss/keycloak. Then it is going up and running. We can have a database connection when we set DB_VENDOR environment variable. There is a sample setup for Keycloak with MySQL. It is pretty easy with docker.

However, I do not need multiple MySQL instances running. I prefer a single MySQL service at my localhost. So, we need to connect host MySQL within the container. We have to locate the host IP address from the container. Docker used to provide host.docker.internal to resolve the host IP address. However, it resolves to nothing in Ubuntu docker. I found a simple workaround from here.

SETUP

Basically, it set the network mode to host, so localhost can access the actual machine. so I set it with docker-compose with network_mode: host. So the sample compose setup will look like this. With the file, we can set the database clear. The file is generated with jhipster, and I modify it to connect it to the database.

version: '2'
services:
  keycloak:
    image: jboss/keycloak:5.0.0
    network_mode: host
    command: ["-b", "0.0.0.0", "-Dkeycloak.migration.action=import", "-Dkeycloak.migration.provider=dir", "-Dkeycloak.migration.dir=/opt/jboss/keycloak/realm-config", "-Dkeycloak.migration.strategy=OVERWRITE_EXISTING", "-Djboss.socket.binding.port-offset=1000"]
    volumes:
      - ./realm-config:/opt/jboss/keycloak/realm-config
    environment:
      - PROXY_ADDRESS_FORWARDING=true
      - KEYCLOAK_USER=admin
      - KEYCLOAK_PASSWORD=admin
      - DB_VENDOR=mariadb
      - DB_DATABASE=keycloak
      - DB_USER=keycloak
      - DB_PASSWORD=passwd
    ports:
      - 9080:9080
      - 9443:9443
      - 10990:10990

It is important to add PROXY_ADDRESS_FORWARDING=true since we want to working with reverse proxy nginx.

We can then run the file with docker-compose -f keycloak.yml up. However, it does not work because of the message Failed to start service org.wildfly.network.interface.private. There is nothing I can find on the internet, but it works fine on my other computer. It should be a problem with docker in Ubuntu. So, I have used another way. Please comment if you anything related to the issue.

MYSQL

We have to know the IP address of the host in the container. So, I found this command ip route show | awk '/default/ {print $3}'. It will show the host IP in the docker. We can simply do it like docker run --rm alpine ip route show | awk '/default/ {print $3}', and we will see the IP in terminal. For me, the IP is 172.17.0.1. So I add DB_ADDR=172.17.0.1 into the environment. And let myself listen to IP address 0.0.0.0 then the container is allowed to connect to the service.

One more thing is the MySQL user. We can add a user with remote access, but it is not safe. So I add the user in MySQL with SQL command.

CREATE USER 'keycloak'@'172.*' IDENTIFIED BY 'password';
GRANT ALL PRIVILEGES ON `keycloak`.* TO 'keycloak'@'172.*';
FLUSH PRIVILEGES;

or we can simply do GRANT ALL PRIVILEGES ON keycloak.* To 'keycloak'@'172.*' IDENTIFIED BY 'password';. They both work well with mariadb-10.3.14.

NGINX

We want to proxy it with nginx so we do not need to convert certificate. It is easier to replace and do not stop the keycloak service. So, I obtain a certificate with certbot and activate it with Nginx configuration like this.

nginx host configuration

server {
    listen 80;
    listen [::]:80;
    server_name example.com;

    location / {
        rewrite ^ https://example.com$request_uri? permanent;
    }
}

server {
    server_name example.com; # managed by Certbot

    access_log /var/log/nginx/ide.mai1015.com-access.log;
    error_log /var/log/nginx/ide.mai1015.com-error.log;

    # SSL configuration

    listen 443 ssl http2;
    # listen [::]:443 ssl http2 ipv6only=on;

    # Note: You should disable gzip for SSL traffic.
    # See: https://bugs.debian.org/773332
    #
    # Read up on ssl_ciphers to ensure a secure configuration.
    # See: https://bugs.debian.org/765782
    #
    # Self signed certs generated by the ssl-cert package
    # Don't use them in a production server!
    #
    # include snippets/snakeoil.conf;

    # Add index.php to the list if you are using PHP
    index index.html index.htm index.nginx-debian.html;

    location / {
        proxy_pass http://localhost:9080;
        proxy_read_timeout 90;

        # proxy header
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Scheme $scheme;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Host $host;

        # ws
        proxy_http_version 1.1;
        #proxy_set_header Upgrade $http_upgrade;
        #proxy_set_header Connection 'Upgrade';

        # proxy_cache_bypass $http_upgrade;

        #proxy_redirect http://localhost:9080 https://example.com;
    }

    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; # managed by Certbot
    # include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    # ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}

So those configurations are important to allow keycloak to work correctly. Without Host header will break the redirect_url.

    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Scheme $scheme;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header Host $host;

So we have a fully configurated keycloak working behind reverse proxy Nginx.