Configure the CA truststore
What you’ll accomplish
Section titled “What you’ll accomplish”You’ll replace the default DoD CA certificate bundle in the uds-identity-config image with a custom CA bundle so that Keycloak can validate client certificates for X.509/CAC authentication in your environment. This requires building a custom uds-identity-config image.
Prerequisites
Section titled “Prerequisites”- UDS Core deployed
- Docker installed
- UDS CLI installed
- Custom CA certificates available
Before you begin
Section titled “Before you begin”The default uds-identity-config image includes DoD UNCLASS CA certificates, sourced at build time from a URL configured in the Dockerfile. To use your organization’s own CA chain, you must build a custom image with your certificates bundled in.
The truststore is a Java KeyStore (JKS) file generated by the ca-to-jks.sh script during the image build. The Istio gateway also needs to know your CA so it can request client certificates from browsers.
-
Clone the uds-identity-config repository
Terminal window git clone https://github.com/defenseunicorns/uds-identity-config.gitcd uds-identity-config -
Prepare your CA certificate zip file
Assemble your organization’s CA certificate chain into a zip file named
authorized_certs.zipand place it in thesrc/directory of the uds-identity-config repository. -
Build the Docker image with your CA certificates
The Dockerfile’s
CA_ZIP_URLbuild argument controls which certificate zip is used. The default points to a remote DoD CA URL — you must always override this argument to include your own certificates:Terminal window docker build \--build-arg CA_ZIP_URL=authorized_certs.zip \-t registry.example.com/uds/identity-config:1.0.0 \src/To exclude specific certificates from the generated truststore, also pass
CA_REGEX_EXCLUSION_FILTER:Terminal window docker build \--build-arg CA_ZIP_URL=authorized_certs.zip \--build-arg CA_REGEX_EXCLUSION_FILTER="<regex-pattern>" \-t registry.example.com/uds/identity-config:1.0.0 \src/ -
Create the Zarf package for airgap transport
Terminal window uds zarf package create src/ --confirm -
Extract the
tls.cacertvalue for the Istio gatewayThe Istio gateway needs your CA certificate to request client certs from browsers. Extract it from the built image:
Terminal window uds run dev-cacertThis generates a
tls_cacert.yamlfile locally containing the base64-encoded CA certificate value. -
Publish the image and configure the bundle override
Push the image built in the previous step to a registry your cluster can access.
For local testing only:
Terminal window docker build \--build-arg CA_ZIP_URL=authorized_certs.zip \-t ttl.sh/<your-unique-id>:1h \src/docker push ttl.sh/<your-unique-id>:1hIn your
uds-bundle.yaml, setconfigImageto the custom image and apply thetls.cacertvalue from the generated file:uds-bundle.yaml packages:- name: corerepository: registry.defenseunicorns.com/public/coreref: x.x.x-upstreamoverrides:keycloak:keycloak:values:- path: configImagevalue: ttl.sh/<your-unique-id>:1h # or registry.example.com/uds/identity-config:1.0.0 for productionistio-tenant-gateway:uds-istio-config:values:- path: tls.cacertvalue: "<base64-encoded-ca-cert-from-tls_cacert.yaml>" -
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 CA truststore and Istio gateway are configured correctly:
# Verify the gateway is advertising your CA as a trusted issuer# Look for "Acceptable client certificate CA names" in the outputopenssl s_client -connect sso.<domain>:443The Acceptable client certificate CA names section in the output should list your CA’s subject name.
Check the Keycloak init container used your image:
uds zarf tools kubectl get pod -n keycloak -l app.kubernetes.io/name=keycloak \ -o jsonpath='{.items[0].spec.initContainers[0].image}'The output should match your custom image reference.
Troubleshooting
Section titled “Troubleshooting”Problem: ca-to-jks.sh script fails during image build
Section titled “Problem: ca-to-jks.sh script fails during image build”Symptoms: The Docker build fails with an error from the ca-to-jks.sh script.
Solution: Verify your authorized_certs.zip file is in the src/ directory (the directory containing the Dockerfile), not the repository root. Check that the zip file is valid and not corrupted:
unzip -t src/authorized_certs.zipProblem: Browser is not prompted for a client certificate
Section titled “Problem: Browser is not prompted for a client certificate”Symptoms: The login page loads but does not request a CAC/PIV certificate from the browser.
Solution: Two checks:
- Confirm the
tls.cacertoverride was applied toistio-tenant-gatewayand that the bundle was redeployed - Confirm
X509_AUTH_ENABLED: trueis set inrealmAuthFlows— if X.509 auth is disabled, the gateway will not request client certs even if the truststore is configured. See Configure authentication flows.
Problem: Certificate authentication succeeds but OCSP errors appear in logs
Section titled “Problem: Certificate authentication succeeds but OCSP errors appear in logs”Symptoms: X.509 login works but Keycloak logs show OCSP revocation check failures.
Solution: In airgapped or restricted environments, the OCSP responder may be unreachable. Configure fail-open behavior or disable OCSP:
- path: realmInitEnv value: X509_OCSP_FAIL_OPEN: "true"Related Documentation
Section titled “Related Documentation”- Keycloak: X.509 client certificate user authentication — upstream reference for X.509/CAC authentication configuration in Keycloak
- uds-identity-config repository — source repository with Dockerfile,
ca-to-jks.sh, and task definitions
Next steps
Section titled “Next steps”These guides and concepts may be useful to explore next: