Basic Keycloak deployment

Install Keycloak using the Operator.

Performing a basic Keycloak deployment

This guide describes how to perform a basic Keycloak Deployment on Kubernetes or OpenShift using the Operator.

Preparing for deployment

Once the Keycloak Operator is installed and running in the cluster namespace, you can set up the other deployment prerequisites.

  • Database

  • Hostname

  • TLS Certificate and associated keys

Database

A database should be available and accessible from the cluster namespace where Keycloak is installed. For a list of supported databases, see Configuring the database. The Keycloak Operator does not manage the database and you need to provision it yourself. Consider verifying your cloud provider offering or using a database operator.

For development purposes, you can use an ephemeral PostgreSQL pod installation. To provision it, follow the approach below:

Create YAML file example-postgres.yaml:

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:15
          volumeMounts:
            - mountPath: /data
              name: cache-volume
          env:
            - name: POSTGRES_USER
              value: testuser
            - 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

Apply the changes:

kubectl apply -f example-postgres.yaml

Hostname

For a production ready installation, you need a hostname that can be used to contact Keycloak. See Configuring the hostname (v2) for the available configurations.

For development purposes, this guide will use test.keycloak.org.

When running on OpenShift, with ingress enabled, and with the spec.ingress.classname set to openshift-default, you may leave the spec.hostname.hostname unpopulated in the Keycloak CR. The operator will assign a default hostname to the stored version of the CR similar to what would be created by an OpenShift Route without an explicit host - that is ingress-namespace.appsDomain If the appsDomain changes, or should you need a different hostname for any reason, then update the Keycloak CR.

If you set the hostname-admin, or the deprecated hostname-admin-url, even if you enable ingress, no ingress will be created specifically for admin access. Admin access via a separate hostname is generally expected to have access restrictions, which are not currently expressible via the Keycloak CR. Also the default ingress does not prevent accessing admin endpoints, so you may not want to enable ingress handling via the Keycloak CR at all when you have a separate hostname for admin endpoints.

TLS Certificate and key

See your Certification Authority to obtain the certificate and the key.

For development purposes, you can enter this command to obtain a self-signed certificate:

openssl req -subj '/CN=test.keycloak.org/O=Test Keycloak./C=US' -newkey rsa:2048 -nodes -keyout key.pem -x509 -days 365 -out certificate.pem

You should install it in the cluster namespace as a Secret by entering this command:

kubectl create secret tls example-tls-secret --cert certificate.pem --key key.pem

Deploying Keycloak

To deploy Keycloak, you create a Custom Resource (CR) based on the Keycloak Custom Resource Definition (CRD).

If you are on OpenShift, you cannot use wildcard certificates on passthrough Routes with HTTP/2 enabled. The following Keycloak CR on OpenShift if you are using a wildcard certificate with the default IngressClass, creates such a Route. In this situation, modify the YAML based on "Accessing the Keycloak deployment" later in this topic to use TLS Termination or an OpenShift re-encrypt Route.

Consider storing the Database credentials in a separate Secret. Enter the following commands:

kubectl create secret generic keycloak-db-secret \
  --from-literal=username=[your_database_username] \
  --from-literal=password=[your_database_password]

You can customize several fields using the Keycloak CRD. For a basic deployment, you can stick to the following approach:

Create YAML file example-kc.yaml:

apiVersion: k8s.keycloak.org/v2beta1
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: test.keycloak.org
  proxy:
    headers: xforwarded (1)
1 Check your load balancer or reverse proxy configuration to determine whether it utilizes the Forwarded (RFC 7239) or X-Forwarded-* (e.g., X-Forwarded-For) mechanism for header propagation.

Apply the changes:

kubectl apply -f example-kc.yaml

To check that the Keycloak instance has been provisioned in the cluster, check the status of the created CR by entering the following command:

kubectl get keycloaks/example-kc -o go-template='{{range .status.conditions}}CONDITION: {{.type}}{{"\n"}}  STATUS: {{.status}}{{"\n"}}  MESSAGE: {{.message}}{{"\n"}}{{end}}'

When the deployment is ready, look for output similar to the following:

CONDITION: Ready
  STATUS: true
  MESSAGE:
CONDITION: HasErrors
  STATUS: false
  MESSAGE:
CONDITION: RollingUpdate
  STATUS: false
  MESSAGE:

Accessing the Keycloak deployment

The Keycloak deployment can be exposed through a basic Ingress accessible through the provided hostname.

On installations with multiple default IngressClass instances or when running on OpenShift 4.12+ you should provide an ingressClassName by setting ingress spec with className property to the desired class name:

Edit YAML file example-kc.yaml:

apiVersion: k8s.keycloak.org/v2beta1
kind: Keycloak
metadata:
  name: example-kc
spec:
    ...
    ingress:
      className: openshift-default
The operator annotates the Ingress to match expectations for TLS passthrough or TLS termination on OpenShift with the default IngressClass. See below for more on TLS termination.

TLS Termination with default Ingress

When using the operator-provided ingress (spec.ingress.enabled: true), there are many Keycloak CR options (like service and ingress labels and annotations) that allow you to customize your ingress behavior.

With passthrough, the client IP address is not visible to Keycloak because the default ingress controller does not support the PROXY protocol between the ingress and Keycloak.

Passthrough is the best option when you want to use mTLS (mutual TLS), as the TLS connection is established directly between the client and Keycloak, preserving the client certificate for authentication. Edge and reencrypt do not offer mTLS between the client and the ingress via configuration options in the CRs.

With edge, the ingress controller terminates TLS and forwards the connection to Keycloak unencrypted. If internal encryption is required, use reencrypt instead.

With edge or reencrypt, you can only trust the client IP address and other forwarded headers if you restrict access to the Keycloak HTTP port via network policies (see Advanced configuration). For example, allow connections only from the namespace where the ingress controller runs. You must also prevent users in the Keycloak namespace from creating additional ingress or route instances, as an unauthorized ingress could bypass the trusted ingress controller and spoof client IP addresses or other forwarded headers. Without these protections, any Pod that can connect to Keycloak directly can spoof forwarded headers. Parsing proxy headers therefore conflicts with direct backchannel access to Keycloak, as backchannel clients bypass the ingress and could spoof those headers.

This section shows how to achieve the following TLS Termination when using OpenShift:

For other platforms or IngressControllers, you can use the provided examples as a starting point.

Passthrough

It is enabled when you associate a tlsSecret with the http configuration and leave Ingress enabled without specifying a tlsSecret on it.

apiVersion: k8s.keycloak.org/v2alpha1
kind: Keycloak
metadata:
  name: example-kc
spec:
  http:
    tlsSecret: example-tls-secret
  ingress:
    enabled: true
  hostname:
    hostname: test.keycloak.org
On OpenShift you are not allowed to use wildcard certificates on passthrough Routes with HTTP/2 enabled. For wildcard certificates use edge or reencrypt termination.
Edge

There are two ways to achieve TLS Edge. In both it is needed to enable HTTP access.

Using cluster domain and certificate

To use cluster domain do not provide a tls secret for the ingress and ensure to use cluster domain as hostname. Since no certificate is provided to the ingress the cluster certificate is used.

apiVersion: k8s.keycloak.org/v2alpha1
kind: Keycloak
metadata:
  name: example-kc
spec:
  http:
    httpEnabled: true
  ingress:
    enabled: true
  hostname:
    hostname: keycloak.<cluster-domain> (1)
  proxy:
    headers: xforwarded (2)
  networkPolicy:
    enabled: true
    http: ... (3)
1 Must use the cluster domain.
2 Check your load balancer or reverse proxy configuration to determine whether it utilizes the Forwarded (RFC 7239) or X-Forwarded-* (e.g., X-Forwarded-For) mechanism for header propagation.
3 Restrict access to the ingress controller to avoid spoofing of forwarding headers.

Using custom domain and certificate

To use custom domain provide a tls secrets for the ingress and ensure that hostname matches the ingress certificate.

apiVersion: k8s.keycloak.org/v2beta1
kind: Keycloak
metadata:
  name: example-kc
spec:
  http:
    httpEnabled: true
  ingress:
    tlsSecret: example-tls-secret
  hostname:
    hostname: test.keycloak.org
  proxy:
    headers: xforwarded (1)
  networkPolicy:
    enabled: true
    http: ... (2)
1 Check your load balancer or reverse proxy configuration to determine whether it utilizes the Forwarded (RFC 7239) or X-Forwarded-* (e.g., X-Forwarded-For) mechanism for header propagation.
2 Restrict access to the ingress controller to avoid spoofing of forwarding headers.
Reencrypt

There are two ways to achieve TLS Reencryption. In both it is needed to annotate the ingress to specify the desired termination type since operator still lacks logic to set itself the reencrypt termination.

Using cluster domain and certificate

To use cluster domain do not provide a tls secret for the ingress and ensure to use cluster domain as hostname. Since no certificate is provided to the ingress the cluster certificate is used.

apiVersion: k8s.keycloak.org/v2alpha1
kind: Keycloak
metadata:
  name: example-kc
spec:
  http:
    annotations:
      service.alpha.openshift.io/serving-cert-secret-name: svc-tls-secret (1)
      tlsSecret: svc-tls-secret (2)
  ingress:
    annotations:
      route.openshift.io/termination: reencrypt (3)
    enabled: true
  hostname:
    hostname: keycloak.<cluster-domain> (4)
  proxy:
    headers: xforwarded (5)
  networkPolicy:
    enabled: true
    https: ... (6)
1 This will add the annotation to the service generated by the operator, so cluster will create a tls secret named svc-tls-secret
2 Name of the tls secret created by the cluster, as defined in <1>
3 Required because the operator only has built-in OpenShift logic for passthrough and edge.
4 Must use the cluster domain
5 Check your load balancer or reverse proxy configuration to determine whether it utilizes the Forwarded (RFC 7239) or X-Forwarded-* (e.g., X-Forwarded-For) mechanism for header propagation.
6 Restrict access to the ingress controller to avoid spoofing of forwarding headers.

Using custom domain and certificate

To use custom domain provide distinct tls secrets for the ingress and http and ensure that hostname matches the ingress certificate.

apiVersion: k8s.keycloak.org/v2alpha1
kind: Keycloak
metadata:
  name: example-kc
spec:
  http:
    annotations:
      service.alpha.openshift.io/serving-cert-secret-name: svc-tls-secret (1)
      tlsSecret: svc-tls-secret (2)
  ingress:
    annotations:
      route.openshift.io/termination: reencrypt (3)
    tlsSecret: ingress-tls-secret (4)
    enabled: true
  hostname:
    hostname: test.keycloak.org (5)
  proxy:
    headers: xforwarded (6)
  networkPolicy:
    enabled: true
    https: ... (7)
1 This will add the annotation to the service generated by the operator, so cluster will create a tls secret named svc-tls-secret.
2 Name of the tls secret created by the cluster, as defined in callout 1.
3 Required because the operator only has built-in OpenShift logic for passthrough and edge.
4 Certificate to be used at the ingress/route.
5 Domain must match the provided certificate.
6 Check your load balancer or reverse proxy configuration to determine whether it utilizes the Forwarded (RFC 7239) or X-Forwarded-* (e.g., X-Forwarded-For) mechanism for header propagation.
7 Restrict access to the ingress controller to avoid spoofing of forwarding headers.

Custom Access

If the default ingress does not fit your use case, disable it by setting ingress spec with enabled property to false value:

Edit YAML file example-kc.yaml:

apiVersion: k8s.keycloak.org/v2beta1
kind: Keycloak
metadata:
  name: example-kc
spec:
  ...
  ingress:
    enabled: false
  networkPolicy:
    enabled: true
    https: ... (1)
1 Restrict access to the ingress controller to avoid spoofing of forwarding headers.

Apply the changes:

kubectl apply -f example-kc.yaml

You can then provide an alternative ingress resource pointing to the service named <keycloak-cr-name>-service by default.

For debugging and development purposes, consider directly connecting to the Keycloak service using a port forward. For example, enter this command:

kubectl port-forward service/example-kc-service 8443:8443

Configuring the reverse proxy settings matching your Ingress Controller

The Operator supports configuring which of the reverse proxy headers should be accepted by server, which includes Forwarded and X-Forwarded-* headers.

If you Ingress implementation sets and overwrites either Forwarded or X-Forwarded-* headers, you can reflect that in the Keycloak CR as follows:

apiVersion: k8s.keycloak.org/v2beta1
kind: Keycloak
metadata:
  name: example-kc
spec:
  ...
  proxy:
    headers: forwarded|xforwarded
  networkPolicy:
    enabled: true
    https: ... (1)
1 restrict incoming connections to the ingress controller to avoid spoofing of forwarding headers.
If the proxy.headers field is not specified, the Operator falls back to legacy behavior by implicitly setting proxy=passthrough by default. This results in deprecation warnings in the server log. This fallback will be removed in a future release.
When using the proxy.headers field, make sure your Ingress properly sets and overwrites the Forwarded or X-Forwarded-* headers respectively. To set these headers, consult the documentation for your Ingress Controller. Consider configuring it for either reencrypt or edge TLS termination as passthrough TLS doesn’t allow the Ingress to modify the requests headers. Misconfiguration will leave Keycloak exposed to security vulnerabilities.

For more details refer to the Configuring a reverse proxy guide.

Accessing the Admin Console

When deploying Keycloak, the operator generates an arbitrary initial admin username and password and stores those credentials as a basic-auth Secret object in the same namespace as the CR.

Change the default admin credentials and enable MFA in Keycloak before going to production.

To fetch the initial admin credentials, you have to read and decode the Secret. The Secret name is derived from the Keycloak CR name plus the fixed suffix -initial-admin. To get the username and password for the example-kc CR, enter the following commands:

kubectl get secret example-kc-initial-admin -o jsonpath='{.data.username}' | base64 --decode
kubectl get secret example-kc-initial-admin -o jsonpath='{.data.password}' | base64 --decode

You can use those credentials to access the Admin Console or the Admin REST API.

Security Considerations

Anyone with the ability to create or edit Keycloak or KeycloakRealmImport CRs should be a namespace level admin.

Setting the Keycloak CR image requires a high degree of trust as whatever image is running will at least have access to any Secrets used for environment variables.

Similarly the unsupported podTemplate gives the ability to deploy alternative workloads which may be granted the same permissions as the operator itself - which includes the ability to access Secrets in the namespace.

On this page