All Products
Search
Document Center

Container Service for Kubernetes:Use Keycloak as an OIDC provider to verify identities

Last Updated:Mar 26, 2026

This guide walks you through deploying Keycloak in a Container Service for Kubernetes (ACK) managed cluster and configuring it as an OpenID Connect (OIDC) identity provider for the Kubernetes API server. After completing this guide, Kubernetes can authenticate users against Keycloak and enforce RBAC authorization based on their identity.

How it works

The OIDC authentication flow involves three parties: the user, Keycloak (the identity provider), and the Kubernetes API server.

User → Keycloak:      1. Log in and authenticate
Keycloak → User:      2. Return access_token, id_token, and refresh_token
User → kubectl:       3. Call kubectl with --token=<id_token>
kubectl → API server: 4. Authorization: Bearer <id_token>
API server:           5. Verify JWT signature
API server:           6. Check token expiry (iat + exp)
API server:           7. Evaluate RBAC authorization
API server → kubectl: 8. Return result

Understanding this flow helps explain each configuration step:

  • Keycloak realm — the tenant boundary that isolates users, clients, and scopes

  • Keycloak client — represents the API server as a registered OIDC application

  • Client scope and mapper — controls which user attributes are embedded in the ID token

  • API server OIDC parameters — tells the API server which issuer to trust and which claim to use as the Kubernetes username

Prerequisites

Before you begin, make sure you have:

Set up the environment

Prepare a domain name

Prepare a domain name for the Keycloak Service. You will use this domain name throughout the setup.

Generate TLS certificates

To secure Keycloak in a production environment, generate a root certificate and a server certificate. The server certificate must include the Keycloak Service domain name as a Subject Alternative Name (SAN).

# Generate a CA (Certificate Authority) key
openssl genrsa -out rootCA.key 2048

# Generate a CA certificate (valid for 10 years)
openssl req -x509 -new -nodes -key rootCA.key -sha256 -days 3650 -out rootCA.crt

# Generate a server key
openssl genrsa -out server.key 2048

# Create a Certificate Signing Request (CSR)
openssl req -new -key server.key -out server.csr

# Generate the server certificate with the Keycloak domain as SAN
openssl x509 -req -in server.csr -CA rootCA.crt -CAkey rootCA.key -CAcreateserial \
  -out server.crt -days 3650 -sha256 -extensions v3_req \
  -extfile <(echo '[v3_req]'; echo 'subjectAltName = DNS:${Service domain name}')

Prepare a database

By default, Keycloak persists data to local files. For production environments, use an external database. Keycloak supports the following database engines:

Database engineOption valueVersion
MariaDB Servermariadb10.11
Microsoft SQL Servermssql2022
MySQLmysql8.0
Oracle Databaseoracle19.3
PostgreSQLpostgres15

This guide uses ApsaraDB RDS for MySQL with a database named Keycloak. See Create an ApsaraDB RDS for MySQL instance.

Step 1: Deploy Keycloak in the ACK cluster

Deploy five Kubernetes resources to run Keycloak: a Service, two Secrets, an Ingress, and a Deployment.

1.1 Create the Keycloak Service

Create keycloak.yaml with the following content:

apiVersion: v1
kind: Service
metadata:
  labels:
    app: keycloak
  name: keycloak
  namespace: default
spec:
  ports:
    - name: http
      port: 80
      protocol: TCP
      targetPort: 8080
  selector:
    app: keycloak
  type: ClusterIP

Apply the Service:

kubectl apply -f keycloak.yaml

1.2 Create the credentials Secret

Create keycloak-secret.yaml. This Secret stores Base64-encoded database credentials and the initial Keycloak admin account.

apiVersion: v1
data:
  db_passwd: ${Database password encoded by Base64}
  db_username: ${Database username encoded by Base64}
  db_url: ${Database URL or host encoded by Base64}
  keycloak_admin: ${Keycloak administrator username encoded by Base64}
  keycloak_admin_password: ${Keycloak administrator password encoded by Base64}
kind: Secret
metadata:
  name: keycloak-secret
  namespace: default
type: Opaque

Apply the Secret:

kubectl apply -f keycloak-secret.yaml

1.3 Create the TLS Secret

Create keycloak-pki.yaml with the server certificate and key generated in Generate TLS certificates:

apiVersion: v1
data:
  tls.crt: ${Server certificate encoded by Base64}
  tls.key: ${Server key encoded by Base64}
kind: Secret
metadata:
  name: keycloak-pki
  namespace: default
type: IngressTLS

Apply the TLS Secret:

kubectl apply -f keycloak-pki.yaml

1.4 Create the Ingress

Create keycloak-ingress.yaml. Replace ${Service domain name} with the domain name you prepared.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  labels:
    ingress-controller: nginx
  name: keycloak
  namespace: default
spec:
  ingressClassName: nginx
  rules:
    - host: ${Service domain name}
      http:
        paths:
          - backend:
              service:
                name: keycloak
                port:
                  number: 80
            path: /
            pathType: ImplementationSpecific
  tls:
    - hosts:
        - ${Service domain name}
      secretName: keycloak-pki

Apply the Ingress:

kubectl apply -f keycloak-ingress.yaml

After the Ingress is created, a public IP address is assigned. Point your domain name DNS to this IP address.

1.5 Deploy Keycloak

Create keycloak-deploy.yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: keycloak
  name: keycloak
  namespace: default
spec:
  selector:
    matchLabels:
      app: keycloak
  template:
    metadata:
      labels:
        app: keycloak
    spec:
      containers:
        - args:
            - start-dev
          env:
            - name: KEYCLOAK_ADMIN
              valueFrom:
                secretKeyRef:
                  key: keycloak_admin
                  name: keycloak-secret
            - name: KEYCLOAK_ADMIN_PASSWORD
              valueFrom:
                secretKeyRef:
                  key: keycloak_admin_password
                  name: keycloak-secret
            - name: KC_PROXY
              value: edge
            - name: KC_DB_URL_HOST
              valueFrom:
                secretKeyRef:
                  key: db_url
                  name: keycloak-secret
            - name: KC_DB
              value: mysql
            - name: KC_DB_USERNAME
              valueFrom:
                secretKeyRef:
                  key: db_username
                  name: keycloak-secret
            - name: KC_DB_PASSWORD
              valueFrom:
                secretKeyRef:
                  key: db_passwd
                  name: keycloak-secret
          image: 'quay.io/keycloak/keycloak:latest'
          imagePullPolicy: Always
          name: keycloak
          ports:
            - containerPort: 8080
              name: http
              protocol: TCP
          readinessProbe:
            httpGet:
              path: /realms/master
              port: 8080

Apply the Deployment:

kubectl apply -f keycloak-deploy.yaml

Verify Keycloak is running

Open https://${Keycloak Service domain name} in a browser. If the Keycloak welcome page appears, the deployment is successful.

123.png

Step 2: Configure Keycloak

User settings

  1. On the Keycloak welcome page, click Administration Console and log in with the admin credentials from keycloak-secret.yaml.

  2. In the upper-left corner, open the realm drop-down list and click Create Realm.

    123.png

  3. Set Realm name to myrealm and click Create.

  4. Switch to the myrealm realm. In the left navigation pane, click Users, then click Add user.

    user.png

  5. Set Username to myuser and click Create.

  6. On the myuser page, click Attributes. Add an attribute with Key name and Value ack, then click Save. This attribute will be injected into the ID token.

    345.png

  7. Click Credentials, then click Set Password. Set Temporary to On and click Save. The user must change their password on first login. Users can log in to their Keycloak account at https://${Keycloak Service domain name}/realms/${Your realm}/account.

    22.png

Client settings

  1. In the left navigation pane, click Clients, then click Create client.

  2. Complete the three-step client creation wizard:

    1. General settings: Set both Client ID and Name to ack, then click Next. general.png

    2. Capability config: Set Client authentication to On (this makes the client confidential), keep other defaults, and click Next. 123.png

    3. Login settings: Set Valid redirect URIs to http://* to allow all HTTP redirect URIs, then click Save. image.png

Client scope settings

Client scopes let you share protocol mappings across clients. Set up a scope that injects the user's name attribute into the ID token.

  1. In the left navigation pane, click Client scopes, then click Create client scope.

  2. Set Name to ack-kubernetes, keep other defaults, and click Save.

    image.png

  3. On the ack-kubernetes page, click Mappers, then click Configure a new mapper.

    image.png

  4. On the Add mapper page, configure the mapper to inject the name: ack attribute into the ID token: Keep other defaults and click Save.

    • Name: name

    • User Attribute: name

    • Token Claim Name: name

    image.png

  5. In the left navigation pane, click Clients and select the ack client.

  6. Click the Client scopes tab, then click Add client scope. Select ack-kubernetes and click Add.

    image.png

Step 3: Configure the Kube API Server OIDC parameters

Before you configure the API server, gather the following values from Keycloak:

  • OIDC issuer URL: Visit https://${Keycloak Service domain name}/realms/myrealm/.well-known/openid-configuration and copy the issuer field. The URL is https://${Keycloak Service domain name}/realms/myrealm.

  • Client ID: ack (configured in Client settings).

  • CA certificate: The Base64-encoded rootCA.crt generated in Generate TLS certificates.

The Kubernetes API server supports OIDC natively. Configure the following parameters to point it to the Keycloak issuer.

  1. Log in to the ACK console. In the left navigation pane, click Clusters.

  2. Click the cluster name. In the left navigation pane, click Add-ons.

  3. On the Core Components tab, find Kube API Server and click Configuration.

  4. In the Kube API Server Parameters dialog box, set the following parameters and click OK.

    Important

    The API server must be able to reach the oidcIssuerURL. If the cluster does not have internet access, enable it first. See Enable an existing ACK cluster to access the Internet. If the API server still cannot reach the URL after enabling internet access, run kubectl get endpoints to check the number of backend IP addresses: - If there are multiple IP addresses, log in to a worker node and test connectivity to the oidcIssuerURL. Check internet access and security group rules. - If there is only one IP address, submit a ticketsubmit a ticket.

    ParameterDescriptionExampleRequired
    oidcIssuerURL (OIDC provider URL)The OIDC issuer URL. The API server uses this URL to discover public signing keys and validate tokens.https://${Keycloak Service domain name}/realms/myrealmYes
    oidcClientId (OIDC Token Client ID)The client ID registered in Keycloak.ackYes
    oidcUsernameClaim (Username JWT claim)The JWT claim to use as the Kubernetes username. Must match the Token Claim Name configured in the client scope mapper.nameNo
    oidcUsernamePrefix (Username prefix)A prefix prepended to the username claim to avoid conflicts with existing Kubernetes users. Enter - to disable prefixing.-No
    oidcCAContent (CA certificate)The Base64-encoded root certificate generated in Generate TLS certificates. The API server uses this to verify the Keycloak TLS certificate.Base64-encoded rootCA.crtNo

    API server.png

Step 4: Verify the authentication flow

Get an ID token

Run the following command to request an ID token from Keycloak:

curl -ks -X POST https://${Keycloak Service domain name}/realms/myrealm/protocol/openid-connect/token \
  -d grant_type=password \
  -d client_id=ack \
  -d username=myuser \
  -d password=${myuser password} \
  -d scope=openid \
  -d client_secret=${client secret}

Replace the following placeholders:

PlaceholderValue
${Keycloak Service domain name}The domain name of your Keycloak Service
${myuser password}The password set for myuser in User settings
${client secret}In the Keycloak console, go to Clients > ack > Credentials and copy the Client secret value

The response body contains the id_token field. Copy that value.

(Optional) Parse the ID token

Use jwt-cli to decode the token and confirm the claims:

jwt decode <id_token>

The output should show iss matching your oidcIssuerURL and name set to ack — the user attribute injected by the client scope mapper.

Set up RBAC and call the API server

Create a ClusterRole and ClusterRoleBinding that grant the ack user read access to namespaces:

kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: keycloak-example
rules:
  - apiGroups: [""]
    resources: ["namespaces"]
    verbs: ["get", "list"]
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: ack-crb
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: keycloak-example
subjects:
  - kind: User
    name: ack
    apiGroup: rbac.authorization.k8s.io

Call the API server with the ID token:

curl -k https://${API server address}/api/v1/namespaces \
  -H "Authorization: Bearer ${id_token}"

Replace ${API server address} with the public or internal endpoint of your cluster. Find these in the ACK console under Cluster Information > Basic Information > Network: API server Public Endpoint or API server Internal Endpoint.

The API server returns the list of namespaces, confirming that OIDC authentication is working.

image.png

To verify that role-based access control (RBAC) authorization also works, request a resource the ack user is not authorized to access. The API server identifies the user and denies the request, confirming that both authentication and authorization are enforced.

image.png