How to get Keycloak working with Docker

NOTE: This article might be being revised continuously because of new insights.

This blog describes how I created a couple of Docker images to demonstrate Keycloak.
Important in this blog is that the whole process will be described. I attended a couple of keycloak sessions during Javaone this year and during these sessions the illusion was created that adding Keycloak as the security provider for your application is very easy and almost non-invasive for your code.
What they did not tell you that configuring a server that could use keycloak was not as trivial.
This blog will also expose a java web application with rest end-points to show how the auth works.

This blog will tell all :-)

NOTES:

  • I will use usernames and passwords in this document as this is a demo blog. Be sure to change the relevant stuff if you want to use it for realzlike :-) I will not say so again.
  • This demo has done on a Mac and the commands will reflect that. You might have to do some translating if you are on another OS. Sorry will not be fixed

The wanted setup

The whole idea is to setup Keycloak as a separate server as a kind of “Security as a service” solution.

This is the setup I choose:

  • Get a docker data volume for my database values
  • jboss/keycloak-postgres docker image to serve as the keycloak security server / service
  • Tune a postgress docker image to serve as db for the keycloak server
  • jboss/wildfly docker image as the base for the application server. This images will be adjusted to enable keycloak as security provider.
  • put it all behind an apache proxy in a production environment.

Get a keycloak authentication server up and running

Time to maybe read more here about what the following commands mean.

1
2
3
4
5
6
# Data volume
docker run --name ivonet-postgres-data -v /var/lib/postgresql/data busybox true
# Postgres coupled to the datavolume
docker run --name ivonet-keycloak-postgres --volumes-from ivonet-postgres-data -p 15432:5432 -e POSTGRES_DATABASE=keycloak -e POSTGRES_USER=keycloak -e POSTGRES_PASSWORD=keycloak -e POSTGRES_ROOT_PASSWORD=s3cr3t -d postgres
# Keycloak server image linking to the postgres image
docker run --name ivonet-keycloak --link ivonet-keycloak-postgres:postgres -p 10000:8080 -e POSTGRES_DATABASE=keycloak -e POSTGRES_USER=keycloak -e POSTGRES_PASSWORD=keycloak jboss/keycloak-postgres

If you are not interested in accessing the ivonet-postgres-data with external tools, then you can eliminate the -p parameter from the ivonet-keycloak-postgres command.
As you might have noticed I gave the external port 15432. I did this because on my production environment I already have a native postgres running and am migrating slowly.

So now we have a setup that might work :-)
lets try it out and enter the following in the terminal:

1
open http://$(docker-machine ip default):10000/auth

You should now see something like this:

Keycloak-Docker.png

Play around.

NOTE: the default username and password is admin and admin.

Tune wildfly

So now we have a keycloak auth server up and running. it is time to get another instance of wildfly and make it keycloak enabled. This is the part not mentioned in the sessions I followed and what stumped me in the beginning. On my local machene I mostly use Glassfish as my EE development environment and I could not get the sample apps to work… After a couple of hours I started thinking for real :-) and doing some reading and it actually made sense that it didn’t work. I was so focuessed on the demo I saw that I didn’t realize that glassfish != wildfly and that something might have to be done to get stuff working.

Wel as you may have guessed you actually do need something else. Wildfly is the obvious choise because jboss is the major contributor to keycloak. You need an adaptor installed on the server, because you want the EE container to recognize keycloak as a security provider.

JBoss provides a docker image for that to but as of the time of this writing it was in wildfly 9.0.1.Final and on keycloak 1.5.0.Final and the most current versions are 9.0.2.Final for wildfly and 1.6.1.Final for keycloak so I upgraded from the latest default wildfly image.

The power of docker is to make adjustments gradually and modularly and so I did that too…

  • first upgraded to wildfly with the admin console enabled

Dockerfile:

1
2
3
FROM jboss/wildfly
RUN /opt/jboss/wildfly/bin/add-user.sh ivonet s3cr3t --silent
CMD ["/opt/jboss/wildfly/bin/standalone.sh", "-b", "0.0.0.0", "-bmanagement", "0.0.0.0"]
  • now build it: docker build --tag ivonet/wildfly-admin .
  • I also pushed it to de docker repository because I intent to reuse it for development purposes
1
2
docker login
docker push ivonet/wildfly-admin
  • add to this admin enabled wildfly by adding the keycloak adapter

See this Dockerfile for the one I used to build my own version of Wilfly with the keycloak adapter installed.

  • build it: docker build --tag ivonet/wildfly-admin-keycloak-adapter .
  • push it to the repo for reuse:
1
2
docker login
docker push ivonet/wildfly-admin-keycloak-adapter
  • As you can see this images is extended from ivonet/wildfly-admin that was just created.
  • it sets an environment variable for the keycloak version
  • it changes the workdir
  • it fetches (curl) the correct adapter from internet and un-tars’ it
  • changes workdir again
  • changes the standalone.xml file at the appropriate points in the file with the sed command to activate the security provider module/adapter
  • expose the standard port and the admin console port

This Dockerfile is of course the product of some trial and error I had to find out if the install was correct. This part will not be explained here, but if you want more input on this subject, drop me a line.

This image is again a base for more because now it is time to write an app and deploy it.

Screencast

I’ve made a screencast with a basic talk about Keycloak explaining a lot of the above text.

Production environment

Well I found out very soon that all of the above was great but didn’t work very wel in production.
My production environment is an Ubuntu Linux distribution and I access all my sites through Apache2 VirtualHost configurations. Apache is my front proxy and directs all based on servername resolves and ports.

When trying to put my keycloak docker construction as described above behind an Apache ProxyPass construction it all went to pieces. As we are talking about a security solution it seems kinda important to do all through https. So I went to letsencrypt and got myself a certificate and proxypassed my content to the inner docker endpoint. The trouble was that when talking secure (https) I got all kinds of error messages about “Mixed content” and stuff was blocked by the browser(s). Solving this was way more hassle than I expected and took my about two evenings of googling and reading to fix.

First I had to create a custom Dockerfile based on the jboss/keycloak-postgres image to change a few settings in the standalone.xml of that distribution. These settings can be found in the documentation but are not easy to find.
and replace the current image with my own

On build machine:

1
2
3
docker build --tag ivonet/keycloak-postgres .
docker login
docker push ivonet/keycloak-postgres

On server:

1
2
3
docker stop ivonet-keycloak
docker rm ivonet-keycloak
docker run --name ivonet-keycloak --link ivonet-keycloak-postgres:postgres -p 10000:8080 -e POSTGRES_DATABASE=keycloak -e POSTGRES_USER=keycloak -e POSTGRES_PASSWORD=keycloak ivonet/keycloak-postgres

Notice that now I get the ivonet/keycloak-postgres and not the jboss/keycloak-postgres.

Then I had to make changes to the Apache config:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<VirtualHost *:80>
ServerName security.sample.com

<Location />
RedirectPermanent / https://security.sample.com/auth/admin
</Locatio>n
</VirtualHost>
<VirtualHost *:443>
ServerName security.ivonet.it

SSLEngine on
SSLProtocol all -SSLv2
SSLCipherSuite ALL:!ADH:!EXPORT:!SSLv2:RC4+RSA:+HIGH:+MEDIUM

# Please look at letsencrypt.org for more info on this part of the config.
SSLCertificateFile /etc/letsencrypt/live/security.sample.com/cert.pem
SSLCertificateKeyFile /etc/letsencrypt/live/security.sample.com/privkey.pem
SSLCertificateChainFile /etc/letsencrypt/live/security.sample.com/chain.pem

ProxyRequests Off
ProxyPass / http://172.17.0.1:11000/
ProxyPassReverse / http://172.17.0.1:11000/
ProxyPreserveHost On

RequestHeader set X-Forwarded-For "https"
RequestHeader set X-Forwarded-Proto "https"

<Location />
Order deny,allow
Allow from all
SSLRequireSSL
</Location>

LogLevel info
ErrorLog /var/log/apache2/security-error.log
CustomLog /var/log/apache2/security-access.log combined
</VirtualHost>

Now I have no mixed content messages anymore and a certificate that is not self signed. Great stuff.
Hopefully I’m ready for the next step.

Realm backup

1
2
3
# make sure that the ivonet-keycloak-postgres image is started!
docker start ivonet-keycloak-postgres
docker run --name ivonet-keycloak-backup -it --rm -v $(pwd):/backup --link ivonet-keycloak-postgres:postgres -p 10001:8080 -e POSTGRES_DATABASE=keycloak -e POSTGRES_USER=keycloak -e POSTGRES_PASSWORD=keycloak jboss/keycloak-postgres /opt/jboss/keycloak/bin/standalone.sh -Dkeycloak.migration.action=export -Dkeycloak.migration.provider=singleFile -Dkeycloak.migration.realmName=IvoNet -Dkeycloak.migration.file=/backup/keycloak-realm-IvoNet-$(date +"%Y-%m-%d").json

This command is quite involved and will do:

  • run the jboss/keycloak-postgres image
  • naming it ivonet-keycloak-backup but will be removed after quit
  • map the current host folder to the image folder /backup
  • link ivonet-keycloak-postgres to the postgres image
  • start keycloak with a couple of keycloak specific parameters telling it to extract the realmName=IvoNet to a singleFile

The extraction will take place and after that the server is still running. You can quit it by pressing ctrl-c.

Now you will see a file in the current folder named something like: keycloak-realm-IvoNet-2015-11-12.json

This is a file you can import as a ream in Keycloak.

Now you can even make it more efficient:

1
2
3
4
# make sure that the ivonet-keycloak-postgres image is started!
docker start ivonet-keycloak-postgres
docker run --name ivonet-keycloak-backup -v $(pwd):/backup --link ivonet-keycloak-postgres:postgres -p 10001:8080 -e POSTGRES_DATABASE=keycloak -e POSTGRES_USER=keycloak -e POSTGRES_PASSWORD=keycloak ivonet/keycloak-postgres /opt/jboss/keycloak/bin/standalone.sh -Dkeycloak.migration.action=export -Dkeycloak.migration.provider=singleFile -Dkeycloak.migration.realmName=IvoNet -Dkeycloak.migration.file=/backup/keycloak-realm-IvoNet-$(date +"%Y-%m-%d").json
docker stop ivonet-keycloak-backup

Next time you can just run:

1
2
3
docker run ivonet-keycloak-backup
sleep 25
docker stop ivonet-keycloak-backup

and it will create the the json file at the location you ran the command. Now I think that you can change these commands to suit your needs. e.g. when you always want to backup to a specific host location you can replace $(pwd) with a local folder.

Conclusion

Configuring Keycloak for production based on docker and all was not as easy as I was made to believe. I learned a lot though and that’s why I love doing this stuff so much.
Hope you get stuff working a but faster than I did with the help provided here.

Have fun.