Install Keycloak with the Operator
Austin Cunningham
Posted on September 15, 2023
Install the Keycloak via the operator
Create a project
oc new-project keycloak
I am going to install the operator the OperatorHub in Openshift which uses OLM(Operator Lifecyecle Manager). Select Operator/OperatorHub from the side bar and search for keycloak and install it in the keycloak namespace
I will go through the requirements now. This will not be a production setup.
Install an DB
Going to install an Ephemeral PostgreSQL DB as this is just a demo, create a yaml file called example-postgres.yaml
with the following content.
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
env:
- name: POSTGRES_PASSWORD
value: postgres #<-- change this
- name: POSTGRES_USER
value: postgres #<-- change this
- name: PGDATA
value: /data/pgdata
- name: POSTGRES_DB
value: keycloak
---
apiVersion: v1
kind: Service
metadata:
name: postgres-db
spec:
selector:
app: postgresql-db
ports:
- port: 5432
targetPort: 5432
And then apply the file to the cluster
oc apply -f example-postgres.yaml
The result
statefulset.apps/postgresql-db created
service/postgres-db created
After checking the postgres pod logs I see the following error
mkdir: cannot create directory ‘/data’: Permission denied
Nothing is ever easy. This is because Openshift blocks containers being run as root. To get this to work we need to add a service account with the correct permissions and add that service account to the statefulset. This is a good reference blog for more details. Here are the commands I used
# create the service account
~ oc create sa postgres-sa
serviceaccount/postgres-sa created
# add policy
~ oc adm policy add-scc-to-user anyuid -z postgres-sa
clusterrole.rbac.authorization.k8s.io/system:openshift:scc:anyuid added: "postgres-sa"
# set the policy on the namespace
~ oc adm policy add-scc-to-user anyuid -z postgres-sa -n keycloak
clusterrole.rbac.authorization.k8s.io/system:openshift:scc:anyuid added: "postgres-sa"
# add the service account to the statefulset
~ oc set sa statefulset postgresql-db postgres-sa
statefulset.apps/postgresql-db serviceaccount updated
# delete the failing existing pod
oc delete po postgresql-db-0
# check the pods
~ oc get po
NAME READY STATUS RESTARTS AGE
keycloak-operator-5659f58f4b-vtrm9 1/1 Running 0 29m
postgresql-db-0 1/1 Running 0 8m55s
Ok that's working
Hostname
We need a resolvable domain name. You can add any domain to your openshift cluster using a customDomain. As I couldn't be bothered setting up an actual domain name here or a valid cert this a quick hack for creating a resolvable domain name on Openshift.
# create a self signed cert
openssl req -subj '/CN=apps.austin.me/O=Test Keycloak./C=US' -newkey rsa:2048 -nodes -keyout key.pem -x509 -days 365 -out certificate.pem
# create cert project
oc new-project certs
# create a secret from that cert in the cert namespace
oc create secret tls austin-me-tls --cert=austin.me.crt --key=austin.me.key -n certs
# create a custom domain based on the cert
oc apply -f - <<EOF
---
apiVersion: managed.openshift.io/v1alpha1
kind: CustomDomain
metadata:
name: cunningham
spec:
domain: apps.austin.me
scope: External
certificate:
name: austin-me-tls
namespace: certs
EOF
# Wait for the custom domain to become ready
oc get customdomains
NAME ENDPOINT DOMAIN STATUS
cunningham oeffrs.cunningham.aucunnin.lpi0.s1.devshift.org apps.austin.me Ready
All pretty standard a this point but the domain apps.austin.me doesn't exist. So here is the hack edit the custom domain oc edit customdomain cunningham
and replace the spec.host with the endpoint.
apiVersion: managed.openshift.io/v1alpha1
kind: CustomDomain
metadata:
name: cunningham
spec:
certificate:
name: austin-me-tls
namespace: certs
domain: oeffrs.cunningham.aucunnin.lpi0.s1.devshift.org #<---- was apps.austin.me now points at the endpoint
loadBalancerType: Classic
scope: External
So our domain going forward is the endpoint oeffrs.cunningham.aucunnin.lpi0.s1.devshift.org
Create a DB secret
oc project keycloak
# change these to match the secret used in your statefulset
kubectl create secret generic keycloak-db-secret \
--from-literal=username=postgres \
--from-literal=password=postgres
Create the Keycloak CR
The CR(Custom Resource) creates a instance of the Keycloak UI. Create a yaml file called example-kc.yaml
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: austin-me-tls
hostname:
hostname: oeffrs.cunningham.aucunnin.lpi0.s1.devshift.org
And then apply the file
kubectl apply -f example-kc.yaml
We can check the CR status of keycloak, it will look like this when it is type: Ready
status: "True"
# using https://mikefarah.gitbook.io/yq/
~ oc get keycloak example-kc -oyaml | yq '.status'
conditions:
- lastTransitionTime: "2023-09-15T08:08:56.665783288Z"
message: ""
observedGeneration: 1
status: "True" #<------ This is what we are looking for I hate these condition to difficult to read a first glance
type: Ready
- lastTransitionTime: "2023-09-15T08:08:26.147242224Z"
message: ""
observedGeneration: 1
status: "False"
type: HasErrors
- lastTransitionTime: "2023-09-15T08:08:26.147242224Z"
message: ""
observedGeneration: 1
status: "False"
type: RollingUpdate
instances: 1
observedGeneration: 1
selector: app=keycloak,app.kubernetes.io/managed-by=keycloak-operator,app.kubernetes.io/instance=example-kc
That looks to have completed and if we port-forward the service it looks to be working
~ oc port-forward service/example-kc-service 8443:8443
Forwarding from 127.0.0.1:8443 -> 8443
Forwarding from [::1]:8443 -> 8443
Handling connection for 8443
Handling connection for 8443
Handling connection for 8443
Handling connection for 8443
Handling connection for 8443
And if we check the route we can open the keycloak landing page.
oc get routes --namespace keycloak
NAME HOST/PORT PATH SERVICES PORT TERMINATION WILDCARD
example-kc-ingress-76p8j oeffrs.cunningham.aucunnin.lpi0.s1.devshift.org ... 1 more example-kc-service https passthrough/Redirect None
Posted on September 15, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.