Skip to content

Keycloak

Keycloak provides user federation, strong authentication, authorisation and is a great addition to our on-prem SSO solution.

Instantiate a postgres cluster using the PGO

When backed by postgresql, it's easy to run multiple instances of Keycloak to get high-availability and scalability, so let's create a cluster with our operator. There's a new trick in our postgresql config, creating the namespaced schema for the user keycloak.

apiVersion: v1
kind: ConfigMap
metadata:
  name: db-init-schema
  namespace: keycloak
data:
  init.sql: |
    \echo "EXECUTING DB INIT SCRIPT"
    \c keycloak
    CREATE SCHEMA keycloak AUTHORIZATION keycloak;
---
apiVersion: postgres-operator.crunchydata.com/v1beta1
kind: PostgresCluster
metadata:
  name: keycloak
  namespace: keycloak
spec:
  postgresVersion: 16
  databaseInitSQL:
    key: init.sql
    name: db-init-schema
  instances:
    - name: instance1
      replicas: 2
      dataVolumeClaimSpec:
        accessModes:
        - ReadWriteOnce
        resources:
          requests:
            storage: 2Gi
        storageClassName: rook-cephfs
      affinity:
        podAntiAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
          - weight: 1
            podAffinityTerm:
              topologyKey: kubernetes.io/hostname
              labelSelector:
                matchLabels:
                  postgres-operator.crunchydata.com/cluster: keycloak
                  postgres-operator.crunchydata.com/instance-set: instance1


  backups:
    pgbackrest:
      image: registry.developers.crunchydata.com/crunchydata/crunchy-pgbackrest:ubi8-2.47-2
      manual:
        options:
        - --type=full
        repoName: repo1
      repos:
      - name: repo1
        volume:
          volumeClaimSpec:
            accessModes:
            - "ReadWriteOnce"
            resources:
              requests:
                storage: 2Gi
            storageClassName: rook-cephfs

A pre-emptive fly in the ointment, TLS

If you don't plan on using LDAP federation, feel free to skip this section.

If you plan to use the LDAP server we created in the previous step you will either need to use plain-text LDAP protocol, or configure your LDAP server with a TLS certificate. If you use a self-signed TLS certificate, or an internal root you will need to configure a Java keystore with this certificate otherwise Keycloak will fail to connect.

Create Java keystore

First, generate a Java-compatible keystore, any machine that has Java available should suit:

keytool -import -alias my-ldap-cert -keystore keycloak-spi.truststore.jks -file /path/to/my-ldap-cert.crt

Turn keystore into Kubernetes secret

Now turn that into a k8s secret that we can consume in our deployment:

kubectl -n keycloak create secret generic allcerts --from-file=./keycloak-spi.truststore.jks

Install the Helm chart

helm upgrade \
  --install keycloak bitnami/keycloak \
  --create-namespace \
  -n keycloak \
  --set production=true \
  --set proxy=edge \
  --set replicaCount=2 \
  --set pdb.create=true \
  --set postgresql.enabled=false \
  --set externalDatabase.database=keycloak \
  --set externalDatabase.existingSecret="keycloak-pguser-keycloak" \
  --set externalDatabase.existingSecretHostKey=host \
  --set externalDatabase.existingSecretPortKey=port \
  --set externalDatabase.existingSecretUserKey=user \
  --set externalDatabase.existingSecretPasswordKey=password \
  --set extraEnvVars\[0\].name=KC_DB_SCHEMA \
  --set extraEnvVars\[0\].value=keycloak
helm upgrade \
  --install keycloak bitnami/keycloak \
  --create-namespace \
  -n keycloak \
  --set production=true \
  --set proxy=edge \
  --set replicaCount=2 \
  --set pdb.create=true \
  --set postgresql.enabled=false \
  --set externalDatabase.database=keycloak \
  --set externalDatabase.existingSecret="keycloak-pguser-keycloak" \
  --set externalDatabase.existingSecretHostKey=host \
  --set externalDatabase.existingSecretPortKey=port \
  --set externalDatabase.existingSecretUserKey=user \
  --set externalDatabase.existingSecretPasswordKey=password \
  --set extraEnvVars\[0\].name=KC_DB_SCHEMA \
  --set extraEnvVars\[0\].value=keycloak \
  --set spi.existingSecret=allcerts \
  --set spi.truststorePassword=changeit \
  --set spi.truststoreFilename=keycloak-spi.truststore.jks \
  --set spi.truststoreFilename=keycloak-spi.truststore.jks

Find the default user

By default, the first user is named user and the password is available as a secret:

kubectl -n keycloak get secrets/keycloak -o jsonpath="{.data.admin-password}" | base64 -d

Copy that to your clipboard and we'll use it to login in the next step.

Port forward and configure

Find the service name we need to connect to:

kubectl -n keycloak get svc -l app.kubernetes.io/component=keycloak

You should see a list similar to the following:

keycloak            ClusterIP   10.104.61.134   <none>        80/TCP    8m50s
keycloak-headless   ClusterIP   None            <none>        80/TCP    8m50s

We want keycloak. So:

kubectl -n keycloak port-forward svc/keycloak 8080:80

Now you should be able to connect to http://localhost:8080 and use the credentials from the previous step to login and configure Keycloak.

Next steps

For production use you would now need to provision some DNS entries, and an ingress configuration to grant access outside of the cluster.