Description
Outbound TLS trust has no configuration surface. There is no ssl_verify-style switch and no way to point the gateway at a private CA bundle from config:
-
no tls / ca_file / verify field in the upstream: config section, which otherwise carries the connection-layer settings (timeouts, keepalive, pool, retries);
-
no TLS field on any resource in schemas/resources/ — in particular provider_key, which is where an upstream endpoint (api_base) is declared;
-
AISIX_UPSTREAM_SSL_VERIFY is rejected at boot:
unknown field `upstream_ssl_verify`, expected one of `etcd`, `resources_file`,
`proxy`, `admin`, `observability`, `cache`, `ratelimit`, `upstream`,
`downstream`, `managed`, `bedrock_endpoint_url`
A private CA can be trusted today, but only as an OS-level side effect and only process-wide: SSL_CERT_FILE / SSL_CERT_DIR are honoured, and are additive to the built-in roots (setting SSL_CERT_FILE to a private CA alone still leaves public providers trusted). Neither is documented.
Requested, in order:
- Document the mechanism that already works —
SSL_CERT_FILE / SSL_CERT_DIR, and the fact that it is additive. Zero code.
- A first-class config surface for a private CA, so trust is declared where the endpoint is declared rather than in the process environment — a deployment-level
upstream.tls.ca_file, and/or a per-provider_key CA for deployments facing more than one private CA.
- An explicit, scoped verification override for test environments, as the peers below provide. Not global, and not the default.
Why
Self-hosted model endpoints and internal OIDC providers behind an enterprise CA are routine in on-prem deployments, and every outbound path is affected — the provider bridges, guardrails, MCP OAuth, A2A, OTLP export, and JWKS/discovery all share aisix_gateway::client_builder() (crates/aisix-gateway/src/upstream_http.rs:121).
The current answer is an environment variable that does not appear in the docs, the example config, or the resource schemas, so it is not discoverable from the failure. What operators see is a generic 502:
transport error: error sending request for url (https://...): client error (Connect): invalid peer certificate: UnknownIssuer
Worth flagging for whoever picks this up: the intuitive fix — dropping the CA in /usr/local/share/ca-certificates and running update-ca-certificates — does work, but only as root. The image runs as user aisix, so the command fails with Permission denied, leaves the bundle unchanged, and the gateway still rejects the certificate. It looks like the CA was installed when it was not.
Priority
Medium. A working workaround exists, but it is undocumented, process-wide, and cannot express "this one upstream, this one CA".
Reproduction
ghcr.io/api7/aisix:0.6.0, standalone (resources_file), against an HTTPS upstream whose certificate is signed by a private CA:
provider_keys:
- display_name: private-pk
provider: openai
api_key: sk-mock
api_base: https://internal-llm.example:8443/v1
| Setting |
Result |
| (none) |
502 invalid peer certificate: UnknownIssuer |
SSL_CERT_FILE=/certs/ca.crt |
200 |
SSL_CERT_DIR=/certs |
200 |
CA into system store + update-ca-certificates, as root |
200 |
CA into system store, as the default aisix user |
502 — update-ca-certificates silently fails |
REQUESTS_CA_BUNDLE=/certs/ca.crt |
502 |
Prior art
Every self-hostable peer exposes at least a CA bundle; three of four also expose an explicit skip.
Surveyed 2026-07-31; every claim rests on a fetched docs/source page.
Description
Outbound TLS trust has no configuration surface. There is no
ssl_verify-style switch and no way to point the gateway at a private CA bundle from config:no
tls/ca_file/verifyfield in theupstream:config section, which otherwise carries the connection-layer settings (timeouts, keepalive, pool, retries);no TLS field on any resource in
schemas/resources/— in particularprovider_key, which is where an upstream endpoint (api_base) is declared;AISIX_UPSTREAM_SSL_VERIFYis rejected at boot:A private CA can be trusted today, but only as an OS-level side effect and only process-wide:
SSL_CERT_FILE/SSL_CERT_DIRare honoured, and are additive to the built-in roots (settingSSL_CERT_FILEto a private CA alone still leaves public providers trusted). Neither is documented.Requested, in order:
SSL_CERT_FILE/SSL_CERT_DIR, and the fact that it is additive. Zero code.upstream.tls.ca_file, and/or a per-provider_keyCA for deployments facing more than one private CA.Why
Self-hosted model endpoints and internal OIDC providers behind an enterprise CA are routine in on-prem deployments, and every outbound path is affected — the provider bridges, guardrails, MCP OAuth, A2A, OTLP export, and JWKS/discovery all share
aisix_gateway::client_builder()(crates/aisix-gateway/src/upstream_http.rs:121).The current answer is an environment variable that does not appear in the docs, the example config, or the resource schemas, so it is not discoverable from the failure. What operators see is a generic 502:
Worth flagging for whoever picks this up: the intuitive fix — dropping the CA in
/usr/local/share/ca-certificatesand runningupdate-ca-certificates— does work, but only as root. The image runs as useraisix, so the command fails withPermission denied, leaves the bundle unchanged, and the gateway still rejects the certificate. It looks like the CA was installed when it was not.Priority
Medium. A working workaround exists, but it is undocumented, process-wide, and cannot express "this one upstream, this one CA".
Reproduction
ghcr.io/api7/aisix:0.6.0, standalone (resources_file), against an HTTPS upstream whose certificate is signed by a private CA:invalid peer certificate: UnknownIssuerSSL_CERT_FILE=/certs/ca.crtSSL_CERT_DIR=/certsupdate-ca-certificates, as rootaisixuserupdate-ca-certificatessilently failsREQUESTS_CA_BUNDLE=/certs/ca.crtPrior art
ssl_verify: falseor a CA path, plusSSL_VERIFY/SSL_CERT_FILEca_cert_pemandinsecure_skip_verifyin providernetwork_configca_certificates,tls_verify,tls_verify_depthon the Service entityBackendTLSPolicy.validation.caCertificateRefsssl_verify, defaulttruesince 3.16.0; upstream server certs are not verified at all, and upstreamtls.verifyis Kafka-onlyEvery self-hostable peer exposes at least a CA bundle; three of four also expose an explicit skip.
Surveyed 2026-07-31; every claim rests on a fetched docs/source page.