Keycloak installation on Kubernetes
Keycloak is "an open source identity and access management solution". Using a local database, or a variety of backends (think OpenLDAP), you can provide Single Sign-On (SSO) using OpenID, OAuth 2.0, and SAML.
Keycloak's OpenID provider can also be used to provide OIDC-based authentication to your Kubernetes cluster, or in combination with Traefik Forward Auth, to protect vulnerable services with an extra layer of authentication.
Keycloak requirements
Ingredients
Already deployed:
- A Kubernetes cluster
- Flux deployment process bootstrapped
- An Ingress controller to route incoming traffic to services
- Persistent storage to store persistent stuff
Optional:
- External DNS to create an DNS entry the "flux" way
Preparation
Keycloak Namespace
We need a namespace to deploy our HelmRelease and associated YAMLs into. Per the flux design, I create this example yaml in my flux repo at /bootstrap/namespaces/namespace-keycloak.yaml
:
apiVersion: v1
kind: Namespace
metadata:
name: keycloak
Keycloak HelmRepository
We're going to install the Keycloak helm chart from the bitnami repository, so I create the following in my flux repo (assuming it doesn't already exist):
apiVersion: source.toolkit.fluxcd.io/v1beta1
kind: HelmRepository
metadata:
name: bitnami
namespace: flux-system
spec:
interval: 15m
url: oci://registry-1.docker.io/bitnamicharts/keycloak
Keycloak Kustomization
Now that the "global" elements of this deployment (just the HelmRepository in this case) have been defined, we do some "flux-ception", and go one layer deeper, adding another Kustomization, telling flux to deploy any YAMLs found in the repo at /keycloak/
. I create this example Kustomization in my flux repo:
apiVersion: kustomize.toolkit.fluxcd.io/v1beta2
kind: Kustomization
metadata:
name: keycloak
namespace: flux-system
spec:
interval: 30m
path: ./keycloak
prune: true # remove any elements later removed from the above path
timeout: 10m # if not set, this defaults to interval duration, which is 1h
sourceRef:
kind: GitRepository
name: flux-system
healthChecks:
- apiVersion: helm.toolkit.fluxcd.io/v2beta1
kind: HelmRelease
name: keycloak
namespace: keycloak
Fast-track your fluxing! 🚀
Is crafting all these YAMLs by hand too much of a PITA?
"Premix" is a git repository, which includes an ansible playbook to auto-create all the necessary files in your flux repository, for each chosen recipe!
Let the machines do the TOIL!
Keycloak DNSEndpoint
If, like me, you prefer to create your DNS records the "GitOps way" using ExternalDNS, create something like the following example to create a DNS entry for your Authentik ingress:
apiVersion: externaldns.k8s.io/v1alpha1
kind: DNSEndpoint
metadata:
name: "keycloak.example.com"
namespace: keycloak
spec:
endpoints:
- dnsName: "keycloak.example.com"
recordTTL: 180
recordType: CNAME
targets:
- "traefik-ingress.example.com"
Tip
Rather than creating individual A records for each host, I prefer to create one A record (nginx-ingress.example.com
in the example above), and then create individual CNAME records pointing to that A record.
Keycloak HelmRelease
Lastly, having set the scene above, we define the HelmRelease which will actually deploy keycloak into the cluster. We start with a basic HelmRelease YAML, like this example:
apiVersion: helm.toolkit.fluxcd.io/v2beta1
kind: HelmRelease
metadata:
name: keycloak
namespace: keycloak
spec:
chart:
spec:
chart: keycloak
version: 17.3.x # auto-update to semver bugfixes only (1)
sourceRef:
kind: HelmRepository
name: bitnami
namespace: flux-system
interval: 15m
timeout: 5m
releaseName: keycloak
values: # paste contents of upstream values.yaml below, indented 4 spaces (2)
- I like to set this to the semver minor version of the Keycloak current helm chart, so that I'll inherit bug fixes but not any new features (since I'll need to manually update my values to accommodate new releases anyway)
- Paste the full contents of the upstream values.yaml here, indented 4 spaces under the
values:
key
If we deploy this helmrelease as-is, we'll inherit every default from the upstream Keycloak helm chart. That's probably hardly ever what we want to do, so my preference is to take the entire contents of the Keycloak helm chart's values.yaml, and to paste these (indented), under the values
key. This means that I can then make my own changes in the context of the entire values.yaml, rather than cherry-picking just the items I want to change, to make future chart upgrades simpler.
Why not put values in a separate ConfigMap?
Didn't you previously advise to put helm chart values into a separate ConfigMap?
Yes, I did. And in practice, I've changed my mind.
Why? Because having the helm values directly in the HelmRelease offers the following advantages:
- If you use the YAML extension in VSCode, you'll see a full path to the YAML elements, which can make grokking complex charts easier.
- When flux detects a change to a value in a HelmRelease, this forces an immediate reconciliation of the HelmRelease, as opposed to the ConfigMap solution, which requires waiting on the next scheduled reconciliation.
- Renovate can parse HelmRelease YAMLs and create PRs when they contain docker image references which can be updated.
- In practice, adapting a HelmRelease to match upstream chart changes is no different to adapting a ConfigMap, and so there's no real benefit to splitting the chart values into a separate ConfigMap, IMO.
Then work your way through the values you pasted, and change any which are specific to your configuration.
Configure Keycloak Helm Chart
The following sections detail suggested changes to the values pasted into /keycloak/helmrelease-keycloak.yaml
from the Keycloak helm chart's values.yaml. The values are already indented correctly to be copied, pasted into the HelmRelease, and adjusted as necessary.
Ingress
Setup your ingress for the Keycloak UI, enabling at least ingress.enabled
as below, and additional TLS options as necessary1:
ingress:
## @param ingress.enabled Enable ingress record generation for Keycloak
##
enabled: false
Either leave blank to accept the default ingressClassName, or set to whichever ingress controller you want to use.
Install Keycloak!
Commit the changes to your flux repository, and either wait for the reconciliation interval, or force a reconcilliation using flux reconcile source git flux-system
. You should see the kustomization appear...
~ ❯ flux get kustomizations keycloak
NAME READY MESSAGE REVISION SUSPENDED
keycloak True Applied revision: main/70da637 main/70da637 False
~ ❯
The helmrelease should be reconciled...
~ ❯ flux get helmreleases -n keycloak keycloak
NAME READY MESSAGE REVISION SUSPENDED
keycloak True Release reconciliation succeeded v17.3.x False
~ ❯
And you should have happy pods in the keycloak namespace:
~ ❯ k get pods -n keycloak
NAME READY STATUS RESTARTS AGE
keycloak-0 1/1 Running 1 (3d17h ago) 26d
keycloak-postgresql-0 1/1 Running 1 (3d17h ago) 26d
~ ❯
Browse to the URL you configured in your ingress above, and confirm that the Keycloak UI is displayed. Login with the admin user you defined above, and confirm a successful login.
Create Keycloak user
Why are we adding a user when I have an admin user already?
Do you keep a spare set of house keys somewhere other than your house? Do you login as root
onto all your systems? Think of this as the same prinicple - lock the literal admin
account away somewhere as a "password of last resort", and create a new user for your day-to-day interaction with Keycloak.
Within the "Master" realm (no need for more realms unless you want to), navigate to Manage -> Users, and then click Add User at the top right:
Populate your new user's username (it's the only mandatory field)
Set Keycloak user credentials
Once your user is created, to set their password, click on the "Credentials" tab, and procede to reset it. Set the password to non-temporary, unless you like extra work!
Summary
What have we achieved? We've got Keycloak running and accessible, we've created our normal-use user, and we're ready to flex the power of Keycloak to deploy an OIDC provider for Kubernetes, or to provide OIDC to Traefik Forward Auth to protect vulnerable UIs!
Summary
Created:
- Keycloak running and ready for authentication !
Next:
- Configure Kubernetes OIDC authentication, unlocking production readiness as well as the Kubernetes Dashboard and Weave GitOps UIs (coming soon)
Chef's notes 📓
-
There's a trick to using a single cert across multiple Ingresses or IngressRoutes (coming soon) ↩
Tip your waiter (sponsor) 👏
Did you receive excellent service? Want to compliment the chef? (..and support development of current and future recipes!) Sponsor me on Github / Ko-Fi / Patreon, or see the contribute page for more (free or paid) ways to say thank you! 👏
Employ your chef (engage) 🤝
Is this too much of a geeky PITA? Do you just want results, stat? I do this for a living - I'm a full-time Kubernetes contractor, providing consulting and engineering expertise to businesses needing short-term, short-notice support in the cloud-native space, including AWS/Azure/GKE, Kubernetes, CI/CD and automation.
Learn more about working with me here.
Flirt with waiter (subscribe) 💌
Want to know now when this recipe gets updated, or when future recipes are added? Subscribe to the RSS feed, or leave your email address below, and we'll keep you updated.