I wanted to look into Keycloak.X for quite a while. Keycloak.X is a lighter, faster, easier, more scalable, more cloud-native solution than the—now legacy—WildFly based Keycloak.
Keycloak.X is now officially known as Keycloak 17.0.0, the first official Quarkus-based version. It’s been released a few days ago[1] and so it was the right time to look at it.
I’m going to show you how I run Keycloak 17.0.0 with TLS behind Envoy proxy with Docker Compose. I’ve blogged about Keycloak and Keycloak behind Envoy before so this article is a recap of some of the previous articles from this blog.
Let’s go.
§directory structure
Here’s what we are dealing with:
[rad] keycloak-compose (keycloak-17) $ tree -a .
.
├── .docker
│ └── keycloak
│ └── Dockerfile
├── compose.yml
└── etc
└── envoy
└── envoy-keycloak.yaml
§Keycloak Dockerfile
Quarkus-based Keycloak 17.0.0 cannot be started without executing the build step. From Keycloak documentation[2]:
The
build
command is responsible for producing an immutable and optimized server image, which is similar to building a container image. In addition to persisting any build option you have set, this command also performs a series of optimizations to deliver the best runtime when starting and running the server. As a result, a lot of processing that would usually happen when starting and running the server is no longer necessary and the server can start and run faster.
There is no way to get away from executing build. We can use the start –auto-build flag (also documented in 2), but one way or another, we have to build it. The –auto-build option takes some time to execute on every start and building a custom image helps us shave some time off on subsequent starts.
I guessed that I wanted an optimized image so I opted in for building my own Docker image.
Building a custom Docker image is documented here[3].
My Dockerfile (.docker/keycloak/Dockerfile) looks like this:
|
|
Let’s go through some of its most interesting aspects.
The builder stage configures the database provider, here Postgres, and executes kc.sh build. This is where the optimized build is created.
The second stage uses the builder stage and simply copies the build output into the final container. If you have looked at the original Keycloak documentation3, you have probably noticed that I do not generate a certificate here. I am putting Keycloak behind Envoy, which will terminate TLS for me. I do not need a certificate.
The new thing in 17.0.0 is the use of KEYCLOAK_ADMIN and KEYCLOAK_ADMIN_PASSWORD environment variables. Prior to 17.0.0, to create an initial administrator account, we had to execute the /opt/jboss/keycloak/bin/add-user-keycloak.sh. script and pass -u user -p pass arguments. If you read some of my previous article, you maybe remember this command:
|
|
A note of caution: in a real deployment, you’d not store those credentials directly in the Dockerfile. You’d pass them from outside. I kept them here for brevity only.
Three other relevant notes here:
- KC_HOSTNAME: configures the hostname on which Keycloak is intended to be running,
- KC_HOSTNAME_STRICT: because I am putting Keycloak behind Envoy, I set this to false; in the Docker Compose setup, Envoy will be communicating with Keycloak using the keycloak hostname, this setting disables Keycloak hostname verification,
- KC_HTTP_ENABLED: is set to true because Envoy is terminating TLS, I don’t need TLS termination directly on Keycloak.
Save the contents of the Dockerfile in .docker/keycloak/Dockerfile and build the Docker image:
|
|
§envoy configuration
The etc/envoy/envoy-keycloak.yaml file is exactly the same as in my previous blog post[4], except of the domain name.
|
|
This configuration will match any request to https://idp-dev.gruchalski.com, terminate TLS using a certificate and key from /etc/envoy/certificates/* and forward to proxy-domain1 cluster, which will forward the request to keycloak:8080, where keycloak is Keycloak’s hostname in the Docker Compose configuration.
§certificates
Keycloak is configured to run on idp-dev.gruchalski.com. As in, once running, I will be able to use Keycloak by going to https://idp-dev.gruchalski.com. In order to do so, I’m adding the following line to my /etc/hosts
file:
127.0.0.1 idp-dev.gruchalski.com
I need certificates. Because I am using a regular browser, I preferably want to have certificates that are by default trusted by my operating system. The obvious choice is Let’s Encrypt. I have written before about using Let’s Encrypt certificates for local development[5]. Read that article to find out more.
Long story short, my DNS is managed in Route 53 and I can use Route 53 API to automate the dns-01 challenge to obtain certificates for my local deployment. To do so, I’m using the LEGO client:
|
|
When the command finishes, the directory structure looks like this:
[rad] keycloak-compose (keycloak-17) $ tree -a .
.
├── .docker
│ └── keycloak
│ └── Dockerfile
├── compose.yml
└── etc
└── envoy
├── certificates
│ ├── idp-dev.gruchalski.com.crt
│ ├── idp-dev.gruchalski.com.issuer.crt
│ ├── idp-dev.gruchalski.com.json
│ └── idp-dev.gruchalski.com.key
└── envoy-keycloak.yaml
I’m ready to start the Compose setup.
§docker compose
My compose.yml looks like this:
|
|
You can see that:
- I bind mount the etc/envoy directory in the Envoy container, this directory contains the envoy-keycloak.yaml file and the certificates I got from Let’s Encrypt,
- the Postgres database, username, and password match the values from the Dockerfile.
Start everything with:
|
|
§that’s it
Keycloak 17.0.0 running locally in Docker Compose, with TLS, behind Envoy proxy.
Next step is to bring SPIs back into this setup!