The Keycloak team announced that they were going to move to a new Operator framework that will effectively manage Keycloak installations on a Kubernetes cluster.
So what is an Operator in the Kubernetes context?
Let’s simplify it a bit here... It’s basically a component that takes over the operational aspects of your application. So rather than managing all of the lifecycle and state in scripts and in our minds, they are coded into an operator.
For us, the Keycloak Operator does exactly that and in this blog, I will cover how to set up a very simple Keycloak installation and get ready for development. If you are interested in knowing more about Operators you will likely find some good info here.
If you aren’t familiar with Keycloak; it’s an open source identity and access management software. Its current version, 22, is used a lot in the wild already. It provides single sign-on capability with OAuth/OIDC, AD, LDAP, and SAML v2 as well.
If you aren't very familiar with Keycloak, I have also written a small self-paced Keycloak tutorial that goes through all the basics, as well as some advanced configs.
Let's get to installing a basic keycloak instance backed by a PostgreSQL database.
Here’s the plan of action:
I am choosing to install the keycloak operator via the webconsole which is an easy way to do it.
It’s also possible to do it via CLI.
Simply search for keycloak
and press install:
Once you press install the installer will ask which namespace you would like to install to. In my case it is the rhbk
.
This could take a couple of seconds or minutes depending on your cluster.
For the Keycloak server to store its state and data it should have a database. For this, I will use Crunchy.
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: postgresql-db
spec:
serviceName: postgresql-db-service
selector:
matchLabels:
app: postgresql-db
replicas: 1
template:
metadata:
labels:
app: postgresql-db
spec:
containers:
- name: postgresql-db
image: postgres:latest
volumeMounts:
- mountPath: /data
name: cache-volume
env:
- name: POSTGRES_PASSWORD
value: testpassword
- name: PGDATA
value: /data/pgdata
- name: POSTGRES_DB
value: keycloak
volumes:
- name: cache-volume
emptyDir: {}
---
apiVersion: v1
kind: Service
metadata:
name: postgres-db
spec:
selector:
app: postgresql-db
type: LoadBalancer
ports:
- port: 5432
targetPort: 5432
Next, let's generate a self-signed certificate. In a production environment, you'd typically obtain a certificate from a certificate authority. However, for testing purposes, we can use a self-signed certificate.
On a Linux machine, you can create a self-signed certificate like this. In this example, keycloak.rhbk.apps.green.demoshift.com
represents the address of your Keycloak instance, and "O" stands for test demoshift
. You can replace test demoshift
with any organization identifier you prefer; in my case, it's denoting a test organization.
openssl req -subj '/CN=keycloak.rhbk.apps.green.demoshift.com/O=Test demoshift./C=US' -newkey rsa:2048 -nodes -keyout key.pem -x509 -days 365 -out certificate.pem
Generating a 2048 bit RSA private key
.....................+++++
.....................+++++
writing new private key to 'key.pem'
Let’s load this certificate into our Kubernetes environment by using the following command.
Make sure the path for the cert is correct, in my case, I am in the same dir as my cert.
kubectl create secret tls example-tls-secret --cert certificate.pem --key key.pem
Also a good practice is to load the database password as a secret rather than having it littered around different yaml files.
kubectl create secret generic keycloak-db-secret \
--from-literal=username=postgres \
--from-literal=password=testpassword
apiVersion: k8s.keycloak.org/v2alpha1
kind: Keycloak
metadata:
name: example-kc
spec:
instances: 1
db:
vendor: postgres
host: postgres-db
usernameSecret:
name: keycloak-db-secret
key: username
passwordSecret:
name: keycloak-db-secret
key: password
http:
tlsSecret: example-tls-secret
hostname:
hostname: keycloak.rhbk.apps.green.demoshift.com
Now, if we go back to the webconsole you should be able to see:
OpenShift has this nice feature that you can see the route once you click your application. also visible in the image below. Head over to your instance and login.
To login, obviously, one needs the admin password which in this case has been autogenerated by the operator. Let’s get that by running the following command:
kubectl get secret example-kc-initial-admin -o jsonpath='{.data.password}' | base64 --decode
Assuming we have the password, let’s go back to the route and click on Adminstration console
. The username is admin
and password should be what we retrieved from the command above.
Login and Viola!
we have landed on our freshly installed Keycloak server.
Currently, we have only the master realm. Generally, it's advisable to keep the master realm as is, making minimal changes, to avoid potential lockout situations. Now, let's explore importing a realm. This is where the Operator's capabilities shine.
We'll create a RealmImportCR (Custom Resource), and it will facilitate the addition of a new realm to Keycloak. Consider this scenario: if the Keycloak pod experiences downtime at any point, the Operator will ensure that it's restored to the exact state it was in. Where is this state stored? It's stored within the Custom Resources and the database, providing a robust system for maintaining and recovering configurations.
Okay let’s test this out further. How about adding a new realm via the KCImportRealmCR?
Let's go back to the Operator view
Click on KeycloakRealmImport -> Create instance
I had a realm json file. Obviously, operators and most K8s use cases expect yaml so I had to make a conversion. I used the following yq for conversion. There are also online tools available, but given the sensitive nature of realms, I wouldn’t suggest using a random online tool to convert to Yaml.
Load the yaml and press create:
It can take a couple of seconds to come up. As soon as we create the CR, the Keycloak Operator will pick it up and add it to the running Keycloak instance.
If you log back into the admin console it should look like this:
If we explore further and look into the clients there is a backend-service
. It’s a Client config for a Quarkus-based REST service written in Java. That’s for another post.
There is a lot more that can be done with Operators. If you are looking for more in-depth details the Keycloak docs is a great resource. If you are looking to explore more and try this installation.
More to come on Keycloak in later posts.
Also published here.