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

上游 mTLS

当上游服务要求双向 TLS 时,API7 网关可以作为 TLS 客户端,向上游出示客户端证书,并可选使用受信任 CA 验证上游的服务器证书。请在服务的 upstream.tls 配置块中配置上游 mTLS:将 client_certclient_key 设置为网关出示的证书与私钥,并将 verifyca_certs 一起设置,用于验证上游服务器证书。

信息

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

工作原理

  1. 网关使用上游的 host/SNI 打开到上游的 TLS 连接。
  2. 上游出示服务器证书。如果 tls.verifytrue,网关会根据 tls.ca_certs 验证该证书;否则会接受该证书但不进行验证。
  3. 上游请求客户端证书。网关会出示 tls.client_certtls.client_key 中配置的证书与私钥。
  4. 上游根据自己的受信任 CA 验证客户端证书。验证成功后,请求会被代理;否则网关返回 HTTP 502

前置条件

  • 按照从控制台获取令牌中的步骤获取令牌。
  • 已启用 TLS、会请求客户端证书并根据受信任 CA 验证证书的上游服务。

准备以下 PEM 编码材料:

项目用途
客户端证书 + 私钥由网关出示给上游。必须由上游信任的 CA 签发。
上游 CA 证书网关用来验证上游服务器证书。仅当 tls.verifytrue 时需要。

以下示例使用带有如下配置的 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: rewriteupstream_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;快速入门创建的网关组可使用 default。将节点 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= 部分会回显网关出示的客户端证书 subject DN,确认上游已基于网关客户端证书完成 mTLS 握手。

如果移除 tls.client_certtls.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: rewriteupstream_host,使 SNI 与上游证书匹配。
连接被拒绝或出现明文错误上游协议为 http,而不是 https/grpcs将上游协议设置为 httpsgrpcs

后续步骤