Adding Custom Extensions To The New (Quarkus) Keycloak Operator

In recent times the Keycloak Operator for OpenShift has moved from the old EAP based implementation to a new implementation based on Quarkus. At the same time the Custom Resource Definition for a Keycloak object has moved from apiVersion v1alpha1 to v2alpha1.

While on average this is a good move, and it brings many improvements, some functionality appears to have not made the transition yet, most notably the ability to easily add custom extensions and providers.

Since one of our customers encountered this and asked for our help we went and sought a solution to this, without having to resort to building custom images, as those would add an extra maintenance burden on an already busy team.

The Solution

The solution is actually fairly straightforward: While the operator no longer has a configuration option for extensions, Keycloak itself still happily loads any extensions it finds on disk (from /opt/keycloak/extensions in the case of using the operator). The remaining problem now is to add the files there without resorting to building custom images.

That second, smaller, problem is actually accounted for by using the spec.unsupported.podTemplate stanza in the Keycloak custom resource. Here you can add a Kubernetes Pod Template that is merged with the default Pod Template for your Keycloak Instances.

N.B. As the name of the stanza implies this is UNSUPPORTED. It works at the time of writing, it might work tomorrow or next month, but it is not guaranteed to keep on working forever.

An Example

Let’s put all of what we’ve discovered together:

  1. Create a project on OpenShift for our experiment:
    1oc new-project keycloak-test
    
  2. Install the Keycloak Operator in this namespace:
    1. Create an OperatorGroup
       1oc apply -f - << EOF
       2apiVersion: operators.coreos.com/v1
       3kind: OperatorGroup
       4metadata:
       5  generateName: keycloak-test-
       6  namespace: keycloak-test
       7spec:
       8  targetNamespaces:
       9  - keycloak-test
      10  upgradeStrategy: Default
      11EOF
      
    2. Create a Subscription
       1oc apply -f - << EOF
       2apiVersion: operators.coreos.com/v1alpha1
       3kind: Subscription
       4metadata:
       5  name: keycloak-operator
       6  namespace: keycloak-test
       7spec:
       8  channel: fast
       9  installPlanApproval: Automatic
      10  name: keycloak-operator
      11  source: community-operators
      12  sourceNamespace: openshift-marketplace
      13EOF
      
  3. Download the extensions you wish to use, we are going to use “Apple Identity Provider for Keycloak” extensions, since it’s easy to see if it’s loaded from the main admin page.
    1  wget https://github.com/klausbetz/apple-identity-provider-keycloak/releases/download/1.13.0/apple-identity-provider-1.13.0.jar
    
  4. Create a ConfigMap named providers, and fill it with the extension(s) you wish to use.
    1  oc create configmap providers --dry-run=client -o yaml --from-file=apple-identity-provider-1.13.0.jar > providers-cm.yaml
    2  oc apply -f providers-cm.yaml
    
  5. Create a new Keycloak instance
     1oc create -f - << EOF
     2apiVersion: k8s.keycloak.org/v2alpha1
     3kind: Keycloak
     4metadata:
     5  labels:
     6    app: sso
     7  name: example-keycloak
     8  namespace: keycloak-test
     9spec:
    10  hostname:
    11    hostname: keycloak.apps-crc.testing
    12  http:
    13    tlsSecret: my-tls-secret
    14  ingress:
    15    annotations:
    16      route.openshift.io/termination: reencrypt
    17  instances: 1
    18  startOptimized: false
    19  unsupported:
    20    podTemplate:
    21      spec:
    22        containers:
    23        - volumeMounts:
    24          - mountPath: /opt/keycloak/providers
    25            name: keycloak-providers
    26        volumes:
    27        - configMap:
    28            name: providers
    29          name: keycloak-providers
    30EOF
    
  6. The new Keycloak instance isn’t yet starting, since it can’t find the secret my-tls-secret, let’s remedy that
    1oc annotate service example-keycloak-service service.beta.openshift.io/serving-cert-secret-name=my-tls-secret
    
  7. Wait for the Keycloak pod to come up. Then login to the admin console as the admin user. You can get the password from the keycloak-initial-admin secret
    1oc get secret example-keycloak-initial-admin -o go-template='{{ .data.password | base64decode }}'
    
    The URL to login to is listed in your Keycloak CR as spec.hostname. You will need to adjust that one to suit your installation. In our example we also changed the default route from “passthrough” TLS termination to “Reencrypt”, using the wildcard certificates on our OpenShift router.
  8. If everything went well you should now have the “Apple” provider available
    A Keycloak Admin console showing the availability of the apple provider highlighted
    The Keycloak Admin console showing the availability of the Apple provider

Improvements

In this example we have used a ConfigMap to store our extensions. While this works in this example, where the extension is fairly small, for larger or more extensions it might be better to switch to a PersistentVolumeClaim, and a Kubernetes Job to download extensions to that PVC. This is left as an exercise for the reader.

Gerelateerde posts