Repository navigation
feat: add outbound proxy support for the OTel Collector sidecar - #476
WentingWu666666 wants to merge 13 commits into
Conversation
Route the per-cluster OTel Collector sidecar's OTLP exporter egress through an outbound proxy (Azure Arc Gateway and standard forward proxies) using the de-facto HTTP_PROXY / HTTPS_PROXY / NO_PROXY convention, which the collector's Go runtime honors natively. No image changes are required. Plumbing (Helm values -> operator env -> plugin params -> sidecar): - values.yaml: new Azure.proxySettings block (isProxyEnabled, httpProxy, httpsProxy, noProxy, proxyCert reserved for a future phase). - New 08_proxy_secret.yaml hydrates a proxy Secret in the operator namespace when enabled; NO_PROXY always appends an in-cluster bypass list so kube-apiserver, localhost Postgres scrapes and the in-pod OTLP receiver stay direct. Helpers documentdb.proxySecretName / documentdb.noProxy added. - 09_documentdb_operator.yaml wires the Secret via envFrom (single source of truth) and rolls the operator on proxy-config change via a checksum annotation. - constants.go / util.go: proxy env names, plugin-param names, and HTTPProxy/HTTPSProxy/NoProxy/ProxyEnabled helpers. - cnpg_cluster.go forwards the proxy params alongside the OTel sidecar params; cnpg_sync.go tracks them so changes trigger a rolling restart. - sidecar-injector config.go parses the params; lifecycle.go injects the proxy env (UPPER and lowercase) onto the collector only when configured. Tests: config round-trip, proxyEnvVars, util ProxyEnabled, and helm-unittest for the Secret and operator envFrom/checksum. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d5439e1a-20af-4212-9864-0c0863ae0ab5 Signed-off-by: Wenting Wu <wentingwu@microsoft.com>
Document `proxy` as the OSS values key. Azure.proxySettings is honored only as a compatibility fallback (Azure Arc injects it) via a documentdb.proxy helper; `proxy` wins when both are set. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d5439e1a-20af-4212-9864-0c0863ae0ab5 Signed-off-by: Wenting Wu <wentingwu@microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d5439e1a-20af-4212-9864-0c0863ae0ab5 Signed-off-by: Wenting Wu <wentingwu@microsoft.com>
… unused helper, document credentials limit Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d5439e1a-20af-4212-9864-0c0863ae0ab5 Signed-off-by: Wenting Wu <wentingwu@microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d5439e1a-20af-4212-9864-0c0863ae0ab5 Signed-off-by: Wenting Wu <wentingwu@microsoft.com>
There was a problem hiding this comment.
🟡 Changes recommended
Proxy routing can disrupt Kubernetes API access and leave HTTP-only collector configurations attempting direct egress.
3 open issues
What changed in this PR
Adds outbound proxy configuration for the OTel Collector sidecar through Helm, the operator, and CNPG plugin parameters.
Changes:
- Adds proxy values, Secret wiring, bypass defaults, and operator restart checksums.
- Propagates proxy settings to collector containers and tracks configuration changes.
- Adds Helm and Go tests for proxy configuration.
| File | Description |
|---|---|
| operator/src/internal/utils/util.go | Reads proxy environment variables. |
| operator/src/internal/utils/util_test.go | Tests proxy readers. |
| operator/src/internal/utils/constants.go | Defines proxy environment and parameter names. |
| operator/src/internal/cnpg/cnpg_sync.go | Tracks proxy parameter changes. |
| operator/src/internal/cnpg/cnpg_cluster.go | Forwards proxy configuration. |
| operator/documentdb-helm-chart/values.yaml | Documents proxy settings. |
| operator/documentdb-helm-chart/tests/09_operator_deployment_test.yaml | Tests Secret wiring and checksum presence. |
| operator/documentdb-helm-chart/tests/08_proxy_secret_test.yaml | Tests proxy Secret rendering and compatibility settings. |
| operator/documentdb-helm-chart/templates/09_documentdb_operator.yaml | Injects proxy settings and restart checksum. |
| operator/documentdb-helm-chart/templates/08_proxy_secret.yaml | Creates the proxy Secret. |
| operator/documentdb-helm-chart/templates/_helpers.tpl | Resolves settings and composes bypass entries. |
| operator/cnpg-plugins/sidecar-injector/internal/lifecycle/lifecycle.go | Injects collector proxy variables. |
| operator/cnpg-plugins/sidecar-injector/internal/lifecycle/lifecycle_test.go | Tests proxy variable generation. |
| operator/cnpg-plugins/sidecar-injector/internal/config/config.go | Parses and serializes proxy parameters. |
| operator/cnpg-plugins/sidecar-injector/internal/config/config_test.go | Tests proxy parameter round-tripping. |
Review effort: Balanced
Merge proxyCert with the operator's system CA roots into the per-cluster OTel ConfigMap and point the collector's SSL_CERT_FILE at the bundle. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d5439e1a-20af-4212-9864-0c0863ae0ab5 Signed-off-by: Wenting Wu <wentingwu@microsoft.com>
The OTLP exporter is hardcoded to plaintext gRPC, so no collector component performs TLS through a proxy and the merged CA bundle has no working use case yet. Revisit together with exporter TLS support. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d5439e1a-20af-4212-9864-0c0863ae0ab5 Signed-off-by: Wenting Wu <wentingwu@microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d5439e1a-20af-4212-9864-0c0863ae0ab5 Signed-off-by: Wenting Wu <wentingwu@microsoft.com>
xgerman
left a comment
There was a problem hiding this comment.
I verified three proxy correctness gaps that should be addressed before merge:
-
Kubernetes API bypass: applying
HTTPS_PROXYto the operator can route its in-cluster API connection through the external proxy whenKUBERNETES_SERVICE_HOSTis IPv6 or outside the fixed RFC1918 ranges. Add the actual Kubernetes Service host toNO_PROXY. See #476 (comment) -
HTTP-only proxy configuration: grpc-go always evaluates gRPC targets using the HTTPS proxy selector, even when the OTLP exporter has
tls.insecure: true. With onlyproxy.httpProxyconfigured, OTLP export remains direct. Populate the collectorHTTPS_PROXYfromhttpProxywhenhttpsProxyis empty and test this case. See #476 (comment) -
Short in-cluster service names: the documented endpoint form
otel-collector.monitoring:4317matches neither.svcnor the RFC1918 entries. grpc-go checksNO_PROXYagainst the unresolved hostname, so this traffic can be sent to the external proxy. Require/document an FQDN or explicitnoProxyentry, and narrow the claim that all in-cluster traffic is automatically bypassed. See #476 (comment)
The focused Go suites for the changed operator and injector packages pass; these are runtime proxy-selection gaps not covered by the current tests.
Runtime validation in kindTested commit What works
Confirmed failures
The cluster does not have the Kubernetes 1.33 ImageVolume feature gate enabled, so the DocumentDB admission webhook rejected a full DocumentDB CR. I validated the injector path with a synthetic CNPG Cluster instead. The original Helm revision was restored and all test resources/images were removed after testing. |
1. HTTP-only proxy configuration does not work for the OTLP gRPC exporterLocation: The chart accepts With only Could we either use 2. Mandatory RFC1918 bypass prevents proxying to private destinationsLocation: The helper unconditionally appends For example, an OTLP destination at Could we make the broad private-network bypasses configurable rather than mandatory? We can retain narrow defaults for loopback and cluster-local destinations while allowing users to control whether other private destinations bypass the proxy. |
The proxy Secret used the standard HTTP_PROXY/HTTPS_PROXY/NO_PROXY names and was injected into the operator via envFrom, so the operator's in-cluster client (which dials the literal KUBERNETES_SERVICE_HOST) could be routed through the proxy when the API server IP was outside the default NO_PROXY ranges (e.g. IPv6 or non-RFC1918 Service CIDRs). Use DOCUMENTDB_OTEL_* names for the Secret keys and operator env. The operator still forwards them to the collector sidecar, which exposes the standard names. The operator process itself is never proxied. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d5439e1a-20af-4212-9864-0c0863ae0ab5 Signed-off-by: Wenting Wu <wentingwu@microsoft.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d5439e1a-20af-4212-9864-0c0863ae0ab5 Signed-off-by: Wenting Wu <wentingwu@microsoft.com>
…roxy is set grpc-go looks up the proxy with an https-scheme request, so it reads only HTTPS_PROXY even for plaintext connections. With only httpProxy set the OTLP exporter bypassed the proxy. Fall back to the HTTP proxy for HTTPS_PROXY in the collector env and cover it with tests. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d5439e1a-20af-4212-9864-0c0863ae0ab5 Signed-off-by: Wenting Wu <wentingwu@microsoft.com>
Short Service names such as otel-collector.monitoring are not covered by the default bypass list; tell users to add them to proxy.noProxy. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d5439e1a-20af-4212-9864-0c0863ae0ab5 Signed-off-by: Wenting Wu <wentingwu@microsoft.com>
|
@xgerman thanks for the thorough review. All three points are addressed:
All 116 helm tests and the Go suites for the touched packages pass. Could you take another look and clear the change request if you're satisfied? |
|
@hossain-rayhan thanks for the review. 1. HTTP-only proxy: Fixed in b17f36a. The collector's You're right that env-var assertions can't prove the export actually goes through the proxy. A transport-level test (an e2e that routes the collector's OTLP export through a Squid pod and asserts the CONNECT) is planned as a follow-up rather than in this PR. I'll reply separately on point 2. |
Add proxy.bypassPrivateRanges (default true). Setting it to false lets private-network destinations, such as an OTLP endpoint reachable only through the proxy, use the proxy. Cluster-local entries always bypass. Azure.proxySettings is unchanged and always keeps the ranges. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: d5439e1a-20af-4212-9864-0c0863ae0ab5 Signed-off-by: Wenting Wu <wentingwu@microsoft.com>
|
2. RFC1918 bypass: Fixed in c4611fd. Added |


What
Adds routing-based outbound proxy support for the OpenTelemetry Collector sidecar injected into each DocumentDB pod, so its OTLP exporter egress can traverse an outbound proxy — Azure Arc Gateway and standard forward proxies. Routing is done via env vars. No image changes are required.
The collector's Go runtime honors the de-facto
HTTP_PROXY/HTTPS_PROXY/NO_PROXYconvention natively (http.ProxyFromEnvironment), so enabling the proxy is purely a matter of plumbing those env vars to the collector container.Why
Azure Arc Gateway is a CONNECT-tunneling forward proxy with end-to-end TLS to the target (no MITM), so extension telemetry egress only needs routing — not a proxy certificate. This lets DocumentDB clusters run in proxy-restricted / Arc-enrolled environments.
How (config flow)
values.yaml-> operator Secret + env -> CNPG plugin params -> OTel Collector sidecar env:values.yamlproxyblock (isProxyEnabled,httpProxy,httpsProxy,noProxy)templates/08_proxy_secret.yaml(new)documentdb-proxy-configin the operator namespace when enabled; keys areDOCUMENTDB_OTEL_HTTP_PROXY/DOCUMENTDB_OTEL_HTTPS_PROXY/DOCUMENTDB_OTEL_NO_PROXY(non-standard on purpose, see below)templates/_helpers.tpldocumentdb.proxySecretName;documentdb.noProxyalways appends an in-cluster bypass list (localhost,.svc,.svc.cluster.local, RFC1918) so kube-apiserver, localhost Postgres scrapes and the in-pod OTLP receiver stay directtemplates/09_documentdb_operator.yamlenvFromthe Secret (the operator only forwards the values to the collector; it is never itself proxied) +checksum/proxy-configannotation to roll the operator on changeconstants.go,util.goHTTPProxy/HTTPSProxy/NoProxy/ProxyEnabledhelperscnpg_cluster.go,cnpg_sync.goconfig.go,lifecycle.goScope / follow-up
Testing
go build+go vetfor both the operator and sidecar-injector modulesproxyEnvVars,ProxyEnabled(+ existingcnpg/utils/controllerand injector suites)helm unittest: 116/116 pass, including a new proxy-secret suite and operatorenvFrom/checksum assertionsNO_PROXYmerges user + in-cluster defaults and the operatorenvFrom/annotation wire upValues key / Azure Arc compatibility
The documented OSS key is
proxy. Adocumentdb.proxyhelper also honorsAzure.proxySettingsas a fallback solely for Azure Arc extension compatibility (the Arc platform injects that key), so no downstream fork or overlay is needed.proxywins when both are set;isProxyEnabledis true if either enables it.Out of scope: TLS-intercepting (SSL-bump) proxies / custom proxy CA. The OTLP exporter is currently hardcoded to plaintext gRPC, so this should be revisited together with exporter TLS support.
Operator is not proxied: the Secret keys use
DOCUMENTDB_OTEL_*names instead of the standard ones so the operator's own Kubernetes API client never routes through the proxy, regardless of the cluster's Service CIDR. The collector sidecar still receives the standardHTTP_PROXY/HTTPS_PROXY/NO_PROXY.