Skip to content

Configure the CA truststore

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.

  • UDS Core deployed
  • Docker installed
  • UDS CLI installed
  • Custom CA certificates available

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.

  1. Clone the uds-identity-config repository

    Terminal window
    git clone https://github.com/defenseunicorns/uds-identity-config.git
    cd uds-identity-config
  2. Prepare your CA certificate zip file

    Assemble your organization’s CA certificate chain into a zip file named authorized_certs.zip and place it in the src/ directory of the uds-identity-config repository.

  3. Build the Docker image with your CA certificates

    The Dockerfile’s CA_ZIP_URL build 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/
  4. Create the Zarf package for airgap transport

    Terminal window
    uds zarf package create src/ --confirm
  5. Extract the tls.cacert value for the Istio gateway

    The Istio gateway needs your CA certificate to request client certs from browsers. Extract it from the built image:

    Terminal window
    uds run dev-cacert

    This generates a tls_cacert.yaml file locally containing the base64-encoded CA certificate value.

  6. 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>:1h

    In your uds-bundle.yaml, set configImage to the custom image and apply the tls.cacert value from the generated file:

    uds-bundle.yaml
    packages:
    - name: core
    repository: registry.defenseunicorns.com/public/core
    ref: x.x.x-upstream
    overrides:
    keycloak:
    keycloak:
    values:
    - path: configImage
    value: ttl.sh/<your-unique-id>:1h # or registry.example.com/uds/identity-config:1.0.0 for production
    istio-tenant-gateway:
    uds-istio-config:
    values:
    - path: tls.cacert
    value: "<base64-encoded-ca-cert-from-tls_cacert.yaml>"
  7. Create and deploy your bundle

    Terminal window
    uds create <path-to-bundle-dir>
    uds deploy uds-bundle-<name>-<arch>-<version>.tar.zst

Confirm the CA truststore and Istio gateway are configured correctly:

Terminal window
# Verify the gateway is advertising your CA as a trusted issuer
# Look for "Acceptable client certificate CA names" in the output
openssl s_client -connect sso.<domain>:443

The Acceptable client certificate CA names section in the output should list your CA’s subject name.

Check the Keycloak init container used your image:

Terminal window
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.

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:

Terminal window
unzip -t src/authorized_certs.zip

Problem: 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:

  1. Confirm the tls.cacert override was applied to istio-tenant-gateway and that the bundle was redeployed
  2. Confirm X509_AUTH_ENABLED: true is set in realmAuthFlows — 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"

These guides and concepts may be useful to explore next: