admin-guide

Administrator guide

Keycloak Init runs init.py continuously. It requires an existing Keycloak server, Kubernetes access, and two Secrets in the configured namespace. Prepare initialization files using the user guide.

Prerequisites and configuration

  • An accessible Keycloak base URL, including any deployment-specific path prefix.
  • A working admin account in Keycloak's master realm with permission to list and create realms and manage the target realm's objects.
  • A Kubernetes namespace containing kcadmin and initdata.
  • Kubernetes API discovery access and permission to get those two Secrets.
  • A container image built from this project, available to the cluster.

The Dockerfile installs Python, the Kubernetes Python client, and python-keycloak, and starts python3 -u /init.py. Its base image and Python dependencies are unpinned; this repository does not declare a tested version matrix. Validate a chosen image with your Keycloak deployment before promotion. The CI configuration delegates image building to an external GitLab template.

Environment variable Required / default Meaning
KEYCLOAK_URL Required Keycloak base URL; used for readiness checks and admin API calls
NAMESPACE Required Namespace of both Secrets; not inferred from the Pod namespace
REALM_NAME Default master Target realm; authentication always starts in master
KEYCLOAK_VERIFY_SSL Default false Case-insensitive 1, true, yes, or on enables certificate verification; other values disable it
KUBERNETES_PORT Set by Kubernetes in normal in-cluster use Its presence selects in-cluster authentication; otherwise the script loads kubeconfig

Set KEYCLOAK_VERIFY_SSL=true when the Keycloak certificate is trusted by the container. The default disables verification for both readiness and admin requests and suppresses insecure-request warnings. There is no dedicated application setting for a CA file; arrange certificate trust in the runtime if using a private CA with verification enabled.

Admin credentials are read only from kcadmin. The service has no environment variables for overriding the username/password, Secret names, or retry intervals.

Provision the Secrets

Use the same namespace in these commands and in the Deployment. Examples assume that namespace keycloak already exists.

kcadmin

Create an Opaque Secret with UTF-8 username and password values matching an existing, usable admin account in Keycloak's master realm. This Secret does not itself provision the Keycloak account. Coordinate initial account setup with your Keycloak deployment.

For example, place the exact credentials in protected files tmp/kcadmin/username and tmp/kcadmin/password without trailing newlines, then run from the project root:

chmod 700 tmp tmp/kcadmin
chmod 600 tmp/kcadmin/username tmp/kcadmin/password
kubectl -n keycloak create secret generic kcadmin \
  --from-file=username=tmp/kcadmin/username \
  --from-file=password=tmp/kcadmin/password \
  --dry-run=client -o yaml | kubectl apply -f -

Pre-create this Secret. If it is absent, the code attempts to create random credentials but does not create a matching Keycloak account. That branch also fails to return the credentials, so the caller raises a TypeError when unpacking the result. Restarting alone does not make the generated credentials valid in Keycloak. Replace them with the credentials of a working admin account.

A malformed existing kcadmin Secret can trigger the same unpacking error: the read/decode exception is swallowed and no credentials are returned.

initdata

Create the Secret using the four-file command in the user guide. Include every key, using [] for unused types:

  • clients.json
  • users.json
  • federated_auth.json
  • federated_mappers.json

kubectl --from-file handles Secret encoding. If maintaining a manifest by hand, use literal JSON strings under stringData, or base64-encoded JSON under data. Keep credentials and manifests containing them out of version control.

Deploy in Kubernetes

The following example assumes both Secrets already exist. It grants access only to read those Secrets; the automatic Secret-creation branch is unnecessary in this setup. Standard Kubernetes API discovery permissions must also be available to the service account.

Save this manifest locally, replace the image tag with an available project image and adjust the Keycloak URL and realm, then apply it. Configure registry pull credentials in your cluster if required. The registry path below follows this project's GitLab remote; REPLACE_WITH_IMAGE_TAG is a placeholder.

apiVersion: v1
kind: ServiceAccount
metadata:
  name: keycloak-init
  namespace: keycloak
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: keycloak-init
  namespace: keycloak
rules:
  - apiGroups: [""]
    resources: ["secrets"]
    resourceNames: ["kcadmin", "initdata"]
    verbs: ["get"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: keycloak-init
  namespace: keycloak
subjects:
  - kind: ServiceAccount
    name: keycloak-init
    namespace: keycloak
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: keycloak-init
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: keycloak-init
  namespace: keycloak
spec:
  replicas: 1
  selector:
    matchLabels:
      app: keycloak-init
  template:
    metadata:
      labels:
        app: keycloak-init
    spec:
      serviceAccountName: keycloak-init
      containers:
        - name: keycloak-init
          image: registry.gitlab.com/tak-os-platform/tak-os-containers/keycloak-init:REPLACE_WITH_IMAGE_TAG
          env:
            - name: KEYCLOAK_URL
              value: "https://keycloak.example.com"
            - name: NAMESPACE
              valueFrom:
                fieldRef:
                  fieldPath: metadata.namespace
            - name: REALM_NAME
              value: "example"
            - name: KEYCLOAK_VERIFY_SSL
              value: "true"

Use a Deployment because the process never exits after success; a Job would remain running. The initializer exposes no HTTP service or health endpoint. Keep one replica to reduce concurrent create attempts; there is no leader election, and overlapping instances can race on existence checks.

kubectl apply -f keycloak-init.yaml
kubectl -n keycloak logs -f deployment/keycloak-init

No data volume is needed: Secrets are fetched through the Kubernetes API rather than read from mounted files.

Run outside the cluster

For development, use a Python environment with requests, urllib3, python-keycloak, and kubernetes installed. Use Python 3.10 or newer for the source's type annotations. Supply a kubeconfig whose current context can access the Secrets and ensure KUBERNETES_PORT is unset:

export KEYCLOAK_URL=https://keycloak.example.com
export NAMESPACE=keycloak
export REALM_NAME=example
export KEYCLOAK_VERIFY_SSL=true
python3 -u init.py

This contacts real services and applies changes; it is not a dry run. The script has no dry-run mode. The existing test.sh also runs against a configured Keycloak endpoint; it is a manual container helper, not an automated test suite. Its /data mount is unused by the current implementation.

Runtime behavior and verification

Each pass loads Kubernetes configuration, reads admin credentials, and waits for GET <KEYCLOAK_URL>/realms/master to return HTTP 200. Each request has a five-second timeout, with a five-second wait between unsuccessful checks and no overall deadline. The initializer then authenticates in master, reads initdata, and creates missing objects in this order:

  1. Target realm, with enabled: false if newly created.
  2. Clients.
  3. Users and any requested admin assignments.
  4. Identity providers.
  5. Identity provider mappers.
  6. An update to the user matching the bootstrap admin username in the target realm, if found: enabled, email-verified, with the kcadmin password marked non-temporary.

The final update runs on every successful pass and searches REALM_NAME, even though authentication used master. A same-named account in a different target realm can therefore have its password overwritten with the bootstrap password. Avoid such name collisions and account for this behavior when rotating passwords.

Success is followed by a one-hour sleep. Failures returned as false (including login failure or an unreadable initdata) cause a retry after 30 seconds. Uncaught exceptions, such as invalid JSON or many API errors, terminate the process; the Deployment restarts the container. There is no transaction or rollback, so objects created before an error remain.

Verify both logs and Keycloak state:

  • Confirm Keycloak configuration completed successfully. appears in logs.
  • Inspect the expected clients, users, providers, and mappers in the target realm.
  • Review any skipped mapper, role-assignment, or admin-update errors. These can be logged without preventing the final success message.
  • Review and enable a newly created realm in Keycloak before testing sign-in.
  • Test the intended application's login and any requested admin permissions.

Changes, rotation, and recovery

After updating initdata, wait for the next hourly pass or trigger an earlier read with:

kubectl -n keycloak rollout restart deployment/keycloak-init
kubectl -n keycloak logs -f deployment/keycloak-init

Only additions are applied. Editing or removing an existing entry does not change or delete the corresponding Keycloak object. Make those changes through your Keycloak administration process. The bootstrap admin update described above is an exception to this create-only behavior.

For admin credential rotation, coordinate the change in Keycloak with an update to kcadmin, then restart or wait for the next pass. The Secret must match the account used for authentication; changing it alone is not a password rotation mechanism. An already authenticated pass can still use its old credential values for the final admin update, so stop the initializer while coordinating rotation if needed. Protect backups of both initialization data and Keycloak state.

Stop further initialization by scaling the Deployment to zero:

kubectl -n keycloak scale deployment/keycloak-init --replicas=0

This leaves Keycloak objects and Secrets in place. Scale back to one when ready. Restoring an older initdata does not undo objects already created.

Troubleshooting

Symptom Check / action
KeyError: 'KEYCLOAK_URL' or 'NAMESPACE' Set both required environment variables.
Kubernetes configuration error In a Pod, check service account configuration; locally, check kubeconfig and current context. Presence of KUBERNETES_PORT forces in-cluster loading.
Forbidden access to Secrets Check namespace, service account, RoleBinding, named Secret get permissions, and API discovery access. A failed kcadmin read can lead to a subsequent create attempt.
TypeError while unpacking admin credentials Pre-create or repair kcadmin with both valid encoded keys and credentials matching a real master-realm admin. The missing-Secret creation path does not return credentials.
Repeated Keycloak not ready yet Check network/DNS, base URL and path prefix, certificate trust, and whether /realms/master returns 200. The loop hides request exceptions.
Failed to log in to Keycloak Check kcadmin against the master-realm account and ensure it can authenticate without interactive setup.
No initdata secret found. Check existence and read access in NAMESPACE; the same message is used for any exception reading the Secret.
JSON decoding error or container restarts Include all four keys, valid UTF-8 JSON arrays, and correct Secret encoding. Check kubectl -n keycloak logs deployment/keycloak-init --previous.
An object is reported as already existing Matching objects are skipped. Edit existing objects through Keycloak administration.
New realm cannot be used for sign-in Newly created realms are disabled; review and enable the realm in Keycloak.
Admin role missing after user creation Check logs and the exact admin role lookup described in the user guide. Assign the intended role manually; later passes skip that user.
Admin user '…' not found. The final bootstrap update searches the target realm. A master-only admin will not be found in a different target realm.
Success logged but configuration is incomplete Inspect warnings and actual objects; mapper skips and caught role/admin-update errors do not fail the pass.

For partial failures, fix the cause and restart or allow a retry. Inspect earlier creations before replaying: ordinary objects will be skipped, while any failed admin role assignment on an already-created user needs separate correction.