上游 mTLS
当上游服务要求双向 TLS 时,API7 网关可以作为 TLS 客户端,向上游出示客户端证书,并可选使用受信任 CA 验证上游的服务 器证书。请在服务的 upstream.tls 配置块中配置上游 mTLS:将 client_cert 和 client_key 设置为网关出示的证书与私钥,并将 verify 与 ca_certs 一起设置,用于验证上游服务器证书。
本页介绍的是网关与上游服务之间的 mTLS。若要配置 API 客户端与网关之间的 mTLS,请参阅客户端 mTLS 认证。若要配置控制面与数据面之间的 mTLS,请参阅控制面与数据面之间的双向 TLS。
工作原理
- 网关使用上游的
host/SNI 打开到上游的 TLS 连接。 - 上游出示服务器证书。如果
tls.verify为true,网关会根据tls.ca_certs验证该证书;否则会接受该证书但不进行验证。 - 上游请求客户端证书。网关会出示
tls.client_cert和tls.client_key中配置的证书与私钥。 - 上游根据自己的受信任 CA 验证客户端证书。验证成功后,请求会被代理;否则网关返回
HTTP 502。
前置条件
- 按照从控制台获取令牌中的步骤获取令牌。
- 已启用 TLS、会请求客户端证书并根据受信任 CA 验证证书的上游服务。
准备以下 PEM 编码材料:
| 项目 | 用途 |
|---|---|
| 客户端证书 + 私钥 | 由网关出示给上游。必须由上游信任的 CA 签发。 |
| 上游 CA 证书 | 网关用来验证上游服务器证书。仅当 tls.verify 为 true 时需要。 |
以下示例使用带有如下配置的 nginx 实例:
events {}
http {
server {
listen 8443 ssl;
server_name upstream.test.local;
ssl_certificate /certs/upstream.crt;
ssl_certificate_key /certs/upstream.key;
ssl_client_certificate /certs/ca.crt;
ssl_verify_client on;
location / {
return 200 "upstream-mtls-ok client=$ssl_client_s_dn\n";
add_header Content-Type text/plain;
}
}
}
如果你没有用于测试的一组证书,请使用以下命令生成:
# CA
openssl req -x509 -newkey rsa:2048 -keyout ca.key -out ca.crt -days 365 -nodes \
-subj "/CN=Test CA"
# 上游服务器证书
openssl req -newkey rsa:2048 -keyout upstream.key -out upstream.csr -nodes \
-subj "/CN=upstream.test.local"
openssl x509 -req -in upstream.csr -CA ca.crt -CAkey ca.key -CAcreateserial \
-out upstream.crt -days 365
# 客户端证书(由网关出示)
openssl req -newkey rsa:2048 -keyout client.key -out client.csr -nodes \
-subj "/CN=api7-gateway"
openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial \
-out client.crt -days 365
配置上游 mTLS
第 1 步:创建包含上游 tls 配置块的服务
创建一个上游协议为 https(gRPC 使用 grpcs)的服务,并嵌入客户端证书、私钥,以及在启用验证时网关用于验证上游的 CA 证书。
pass_host: rewrite 与 upstream_host 搭配使用,可以确保 TLS 握手期间发送的 SNI 与上游服务器证书匹配,即使节点是通 过 IP 地址访问的。
- Admin API
- ADC
CLIENT_CRT=$(awk 1 ORS='\\n' client.crt)
CLIENT_KEY=$(awk 1 ORS='\\n' client.key)
CA_CRT=$(awk 1 ORS='\\n' ca.crt)
curl -k "https://localhost:7443/apisix/admin/services/upstream-mtls?gateway_group_id={gateway_group_id}" -X PUT \
-H "X-API-KEY: ${API_KEY}" \
-H "Content-Type: application/json" \
-d "{
\"name\": \"upstream-mtls\",
\"upstream\": {
\"type\": \"roundrobin\",
\"scheme\": \"https\",
\"pass_host\": \"rewrite\",
\"upstream_host\": \"upstream.test.local\",
\"nodes\": [
{ \"host\": \"192.0.2.10\", \"port\": 8443, \"weight\": 1 }
],
\"tls\": {
\"client_cert\": \"${CLIENT_CRT}\",
\"client_key\": \"${CLIENT_KEY}\",
\"verify\": true,
\"ca_certs\": [\"${CA_CRT}\"]
}
}
}"
将 {gateway_group_id} 替换为你的网关组 ID;快速入门创建的网关组可使用 default。将节点 host 替换为你的上游地址。
如果要跳过上游证书验证(例如在开发环境中使用自签名上游证书),请将 tls.verify 设置为 false 并省略 tls.ca_certs。网关仍会出示客户端证书,但不会验证上游服务器证书。不建议在生产环境中这样配置。
services:
- name: upstream-mtls
upstream:
type: roundrobin
scheme: https
pass_host: rewrite
upstream_host: upstream.test.local
nodes:
- host: 192.0.2.10
port: 8443
weight: 1
tls:
client_cert: |
-----BEGIN CERTIFICATE-----
<client.crt content>
-----END CERTIFICATE-----
client_key: |
-----BEGIN PRIVATE KEY-----
<client.key content>
-----END PRIVATE KEY-----
verify: false
ADC 的 upstream.tls 模式仅接受 client_cert、client_key、client_cert_id 和 verify。它不接受 ca_certs 字段,因此当 verify 为 true 时,网关会回退到系统 CA 存储。若要为上游验证固定使用自定义 CA,请按另一个标签页所示通过 Admin API 配置服务。
第 2 步:在服务上创建路由
- Admin API
- ADC
curl -k "https://localhost:7443/apisix/admin/routes/upstream-mtls?gateway_group_id={gateway_group_id}" -X PUT \
-H "X-API-KEY: ${API_KEY}" \
-H "Content-Type: application/json" \
-d '{
"name": "upstream-mtls",
"service_id": "upstream-mtls",
"paths": ["/*"]
}'
在同一个 adc.yaml 中将路由挂载到服务:
services:
- name: upstream-mtls
# ...上游配置块同上...
routes:
- name: upstream-mtls
uris:
- /*
adc sync -f adc.yaml
第 3 步:验证
通过网关发送请求:
curl -i "http://127.0.0.1:9080/anything"
你应收到 200 OK 响应,响应体类似:
upstream-mtls-ok client=CN=api7-gateway
client= 部分会回显网关出示的客户端证书 subject DN,确认上游已基于网关客户端证书完成 mTLS 握手。
如果移除 tls.client_cert 和 tls.client_key 并重新应用服务,同一请求会返回 HTTP 502,因为上游会通过 SSL 警报(peer did not return a certificate)拒绝连接,你可以在网关错 误日志中看到该信息。
从密钥提供商引用证书
除了直接在服务定义中嵌入 PEM 材料外,你还可以使用 $secret://... URI 格式引用存储在 HashiCorp Vault、AWS Secrets Manager 或 Kubernetes Secrets 中的证书。有关可用密钥提供商,请参阅安全凭证管理。
故障排查
| 现象 | 可能原因 | 解决方法 |
|---|---|---|
启用上游 mTLS 后网关返回 HTTP 502 | 上游拒绝了网关的客户端证书。 | 确认证书由上游信任的 CA 签发。检查上游日志中的 TLS 错误。 |
tls.verify: true 时返回 HTTP 502 | 上游服务器证书不受配置的 tls.ca_certs 信任。 | 将签发上游服务器证书的 CA 添加到 tls.ca_certs,或临时设置 tls.verify: false 以确认原因。 |
上游日志中出现 SNI 不匹配并返回 HTTP 502 | 上游证书的 CN/SAN 与网关发送的 SNI 不匹配。 | 在上游上设置 pass_host: rewrite 和 upstream_host,使 SNI 与上游证书 匹配。 |
| 连接被拒绝或出现明文错误 | 上游协议为 http,而不是 https/grpcs。 | 将上游协议设置为 https 或 grpcs。 |
后续步骤
- 客户端 mTLS 认证:为连接到网关的客户端配置 mTLS。
- SSL 证书:在 API7 网关中管理证书。
- 安全凭证管理:将证书存储在外部密钥管理器中。