How to configure Keycloak 18 running http only in production mode?

Viewed 2975

I have the situation that I run Keycloak 18.0.0 behind a reverse proxy that is terminating the secure (https) connection. Therefore I would like to build a Docker image that configures Keycloak to only listen on http (e.g. port 8080).

I have adjusted the Dockerfile from the Keycloak documentation. It uses

  • MariaDB as database
  • Enables Health and Metrics Endpoints
  • Since I'm terminating the TLS connection in the reverse proxy I additionally make use of the --proxy=edge option, when starting Keycloak (see Proxy Mode).
FROM quay.io/keycloak/keycloak:18.0.0 as builder

ENV KC_HEALTH_ENABLED=true
ENV KC_METRICS_ENABLED=true
ENV KC_DB=mariadb
# Install custom providers
RUN curl -sL https://github.com/aerogear/keycloak-metrics-spi/releases/download/2.5.3/keycloak-metrics-spi-2.5.3.jar -o /opt/keycloak/providers/keycloak-metrics-spi-2.5.3.jar
RUN /opt/keycloak/bin/kc.sh build

FROM quay.io/keycloak/keycloak:18.0.0
COPY --from=builder /opt/keycloak/ /opt/keycloak/
WORKDIR /opt/keycloak
ENTRYPOINT ["/opt/keycloak/bin/kc.sh", "start"]

I then build my Docker image using

docker build --no-cache . -t ghcr.io/saw303/zscsupporter-be/keycloak-18.0.0:0.0.1

and start the image within my Docker Composition.

version: "3.9"
services:
  proxy:
    image: caddy:2.5.1-alpine
    ports:
      - "${PROXY_IP}:80:80"
      - "${PROXY_IP}:443:443"
    volumes:
      - ${BASE_PATH:-.}/docker-volume/caddy/Caddyfile:/etc/caddy/Caddyfile:Z
      - ${BASE_PATH:-.}/docker-volume/caddy/caddy_data:/data:Z
      - ${BASE_PATH:-.}/docker-volume/caddy/caddy_config:/config:Z
  
  keycloak:
    image: ghcr.io/saw303/zscsupporter-be/keycloak-18.0.0:0.0.1
    ports:
      - "127.0.0.1:9001:8080"
      - "127.0.0.1:9443:8443"
    environment:
      KC_HOSTNAME: localhost
      KC_HOSTNAME_PORT: 80
      KC_HOSTNAME_STRICT_BACKCHANNEL: true
      KEYCLOAK_ADMIN: admin
      KEYCLOAK_ADMIN_PASSWORD: admin
      KC_DB_URL: jdbc:mariadb://keycloakdb:3306/keycloak
      KC_DB_USERNAME: keycloak
      KC_DB_PASSWORD: secret
      KC_LOG_LEVEL: info
      KC_PROXY: edge

  keycloakdb:
    image: mariadb:10.7.3-focal
    environment:
      MYSQL_ROOT_PASSWORD: root_secret
      MYSQL_DATABASE: keycloak
      MYSQL_USER: keycloak
      MYSQL_PASSWORD: secret
      TZ: "Europe/Zurich"
    tmpfs:
      - /var/lib/mysql:rw
    ports:
      - "127.0.0.1:3307:3306"

When I then start the Docker composition using

docker compose up -d && docker compose logs -f keycloak

I get the following log statement that clearly states that it uses Strict HTTPS.

backend-keycloak-1  | 2022-05-29 17:37:46,922 INFO  [org.keycloak.quarkus.runtime.hostname.DefaultHostnameProvider] (main) Hostname settings: FrontEnd: localhost, Strict HTTPS: true, Path: <request>, Strict BackChannel: false, Admin: localhost, Port: 8,080, Proxied: true

And when I access the administration console

http://localhost:9001/admin/master/console/

I get a blank page and the admin console sends only one request to

http://localhost:9001/admin/master/console/config

which returns wrong URLs https:.

{
  "realm": "master",
  "auth-server-url": "https://localhost:8080/",
  "ssl-required": "external",
  "resource": "security-admin-console",
  "public-client": true,
  "confidential-port": 0
}

Is there a way to change the protocol in field value auth-server-url from https to http?

Update 1: Proxy configuration

I use Caddy Server 2.0 as a reverse proxy. In this test setup caddy is part of the Docker composition and simply redirects all request on port 80 to the keycloak container on port 8080.

{
  admin off
}

localhost:80

reverse_proxy /* keycloak:8080

log

Please note that the behaviour is the same whether I access the Keycloak admin console through the reverse proxy localhost:80 or directly localhost:9001. When accessing it through the proxy I, of course change the port value KC_HOSTNAME_PORT: 80.

Update 2: Set KC_PROXY in docker-compose.yml

The KC_PROXY env variable is now set in the docker-compose.yml but the behaviour is the same. When I jump into the running keycloak container I see the following active configuration


āžœ  docker compose exec keycloak bash
WARN[0000] The "PROXY_IP" variable is not set. Defaulting to a blank string. 
WARN[0000] The "PROXY_IP" variable is not set. Defaulting to a blank string. 
bash-4.4$ bin/kc.sh show-config
Current Mode: none
Runtime Configuration:
        kc.cache =  ispn (PersistedConfigSource)
        kc.config.args =  show-config (SysPropConfigSource)
        kc.db =  mariadb (PersistedConfigSource)
        kc.db.password =  secret (EnvConfigSource)
        kc.db.url =  jdbc:mariadb://keycloakdb:3306/keycloak (EnvConfigSource)
        kc.db.username =  keycloak (EnvConfigSource)
        kc.health-enabled =  true (PersistedConfigSource)
        kc.home.dir =  /opt/keycloak/bin/../ (SysPropConfigSource)
        kc.hostname =  localhost (EnvConfigSource)
        kc.hostname.port =  80 (EnvConfigSource)
        kc.hostname.strict.backchannel =  true (EnvConfigSource)
        kc.http-enabled =  false (PropertiesConfigSource[source=jar:file:///opt/keycloak/lib/lib/main/org.keycloak.keycloak-quarkus-server-18.0.0.jar!/META-INF/keycloak.conf])
        kc.http-relative-path =  / (PersistedConfigSource)
        kc.log-console-output =  default (PropertiesConfigSource[source=jar:file:///opt/keycloak/lib/lib/main/org.keycloak.keycloak-quarkus-server-18.0.0.jar!/META-INF/keycloak.conf])
        kc.log-file =  /opt/keycloak/bin/../data/log/keycloak.log (PropertiesConfigSource[source=jar:file:///opt/keycloak/lib/lib/main/org.keycloak.keycloak-quarkus-server-18.0.0.jar!/META-INF/keycloak.conf])
        kc.log.level =  info (EnvConfigSource)
        kc.metrics-enabled =  true (PersistedConfigSource)
        kc.provider.file.keycloak-metrics-spi-2.5.3.jar.last-modified =  1653899384311 (PersistedConfigSource)
        kc.proxy =  edge (EnvConfigSource)
        kc.quarkus-properties-enabled =  false (PersistedConfigSource)
        kc.show.config =  none (SysPropConfigSource)
        kc.version =  18.0.0 (SysPropConfigSource)

The interesting part is that it indicates that http is disabled.

kc.http-enabled =  false (PropertiesConfigSource[source=jar:file:///opt/keycloak/lib/lib/main/org.keycloak.keycloak-quarkus-server-18.0.0.jar!/META-INF/keycloak.conf])
2 Answers

I managed to get it up and running on localhost by configuring the reverse proxy to use a self-signed certificate.

{
  admin off
}

localhost:443 {
        reverse_proxy keycloak:8080
        tls internal
}

log

The magic part with Caddy is to declare tls internal.

After that I changed my Dockerfile to this

FROM quay.io/keycloak/keycloak:18.0.0 as builder

ENV KC_HEALTH_ENABLED=true
ENV KC_METRICS_ENABLED=true
ENV KC_FEATURES=token-exchange
ENV KC_DB=mariadb
RUN curl -sL https://github.com/aerogear/keycloak-metrics-spi/releases/download/2.5.3/keycloak-metrics-spi-2.5.3.jar -o /opt/keycloak/providers/keycloak-metrics-spi-2.5.3.jar
RUN /opt/keycloak/bin/kc.sh build

FROM quay.io/keycloak/keycloak:18.0.0
COPY --from=builder /opt/keycloak/ /opt/keycloak/
WORKDIR /opt/keycloak
ENTRYPOINT ["/opt/keycloak/bin/kc.sh", "start"]

and my docker-compose.yml accordingly.

version: "3.9"
services:
  proxy:
    image: caddy:2.5.1-alpine
    ports:
      - "${PROXY_IP}:80:80"
      - "${PROXY_IP}:443:443"
    volumes:
      - ${BASE_PATH:-.}/docker-volume/caddy/Caddyfile:/etc/caddy/Caddyfile:Z
      - ${BASE_PATH:-.}/docker-volume/caddy/caddy_data:/data:Z
      - ${BASE_PATH:-.}/docker-volume/caddy/caddy_config:/config:Z

  keycloak:
    image: ghcr.io/saw303/zscsupporter-be/keycloak-18.0.0:0.0.1
    ports:
      - "127.0.0.1:9443:8443"
    restart: unless-stopped
    environment:
      KC_DB_URL: jdbc:mariadb://keycloakdb:3306/keycloak
      KEYCLOAK_ADMIN: admin
      KEYCLOAK_ADMIN_PASSWORD: admin
      KC_HOSTNAME: localhost
      KC_HOSTNAME_STRICT: false
      KC_HTTP_ENABLED: true
      KC_DB_USERNAME: keycloak
      KC_DB_PASSWORD: secret
      KC_LOG_LEVEL: debug
      KC_PROXY: edge

  keycloakdb:
    image: mariadb:10.7.3-focal
    environment:
      MYSQL_ROOT_PASSWORD: root_secret
      MYSQL_DATABASE: keycloak
      MYSQL_USER: keycloak
      MYSQL_PASSWORD: secret
      TZ: "Europe/Zurich"
    tmpfs:
      - /var/lib/mysql:rw
    ports:
      - "127.0.0.1:3307:3306"
    deploy:
      resources:
        limits:
          cpus: "1.0"
          memory: 200M

After this configuration the access to the Keycloak Admin Console finally works. It seems like it is not possible to access the Admin Console with an non secure connection.

It looks like your Keycloak instance doesn't use --proxy=edge config.

Try official doc syntax: --proxy edge (of course translated to Dockerfile syntax) or simpler option is to set Keycloak container env variable KC_PROXY: edge

Related