上游 mTLS
当上游服务要求双向 TLS(mTLS)时,API7 网关可以提供客户端证书,并使用受信任的证书颁发机构验证上游服务器证书。将客户端证书和 CA 作为证书对象存储在网关组中,再在服务的上游配置中引用它们的 ID。
本文介绍网关与上游服务之间的 mTLS。如需配置 API 客户端与网关之间的 mTLS,请参阅客户端 mTLS 认证。如需配置控制面与数据面之间的 mTLS,请参阅控制面与数据面之间的双向 TLS。
工作原理
上游配置使用以下三个字段:
| 字段 | 值 | 用途 |
|---|---|---|
client_certificate | 证书对象 ID | 选择网关向上游提供的证书和私钥。 |
ca_certificates | CA 证书对象 ID 数组 | 选择用于验证上游服务器证书的 CA 证书。 |
tls_verify | 布尔值 | 启用或禁用对上游服务器证书的验证。 |
较旧的 upstream.tls 对象已弃用。请勿在新配置的 tls.client_cert、tls.client_key 或 tls.ca_certs 中放入内联 PEM 值。
前置条件
- 按照从控制台获取 Token中的步骤获取 Token。
- 用于构建 JSON 请求体和读取已创建对象 ID 的
jq。 - 已启用 TLS 且会请求客户端证书的上游。
- 由上游信任的 CA 签发的客户端证书和私钥。
- 签发上游服务器证书的 CA 证书。
上游证书的主体备选名称必须与网关作为 SNI 发送的主机名匹配。示例使用 upstream.test.local,网关节点必须能够将其解析到测试上游。
设置本指南使用的变量:
export API7_GATEWAY_GROUP_ID="default"
export API7_DASHBOARD="https://localhost:7443"
将网关组 ID 和控制台来源替换为适用于你的环境的值。发送请求前,将控制台 Token 导出为 API_KEY。
创建证书对象
创建网关将提供给上游的证书对象:
CLIENT_CERTIFICATE_ID=$(
jq -n \
--rawfile cert client.crt \
--rawfile key client.key \
'{name: "upstream-mtls-client", cert: $cert, key: $key}' |
curl -fsSk "${API7_DASHBOARD}/apisix/admin/certificates?gateway_group_id=${API7_GATEWAY_GROUP_ID}" \
-X POST \
-H "X-API-KEY: ${API_KEY}" \
-H "Content-Type: application/json" \
--data-binary @- |
jq -r '.value.id'
)
创建用于验证上游的 CA 证书对象:
UPSTREAM_CA_CERTIFICATE_ID=$(
jq -n \
--rawfile cert ca.crt \
'{name: "upstream-mtls-ca", cert: $cert}' |
curl -fsSk "${API7_DASHBOARD}/apisix/admin/ca_certificates?gateway_group_id=${API7_GATEWAY_GROUP_ID}" \
-X POST \
-H "X-API-KEY: ${API_KEY}" \
-H "Content-Type: application/json" \
--data-binary @- |
jq -r '.value.id'
)
确认两个请求都返回了 ID:
printf 'client certificate: %s\nupstream CA: %s\n' \
"${CLIENT_CERTIFICATE_ID}" \
"${UPSTREAM_CA_CERTIFICATE_ID}"
配置 HTTPS 上游
创建引用证书对象的服务。即使节点使用其他主机名或 IP 地址寻址,pass_host: rewrite 和 upstream_host 也会使网关发送与上游证书匹配的 SNI 名称。
jq -n \
--arg client_certificate "${CLIENT_CERTIFICATE_ID}" \
--arg ca_certificate "${UPSTREAM_CA_CERTIFICATE_ID}" \
'{
name: "upstream-mtls",
upstream: {
type: "roundrobin",
scheme: "https",
pass_host: "rewrite",
upstream_host: "upstream.test.local",
nodes: [
{
host: "upstream.test.local",
port: 8443,
weight: 100
}
],
client_certificate: $client_certificate,
ca_certificates: [$ca_certificate],
tls_verify: true
}
}' |
curl -fsSk "${API7_DASHBOARD}/apisix/admin/services/upstream-mtls?gateway_group_id=${API7_GATEWAY_GROUP_ID}" \
-X PUT \
-H "X-API-KEY: ${API_KEY}" \
-H "Content-Type: application/json" \
--data-binary @-
对于生产流量,请保持 tls_verify: true。将其设为 false 后,网关仍会提供客户端证书,但不会验证上游服务器的身份。
ADC v0.30.5 仅对已弃用的 upstream.tls 字段建模,无法配置 client_certificate、ca_certificates 和 tls_verify。请使用控制台或 Admin API 完成此工作流。
创建路由并验证
将路由关联到服务:
curl -fsSk "${API7_DASHBOARD}/apisix/admin/routes/upstream-mtls?gateway_group_id=${API7_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": ["/*"]
}'
通过网关发送请求:
curl -i "http://127.0.0.1:9080/anything"
请求成功证明网关已验证上游证书,且上游已接受网关的客户端证书。如需验证网关提供的是哪一张客户端证书,请检查上游访问日志,或在测试响应中输出已认证的客户端主体。
故障排查
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
启用上游 mTLS 后出现 502 Bad Gateway | 上游拒绝了网关的客户端证书。 | 验证客户端证书有效,且由上游信任的 CA 签发。检查上游 TLS 日志。 |
启用 tls_verify: true 时出现 502 Bad Gateway | 网关无法构建从上游证书到某个已引用 CA 证书的信任链。 | 引用签发上游证书的 CA,并包含所有必需的中间证书。 |
| 证书名称不匹配 | SNI 名称与上游证书的主体备选名称不匹配。 | 将 pass_host: rewrite 和 upstream_host 设置为证书覆盖的主机名。 |
| 连接被拒绝或出现明文协议错误 | 上游协议或端口错误。 | 使用 https 或 grpcs 以及上游服务的 TLS 端口。 |
| 无法删除证书对象 | 某个上游仍引用该对象。 | 删除证书对象之前,请从上游中移除证书 ID。 |
后续步骤
- 客户端 mTLS 认证:为连接网关的客户端配置 mTLS。
- SSL 证书:在 API7 网关中管理证书。
- 上游与负载均衡:了解上游配置字段。