NOTE: This article is being revised continuously at this time 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 (1,2) 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 :-)
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:
Time to maybe read more here about what the following commands mean.
# 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:
open http://$(docker-machine ip default):10000/auth
You should now see something like this:
NOTE: the default username and password is admin and admin.
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...
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"]
docker build --tag ivonet/wildfly-admin .
docker login docker push ivonet/wildfly-admin
See this Dockerfile for the one I used to build my own version of Wilfly with the keycloak adapter installed.
docker build --tag ivonet/wildfly-admin-keycloak-adapter .
docker login docker push ivonet/wildfly-admin-keycloak-adapter
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.
I've made a screencast with a basic talk about Keycloak explaining a lot of the above text.
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:
docker build --tag ivonet/keycloak-postgres . docker login docker push ivonet/keycloak-postgres
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:
<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.
# 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:
The extraction will take place and after that the server is still running. You can quit it by pressing
Now you will see a file in the current folder named something like:
This is a file you can import as a ream in Keycloak.
Now you can even make it more efficient:
# 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:
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.
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.
Contact me here.
Last edited by Ivo Woltring, 2016-02-04 23:54:46