Skip to content
Unified Defense StackUnified Defense Stack
You're viewing docs for v1.5. Go to the latest

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
  • UDS Registry account created and authenticated locally with a read token
  • 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, so 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"