Skip to content

fix: TLS Fixes in helm chart - #3194

Draft
SantanM wants to merge 7 commits into
developfrom
SantanM/fix-tls-bug
Draft

fix: TLS Fixes in helm chart#3194
SantanM wants to merge 7 commits into
developfrom
SantanM/fix-tls-bug

Conversation

@SantanM

@SantanM SantanM commented Aug 25, 2026

Copy link
Copy Markdown
Collaborator

logto-check failed with:

wget: unrecognized option '--ca-certificate=/etc/d2e/tls/ca.pem'

That flag is GNU wget; the containers run busybox:latest, whose wget offers only --no-check-certificate. So the check could not verify at all, and the only busybox-compatible spelling would have been a verification bypass -- the exact thing this branch removes everywhere else.

Both affected init containers now use curl --cacert, which verifies chain and hostname by default, on an image already pulled for their own pod: the trex image for logto-check, the worker image for dependencies-check (its Dockerfile installs curl). No new image pull, no second tag to track.

The loops become until <check>; do sleep; done rather than polling and testing for a marker file, which also drops the success-trex/success-dataflow temp files.

Verified curl exists and supports --cacert by running both images, rather than assuming it -- assuming a flag existed is what caused this.

Merge Checklist

Please cross check this list if additions / modifications needs to be done on top of your core changes and tick them off. Reviewer can as well glance through and help the developer if something is missed out.

  • Automated Tests (Jasmine integration tests, Unit tests, and/or Performance tests)
  • Updated Manual tests / Demo Config
  • Documentation (Application guide, Admin guide, Markdown, Readme and/or Wiki)
  • Verified that local development environment is working with latest changes (integrated with latest develop branch)
  • following best practices in code review doc

logto-check failed with:

  wget: unrecognized option '--ca-certificate=/etc/d2e/tls/ca.pem'

That flag is GNU wget; the containers run busybox:latest, whose wget offers only
--no-check-certificate. So the check could not verify at all, and the only
busybox-compatible spelling would have been a verification bypass -- the exact
thing this branch removes everywhere else.

Both affected init containers now use curl --cacert, which verifies chain and
hostname by default, on an image already pulled for their own pod: the trex
image for logto-check, the worker image for dependencies-check (its Dockerfile
installs curl). No new image pull, no second tag to track.

The loops become `until <check>; do sleep; done` rather than polling and testing
for a marker file, which also drops the success-trex/success-dataflow temp files.

Verified curl exists and supports --cacert by running both images, rather than
assuming it -- assuming a flag existed is what caused this.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The trex container copied the internal CA from /etc/d2e/tls/ca.pem and
pointed WEBAPI_TRUST_CERTS at the same path, but never mounted the
d2e-tls volume -- the only container referencing that path without it.
The cp failed, update-ca-certificates had nothing to add, and the
process started with public roots only, so every internal TLS call died:
UnknownIssuer from rustls (Deno edge functions, DENO_TLS_CA_STORE=system)
and PKIX from the GraalVM WebAPI.

Nothing caught it. The args have no set -e, so the failed cp did not stop
startup; the liveness probe is `curl -fk`, which disables the very check
that was broken; and trex has no readiness probe, so the pod reported
Ready. CI runs a real `helm install --wait` plus `kubectl wait
--for=condition=Ready` and went green throughout.

Mount d2e-tls read-only at /etc/d2e/tls, matching the five sibling
containers that already do, which fixes the OS-store and
WEBAPI_TRUST_CERTS paths together since both read that one file.

Then assert instead of hoping: the file is non-empty, is PEM, the copy
succeeded, and -- the part that actually matters -- the anchor is present
in the rebuilt bundle. update-ca-certificates exits 0 over an empty
directory, so "it ran" proves nothing; verifying the self-signed CA
against ca-certificates.crt proves membership. Starting untrusted is
worse than not starting.

Verified in containers: volume absent -> exit 1; valid CA -> "1 added",
entrypoint reached; non-PEM -> exit 1; and the old script with the volume
absent -> exit 0, started anyway. End to end, curl with neither -k nor
--cacert reached idp.d2e.svc.cluster.local and trex.d2e.svc.cluster.local
at HTTP 200, ssl_verify_result 0 -- chain and hostname verified from the
OS store alone.

TLS__EXTRA__CA_CRTS parity with compose is still missing and is left for
a separate change.
Flow runs crashed with ssl.SSLCertVerificationError
(CERTIFICATE_VERIFY_FAILED, unable to get local issuer certificate) from
prefect/events/clients.py -> websockets, plus a preceding
"run-flow: deployment lookup failed" from run-flow.sh's urllib call.

docker-compose sets SSL_CERT_FILE and REQUESTS_CA_BUNDLE on
alp-dataflow-gen-worker to /etc/ssl/certs/ca-certificates.crt; the chart
never got those two lines. Both setups use the same https PREFECT_API_URL,
so that was the entire difference -- which is why compose and the DQD run
on CI were fine while k8s failed.

Without them only clients that explicitly pass certifi.where() are
covered (prefect's httpx client, requests). stdlib urlopen and websockets
use OpenSSL's defaults, which under `pixi run` resolve inside the env
prefix (<env>/ssl/cert.pem, capath empty) and never see the internal CA --
so neither update-ca-certificates nor the certifi append reaches them.
Worse, flows exec in their own per-plugin pixi env provisioned at run
time, so no startup-time append can ever reach them; an inherited env var
can.

Also fail loudly, as with trex: assert the CA is present, is PEM, copies,
and actually lands in /etc/ssl/certs/ca-certificates.crt. That last check
matters more here than anywhere else, because the two new env vars point
every Python HTTP stack at that bundle -- if the anchor is missing from
it, verification fails everywhere.

Verified in ghcr.io/ohdsi/d2e-dataflow-gen-worker:develop against a
server presenting the real leaf. With the chart's rendered args and env:
stdlib urlopen, requests and websockets all pass. Guard cases: volume
absent -> exit 1, non-PEM -> exit 1, valid CA -> proceeds. Most directly,
in /var/lib/d2e-flows/d2e-flows/baked/.pixi/envs/default -- the exact env
from the traceback -- the crash reproduces without the env vars and is
gone with them.
… name

Caddy had no certificate for gateway.<ns>.svc.cluster.local and aborted the
handshake ('no certificate available' -> 'Received fatal alert:
internal_error'). WebAPI dials that name for the jwks_uri advertised by OIDC
discovery, so its OIDC bean failed to build and every analytics-svc call
proxying to WebAPI returned 500.

Caddy is the server on this hop, so no client-side trust setting helps; it
already trusts the internal CA as a client for its upstreams. Add the internal
cert/key to its d2e-tls mount and a site block serving that name, with routes
mirroring the existing :8080 block. SERVER__CRT is the environment's public
certificate and has no cluster-internal SANs; CI hid this by using one
self-signed cert for both.

Verified: caddy validate passes on caddy:2.11-alpine with two independent SNI
policies; deployed and confirmed from trex that the jwks_uri returns HTTP 200
with tls_verify=0. The rendered Caddyfile is byte-identical to the running
ConfigMap.
@SantanM
SantanM force-pushed the SantanM/fix-tls-bug branch from 3dde768 to 8045838 Compare August 27, 2026 02:38
@SantanM
SantanM force-pushed the SantanM/fix-tls-bug branch from c145e2a to 8045838 Compare August 27, 2026 10:14
@SantanM SantanM changed the title fix(chart): use curl for the TLS readiness checks, not busybox wget fix: TLS Fixes in helm chart Aug 31, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants