Security¶
Content-free design¶
genai-otel-bridge never requests prompt text, completion text, message bodies, or any LLM inference content from the platform APIs it polls. It collects only operational signals: request counts, latencies, token counts, cost, error rates, and status codes.
This is enforced at three layers:
- Source strip — a default-deny allow-list in each source package drops every field not on the explicit list before any value enters the processing pipeline.
- Source.Guard content denylist — a defence-in-depth backstop at the composition root
denies the
AbsoluteNeverDenyKeysfloor regardless of any opt-in:gen_ai.*,input.value,output.value,request,response,inputs,outputs,messages,metadata,portkeyHeaders. - Conformance gate tests —
TestLogsExportContentLeakConformanceGateandTestLangsmithRunsContentLeakConformanceGateare release gate tests ininternal/app. They assert the full wired pipeline produces zero content fields in the outbound OTLP payload and must remain green on every release.
See Content Governance for the full model.
SSRF egress guard¶
Outbound HTTP requests go through internal/httpx, a hardened client that enforces an
egress guard at the dialer level:
Always blocked (even if AllowPrivate is set):
- Cloud metadata endpoints:
169.254.169.254,100.100.100.200,fd00:ec2::254 - CGNAT range:
100.64.0.0/10 - Link-local:
169.254.0.0/16,fe80::/10
Blocked unless AllowPrivate is true (dev/in-VPC use):
- Loopback:
127.0.0.0/8,::1 - RFC-1918 private ranges
The guard runs at two points: the dialer Control hook (authoritative for direct dials) and
a pre-dial hostname resolution check (checkDest) that resolves every IP before the
transport. The guard fails closed on DNS resolution failure.
The Portkey logs_export loop adds a second layer for signed-URL downloads: the download
URL is validated against settings.signed_url_allow_hosts with an exact host match before
any fetch, independent of the dialer guard.
Cross-host redirect block¶
internal/httpx rejects redirects to a different hostname. Go strips Authorization and
Cookie headers on cross-host redirects but forwards arbitrary custom headers — a
source's vendor auth token (such as x-portkey-api-key) would otherwise leak to a different
origin. The redirect check also re-runs the host allow-list and IP guard on every hop.
Secret handling¶
Credentials are never stored in git. The config file supports two secret reference forms:
auth:
header: x-portkey-api-key
value: ${PORTKEY_API_KEY} # resolved from the environment
# value: file:/run/secrets/key # ...or resolved from a file path at load time
Plaintext credentials in the config file are accepted for dev use but should be avoided in production. Use Kubernetes Secrets with ExternalSecret / SecretStore for production deployments, or inject credentials via environment variables.
Credentials are never logged — the bridge redacts auth values in all log output and self-telemetry.
Kubernetes network policy¶
The Helm chart deploys a default-deny NetworkPolicy. Egress is whitelisted for:
- DNS (CoreDNS pods in
kube-system, by label selector — not a CIDR, which would break on some CNIs) - Kubernetes API server (
apiServerCIDR— required for leader-election Lease and checkpoint ConfigMap) - OTLP endpoint (port 443, configurable CIDR)
- Source APIs (port 443, configurable CIDR)
Ingress is default-deny except the health port (8080) from any source (required for kubelet probes). The pod cannot receive traffic from the product plane.
EKS
Network policy enforcement on EKS requires the VPC-CNI network-policy feature (or Calico) to be enabled.
TLS¶
All outbound HTTP connections require TLS 1.2 as a minimum. The internal/httpx client
sets TLSMinVersion: tls.VersionTLS12. Connections to plaintext endpoints (HTTP, not HTTPS)
require explicit opt-in via the emit.*.otlp.allow_insecure config flag, which should only
be used for in-cluster collector deployments where the traffic does not leave the cluster.
RBAC (Kubernetes)¶
The pod's ServiceAccount is bound to a namespace-scoped Role (not a ClusterRole):
coordination.k8s.io/leases:get,create,updateon thegenai-otel-bridge-leaderLease only — the pod cannot touch any other Lease.configmaps:get,create,updateon thegenai-otel-bridge-checkpointsConfigMap only — the pod cannot read or modify its owngenai-otel-bridge-configConfigMap.deleteis not granted to the pod's ServiceAccount. The post-delete cleanup hook uses a separate ephemeral ServiceAccount that only exists during uninstall.
License¶
genai-otel-bridge is released under the GNU Affero General Public License v3.0 only
(AGPL-3.0-only). Every source file carries the SPDX identifier:
Third-party dependencies retain their own upstream licenses. See
LICENSING.md and the
full license text.
Reporting a vulnerability¶
Do not open a public issue for security vulnerabilities. Report privately via GitHub's private vulnerability reporting (Security → Report a vulnerability on the repository).
Reports that demonstrate a way to make the service emit inference content, leak credentials, or bypass the egress or SSRF guards are particularly in scope.
See SECURITY.md
for the full policy.
See also¶
- Content Governance — field allow/deny model
- High Availability — RBAC and Kubernetes resource model
- Configuration — secret reference syntax