Skip to content

benefits-mgmt — Dockerfile runtime-base patch#

This page documents a cross-repo follow-up. It belongs in E:\Source\BCS Benefits\benefits-mgmt, not in CommandCenter — but it is tracked here because it pairs with the work landed on 261-finalize-bff-hardening (CA-trust + mTLS for BFF → BMO CC).

Context#

CommandCenter's runtime images now bake the BCS internal CA chain into /usr/local/share/ca-certificates/ via a shared runtime-base Dockerfile stage (see Dockerfile and docker/ca/README.md). The BFF uses this trust path to authenticate to BMO CC over mTLS without DangerousAcceptAnyServerCertificate.

For the channel to be mutually trusted, BMO CC must trust the same CA chain so it can validate the BFF's client certificate when the BFF presents one. That requires the same runtime-base pattern in benefits-mgmt's Dockerfile.

Files to change in benefits-mgmt#

File Change
src/BenefitManager.CommandCenter/Dockerfile Insert a runtime-base stage that installs *.crt from docker/ca/ into the trust bundle, and have the runtime stage(s) inherit from it.
docker/ca/ New folder with .gitkeep, .gitignore (block private keys + ignore-by-default for dev certs), and README.md.
.gitignore Add .dev-certs/ if not already present.

The CommandCenter repo's docker/ca/.gitignore and docker/ca/README.md can be copied verbatim — same convention applies.

Patch sketch#

In src/BenefitManager.CommandCenter/Dockerfile, before the existing runtime/final stage:

# --- Runtime Base ---
# Installs the BCS internal CA chain so BMO CC can validate client certs
# issued from the same chain (e.g. the BFF's mTLS client cert). Mirrors the
# pattern in CommandCenter's root Dockerfile so both sides of the channel
# share trust.
FROM ${BASE_IMAGE_ASPNET} AS runtime-base
ARG BASE_IMAGE_ASPNET

COPY docker/ca/ /tmp/bcs-ca/
RUN set -e; \
    if ls /tmp/bcs-ca/*.crt >/dev/null 2>&1; then \
        cp /tmp/bcs-ca/*.crt /usr/local/share/ca-certificates/ && \
        update-ca-certificates; \
    fi; \
    rm -rf /tmp/bcs-ca

Then change the existing final stage from:

FROM ${BASE_IMAGE_ASPNET} AS final

to:

FROM runtime-base AS final

(Adjust stage names to whatever benefits-mgmt actually uses.)

Dev cert sharing#

For local-dev mTLS to work between CommandCenter's BFF container and BMO CC's container, both sides must trust the same dev CA chain.

The simplest path: run CommandCenter's scripts/dev-certs.ps1 first (generates dev-root.crt + dev-intermediate.crt under CommandCenter/docker/ca/), then copy those two .crt files into benefits-mgmt/docker/ca/ and rebuild the BMO CC image. The leaf bmo-server-cert.pfx and bff-client-cert.pfx files in CommandCenter/.dev-certs/ are already mounted into both containers as docker secrets via CommandCenter's compose.yaml, so no further duplication is needed.

A small scripts/sync-dev-ca.ps1 in benefits-mgmt that pulls the two public certs from ../CommandCenter/docker/ca/ would smooth this — but is optional, not blocking.

Verification (after the patch lands in benefits-mgmt)#

  1. From benefits-mgmt, build the BMO CC image and confirm:
    docker run --rm <image> sh -c \
        "awk -v cmd='openssl x509 -noout -subject' '/BEGIN/{close(cmd)};{print | cmd}' \
         < /etc/ssl/certs/ca-certificates.crt | grep BCS"
    
    Expect both BCS Dev Root CA and BCS Dev Intermediate CA lines.
  2. Bring up CommandCenter's full compose stack with the new BMO CC image.
  3. From the host:
    GET http://localhost/api/bmo/healthz
    
    Expect 200, with the BFF's client cert validated by BMO CC against CommandCenter:ClientAuthentication:AllowedThumbprints (which the CommandCenter compose file sets from BMO_CC_CLIENT_CERT_THUMBPRINT).

Out of scope#

  • Production CA custody. Both repos currently have docker/ca/ empty (or populated with dev-only certs). When the production root + intermediate are minted, drop their public .crt files into the same folder in both repos and rebuild.
  • Issuance of leaf certs from the production CA — handled outside the repos (Key Vault, ops scripts) once custody is decided.