Organization CA bundle
Behind a TLS-inspecting proxy — Netskope, Zscaler, BlueCoat, an internal corporate CA — every
outbound HTTPS call from a Ray pod fails certificate verification. Model registries, dataset
URLs, the Hugging Face hub, S3: all CERTIFICATE_VERIFY_FAILED, because the proxy re-signs
certificates with a root the container’s trust store has never heard of.
Point the chart at a ConfigMap holding your organization’s root CA and it fixes this for the head and worker pods.
Enabling
Section titled “Enabling”Create the ConfigMap out of band — through your GitOps layer, kubectl, or trust-manager:
apiVersion: v1kind: ConfigMapmetadata: name: org-ca-bundle namespace: rayservedata: ca.crt: | -----BEGIN CERTIFICATE----- ...your org CA... -----END CERTIFICATE-----Then:
orgCABundle: configMapName: org-ca-bundleThe key must be ca.crt, and the ConfigMap must be in the release namespace.
When configMapName is empty — the default — nothing is rendered and the resulting
RayService is byte-identical to the no-injection case.
What it does
Section titled “What it does”For both head and worker pods, the chart adds:
- An initContainer (
build-ca-bundle,alpine:3.20by default) that concatenates the base image’s system trust store with your CA:Terminal window cat /etc/ssl/certs/ca-certificates.crt /var/local/org-ca/ca.crt > /shared/combined-ca.crt - A shared
emptyDirthe main container mounts read-only at/shared. The Ray container never sees the raw ConfigMap. - Four environment variables on the main container, all pointing at
/shared/combined-ca.crt.
Concatenating matters. Replacing the system trust store rather than extending it would break TLS to everything the proxy does not re-sign.
The four environment variables
Section titled “The four environment variables”| Variable | Honored by |
|---|---|
SSL_CERT_FILE | OpenSSL, Python ssl, most tooling |
REQUESTS_CA_BUNDLE | requests, urllib3 |
CURL_CA_BUNDLE | curl |
GIT_SSL_CAINFO | git |
GIT_SSL_CAINFO is separate because git’s libcurl ignores the other three and reads only
that one. Without it, pip install git+https://... and any other git-over-HTTPS call in a
worker fails verification even though plain requests and pip succeed — a confusing
partial failure.
The Argo CD interaction
Section titled “The Argo CD interaction”The broad ignore exists only to suppress the autoscaler and runtime mutations KubeRay makes
to rayClusterConfig. To use orgCABundle under Argo CD, narrow it: replace
/spec/rayClusterConfig with targeted JSON pointers at the specific subpaths KubeRay
rewrites, or drop the rule and add narrower ones as drift appears.
Then verify against the running pod rather than the sync status:
kubectl -n rayserve exec $(kubectl -n rayserve get pod -l ray.io/node-type=head -o name) \ -- printenv SSL_CERT_FILEEmpty output means the injection did not reach the pod, whatever Argo CD reports.
Verifying
Section titled “Verifying”POD=$(kubectl -n rayserve get pod -l ray.io/node-type=worker -o name)
kubectl -n rayserve exec $POD -- printenv SSL_CERT_FILE REQUESTS_CA_BUNDLE CURL_CA_BUNDLE GIT_SSL_CAINFOkubectl -n rayserve exec $POD -- ls -l /shared/combined-ca.crtkubectl -n rayserve exec $POD -- python -c "import requests; print(requests.get('https://huggingface.co').status_code)"A 200 from the last command against a host that traverses the proxy is the real test.
If the initContainer failed, the pod never starts and the reason is in its logs:
kubectl -n rayserve logs $POD -c build-ca-bundleThe usual causes are a missing ConfigMap or a key that is not ca.crt.
Why a ConfigMap and not a Secret
Section titled “Why a ConfigMap and not a Secret”A CA certificate is public material by design — the entire PKI trust model depends on root CAs being widely distributed. Mozilla’s bundle ships in every browser and OS, and corporate inspecting-proxy roots are pushed to every device that traverses them.
Kubernetes itself distributes the cluster’s own CA through a ConfigMap
(kube-root-ca.crt, auto-projected into every namespace), and cert-manager’s
trust-manager distributes CA bundles as
ConfigMaps via its Bundle CR. This chart follows that precedent.
Reserve Secrets for things that actually need confidentiality — private keys, OAuth client secrets.
Known gap: httpx
Section titled “Known gap: httpx”requests, urllib3, stdlib urllib, curl, git, and most non-Python TLS tooling honor the
environment variables automatically. httpx is the notable exception, and it is increasingly
common in modern Python libraries — worth grepping your dependencies for.
The initContainer image
Section titled “The initContainer image”orgCABundle: initImage: "alpine:3.20"It needs only sh and cat. Override it if your organization requires images from a
vetted registry — and note that in an air-gapped environment, an unreachable
alpine:3.20 blocks pod startup entirely rather than just skipping the injection.