跳到主要内容
版本:3.9.x

上游 mTLS

如果上游服务要求双向 TLS,API7 网关可以作为 TLS 客户端,向上游出示客户端证书,并可选择使用受信任的 CA 验证上游的服务器证书。在服务的 upstream.tls 中配置上游 mTLS:使用 client_cert 和 client_key 指定网关使用的证书和私钥,使用 verify 和 ca_certs 验证上游的服务器证书。

信息

本页介绍网关与上游服务之间的 mTLS。如需配置 API 客户端与网关之间的 mTLS,请参阅客户端 mTLS 认证。如需配置控制面与数据面之间的 mTLS,请参阅控制面与数据面之间的双向 TLS。

工作原理​

  1. 网关使用上游的 host/SNI 与上游建立 TLS 连接。
  2. 上游出示服务器证书。如果 tls.verify 为 true,网关使用 tls.ca_certs 验证证书;否则直接接受证书。
  3. 上游请求客户端证书。网关使用 tls.client_cert 和 tls.client_key 中配置的证书与私钥进行身份验证。
  4. 上游使用自身信任的 CA 验证客户端证书。验证成功后,网关代理请求;否则,网关返回 HTTP 502。

前置条件​

请在具有运行中数据面节点的专用测试网关组中执行本指南的示例,避免与快速开始或客户端 mTLS 指南的示例路由重叠,或改动已有资源。使用 ADC 时,请先确认 ADC 已连接到该测试网关组。验证时使用该组数据面节点的实际监听地址和端口。

  • 从控制台获取 Token。
  • 一个启用了 TLS、会请求客户端证书并使用受信任的 CA 验证该证书的上游服务。

准备以下 PEM 格式的文件:

文件用途
客户端证书及私钥供网关向上游进行身份验证。证书必须由上游信任的 CA 签发。
上游 CA 证书供网关验证上游的服务器证书。仅当 tls.verify 为 true 时需要。

下文示例使用配置如下的 nginx 实例:

upstream-nginx.conf
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 地址访问上游节点也是如此。

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。将节点的 host 替换为你的上游地址。

如需跳过上游证书验证,例如在开发环境中使用自签名证书时,将 tls.verify 设为 false 并省略 tls.ca_certs。网关仍会出示客户端证书,但不会验证上游的服务器证书。生产环境不建议使用此设置。

第 2 步:在服务上创建路由​

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": ["/*"]
}'

第 3 步:验证配置​

通过网关发送请求:

curl -i "http://127.0.0.1:9080/anything"

应收到 200 OK,响应体格式如下:

upstream-mtls-ok client=CN=api7-gateway

client= 后的内容是网关出示的客户端证书的主题 DN。这表明上游已使用网关的客户端证书完成 mTLS 握手。

如果移除 tls.client_cert 和 tls.client_key 后重新应用服务配置,同一个请求将返回 HTTP 502。此时上游会拒绝连接,并发送 SSL 告警 peer did not return a certificate,可在网关错误日志中查看。

从密钥提供方引用证书​

可以使用 $secret://... URI 引用存储在 HashiCorp Vault、AWS Secrets Manager 或 Kubernetes Secret 中的证书,无需在服务定义中直接写入 PEM 内容。有关可用的提供方,请参阅安全凭证管理。

故障排查​

现象可能原因解决方法
启用上游 mTLS 后,网关返回 HTTP 502上游拒绝了网关的客户端证书。确认证书由上游信任的 CA 签发。检查上游日志中的 TLS 错误。
设置 tls.verify: true 后返回 HTTP 502tls.ca_certs 中配置的 CA 不信任上游服务器证书。将签发该证书的 CA 加入 tls.ca_certs,或临时将 tls.verify 设为 false 以确认原因。
上游日志记录 SNI 不匹配,网关返回 HTTP 502上游证书的 CN 或 SAN 与网关发送的 SNI 不匹配。在上游配置中设置 pass_host: rewrite 和 upstream_host,使 SNI 与上游证书匹配。
连接被拒绝或出现明文协议错误上游协议配置为 http。将上游协议改为 https 或 grpcs。

后续步骤​