Configure Keycloak Airgap CRLs
What you’ll accomplish
Section titled “What you’ll accomplish”You’ll configure Keycloak to validate X.509/CAC certificates against locally loaded Certificate Revocation Lists (CRLs) in an airgapped environment where OCSP responders are unreachable. This involves building an OCI data image containing the CRL files, wrapping it in a Zarf package, and configuring the bundle to mount those files into the Keycloak pod at deploy time.
Prerequisites
Section titled “Prerequisites”- UDS CLI installed
- UDS Registry account created and authenticated locally with a read token
- Docker installed (on the machine where you run the packaging script)
bash,curl,unzip,find, andsortavailable on the machine running the script- Access to a Kubernetes cluster running Kubernetes 1.31+
- X.509/CAC authentication enabled in UDS Core (see Configure Keycloak authentication methods and Configure the CA truststore)
Before you begin
Section titled “Before you begin”In connected environments, Keycloak uses OCSP to check whether a client certificate has been revoked. In a true airgap, OCSP responders are unreachable. The supported alternative is to load CRL files directly into the Keycloak pod so revocation checks can run locally.
This guide uses a Kubernetes ImageVolume to mount an OCI image containing the CRL files into the Keycloak pod. No custom Keycloak image is required.
Kubernetes version requirements:
| Kubernetes version | ImageVolume support |
|---|---|
| 1.31–1.34 | Supported, but the ImageVolume feature gate must be explicitly enabled on the API server and kubelet |
| 1.35+ | Enabled by default; no feature gate configuration needed |
-
Build the CRL Zarf package
Run the packaging script from the UDS Core repo root on a connected machine (or inside the enclave if you are supplying a pre-downloaded ZIP). The script fetches or accepts CRL files, builds an OCI data image from them, generates the Keycloak CRL path string, and creates a Zarf package.
Download CRLs from DISA and build the package (default):
Terminal window bash scripts/keycloak-crl-airgap/create-keycloak-crl-oci-volume-package.shUse a pre-downloaded ZIP:
Terminal window bash scripts/keycloak-crl-airgap/create-keycloak-crl-oci-volume-package.sh \--crl-zip /path/to/crls.zipUse this option when you have already downloaded the CRL ZIP (e.g., on a connected machine before transferring into an airgap) or when you want to supply a custom set of CRLs instead of the default DISA ones.
The script excludes DoD Email (
DODEMAIL*) and Software (DODSW*) CRLs by default. To include them:Terminal window # Include DoD Email CRLsbash scripts/keycloak-crl-airgap/create-keycloak-crl-oci-volume-package.sh --include-email# Include DoD Software CRLsbash scripts/keycloak-crl-airgap/create-keycloak-crl-oci-volume-package.sh --include-swWhen the script completes, you will have two outputs under
./keycloak-crls/:keycloak-crl-paths.txt: the##-delimited CRL path string to paste into your bundle configzarf-package-keycloak-crls-<arch>-<tag>.tar.zst: the Zarf package to add to your bundle
-
Configure Keycloak overrides in your bundle
Add the following to your
uds-bundle.yamlunder the Keycloak package overrides. Paste the contents ofkeycloak-crl-paths.txtas the value forX509_CRL_RELATIVE_PATH.uds-bundle.yaml packages:- name: corerepository: registry.defenseunicorns.com/public/coreref: x.x.x-upstreamoverrides:keycloak:keycloak:values:- path: realmInitEnvvalue:X509_OCSP_CHECKING_ENABLED: "false"X509_OCSP_FAIL_OPEN: "false"X509_CRL_CHECKING_ENABLED: "true"X509_CRL_ABORT_IF_NON_UPDATED: "false"X509_CRL_RELATIVE_PATH: "<paste keycloak-crl-paths.txt contents here>"- path: extraVolumesvalue:- name: ca-certsconfigMap:name: uds-trust-bundleoptional: true- name: keycloak-crlsimage:reference: keycloak-crls:localpullPolicy: Always- path: extraVolumeMountsvalue:- name: ca-certsmountPath: /tmp/ca-certsreadOnly: true- name: keycloak-crlsmountPath: /tmp/keycloak-crlsreadOnly: true -
Add the CRL package to your bundle and set deployment order
The CRL Zarf package must deploy before the Keycloak package so the CRL image is available in the cluster registry when Keycloak starts.
uds-bundle.yaml packages:- name: core-baseref: x.x.x- name: keycloak-crlspath: ./keycloak-crls/zarf-package-keycloak-crls-<arch>-<tag>.tar.zstref: x.x.x- name: core-identity-authorizationref: x.x.x -
Create and deploy your bundle
Terminal window uds create <path-to-bundle-dir>uds deploy uds-bundle-<name>-<arch>-<version>.tar.zst
Verification
Section titled “Verification”Confirm the CRL Zarf package was deployed:
uds zarf package list | grep keycloak-crlsConfirm CRL files are mounted in the Keycloak pod:
uds zarf tools kubectl exec -n keycloak keycloak-0 -c keycloak -- ls -la /tmp/keycloak-crlsThe listed files should match the CRL filenames from keycloak-crl-paths.txt.
Confirm the CRL path configuration:
In the Keycloak admin console at keycloak.<admin_domain> → uds realm → Authentication → Flows → UDS Authentication → X509/Validate Username Form settings, verify the CRL Distribution Points value matches the contents of keycloak-crl-paths.txt.
Test X.509 authentication:
Use your normal mTLS or browser client certificate flow and confirm Keycloak validates the certificate without CRL-related errors in the logs.
Troubleshooting
Section titled “Troubleshooting”Problem: “Volume has a disallowed volume type of ‘image’”
Section titled “Problem: “Volume has a disallowed volume type of ‘image’””Symptom: The Keycloak pod fails to start with a policy violation error referencing image volume type.
Solution: image volumes are allowed by default in UDS Core 1.1.0 and later. If you are running an older version, add a RestrictVolumeTypes Exemption targeting Keycloak pods. See Configure infrastructure exemptions for the exemption format.
Problem: “Failed to pull image … not found”
Section titled “Problem: “Failed to pull image … not found””Symptom: The Keycloak pod fails to start because the CRL image cannot be pulled.
Solution: The CRL Zarf package is missing or the image reference is incorrect. Verify:
- The
keycloak-crlspackage is listed beforecore-identity-authorizationin the bundle and was deployed successfully (uds zarf package list | grep keycloak-crls) - The
extraVolumes.image.referencevalue (keycloak-crls:local) matches the image reference available in the cluster’s Zarf registry
Problem: Keycloak logs show “Unable to load CRL from …”
Section titled “Problem: Keycloak logs show “Unable to load CRL from …””Symptom: X.509 authentication fails and Keycloak logs contain CRL loading errors.
Solution: Verify:
- CRL files exist in the Keycloak container at
/tmp/keycloak-crls(see verification step above) - The value of
X509_CRL_RELATIVE_PATHexactly matches the contents ofkeycloak-crl-paths.txt, including the##delimiters between paths - The CRLs are not expired. Check each file’s
nextUpdatefield withopenssl crl -inform DER -in <file.crl> -noout -nextupdate.
Related documentation
Section titled “Related documentation”- Keycloak: X.509 client certificate user authentication - upstream Keycloak reference for X.509 authenticator configuration
- Kubernetes ImageVolume documentation - upstream reference for OCI image-backed volumes
- Configure Keycloak authentication methods - Enable or disable X.509/CAC, OCSP, and CRL revocation checking via bundle overrides.
- Configure the CA truststore - Replace the default DoD CA bundle with a custom certificate authority for X.509/CAC authentication.