在客户端和 API7 网关之间配置 mTLS
双向 TLS (mTLS) 是传输层安全性 (TLS) 的一种增强形式,也是一种在客户端和服务器之间相互验证的方法。这是通过被称为“握手”的过程来实现的,在握手中,双方交换并验证证书以确认彼此的身份。
本指南将向你展示如何在客户端和 API7 网关之间配置 mTLS,从而防止未经授权的访问并加强安全性。
前置条件
- 安装 API7 企业版。
- 发布你的第一个 API,并确保你的服务具有主机名
test.com。
生成证书和密钥
-
生成证书颁发机构 (CA) 的密钥和证书:
openssl genrsa -out ca.key 2048
openssl req -new -x509 -days 36500 -sha256 \
-key ca.key \
-out ca.crt \
-subj "/CN=MyTestCA" \
-extensions v3_ca \
-config <(printf "[req]\ndistinguished_name=req\n[ v3_ca ]\nbasicConstraints=critical,CA:TRUE\nkeyUsage=critical,keyCertSign,cRLSign\nsubjectKeyIdentifier=hash\nauthorityKeyIdentifier=keyid:always,issuer") -
生成密钥和证书签名请求 (CSR):
openssl genrsa -out server.key 2048
openssl req -new -sha256 \
-key server.key \
-out server.csr \
-subj "/CN=test.com" -
使用 CA 证书签署服务器的 CSR,以生成服务器证书:
openssl x509 -req -days 36500 -sha256 \
-in server.csr \
-CA ca.crt -CAkey ca.key -CAcreateserial \
-out server.crt \
-extensions v3_req \
-extfile <(printf "[v3_req]\nbasicConstraints=CA:FALSE\nkeyUsage=digitalSignature,keyEncipherment\nextendedKeyUsage=serverAuth") -
生成客户端的密钥和证书签名请求 (CSR):
openssl genrsa -out client.key 2048
openssl req -new -sha256 \
-key client.key \
-out client.csr \
-subj "/CN=CLIENT" -
使用 CA 证书签署客户端的 CSR,以生成客户端证书:
openssl x509 -req -days 36500 -sha256 \
-in client.csr \
-CA ca.crt -CAkey ca.key -CAcreateserial \
-out client.crt \
-extensions v3_req \
-extfile <(printf "[v3_req]\nbasicConstraints=CA:FALSE\nkeyUsage=digitalSignature,keyEncipherment\nextendedKeyUsage=clientAuth")
上传证书
如果你希望引用来自密钥管理提供商的 SSL 证书,请参阅引用 HashiCorp Vault 中的密钥、引用 AWS Secrets Manager 中的密钥或引用 Kubernetes Secret 中的密钥。
- Dashboard
- Ingress Controller
创建证书
- 从侧边导航栏选择你的网关组下的 证书,进入 SSL 证书 选项卡。
- 点击 新增 SSL 证书。
- 在弹出的对话框中,执行以下操作:
- 在 名称字段中,输入
Test SSL Certificate。 - 在 证书字段中,上传
server.crt文件。 - 在 私钥字段中,上传
server.key文件。 - 点击 新增。
- 从侧边导航栏选择你的网关组下的 证书,然后点击 CA 证书选项卡。
- 点击 新增 CA 证书。
- 在弹出的对话框中,执行以下操作:
- 在 名称字段中,输入
Test CA Certificate。 - 在 证书字段中,上传
ca.crt文件。 - 点击 新增。
创建 SNI
- 从侧边导航栏选择你的网关组下 的 SNIs。
- 点击 新增 SNI。
- 在弹出的对话框中,执行以下操作:
- 在 名称字段中,输入
Test SNI。 - 在 域名字段中,输入
test.com。 - 在 SSL 协议字段中,选择
TLS 1.2和TLS 1.3。 - 在 SSL 证书字段中,选择你之前创建的
Test SSL Certificate。 - 打开 mTLS 开关。
- 在 CA 证书字段中,选择你之前创建的
Test CA Certificate。 - 点击 新增。
- 在 **Usage(使用情况)**字段中,你应该看到一个主机名为
test.com的匹配的已发布服务。然后你就可以开始验证了。
- Gateway API
- APISIX CRD
API7 Ingress Controller 目前不支持使用 Gateway API 配置客户端和 API7 网关之间的 mTLS。
使用提供的证书和私钥文件生成一个名为 test-tls-secret 的 Kubernetes TLS secret,并将清单输出到 api7 命名空间下的 secret.yaml 中:
kubectl create secret tls test-mtls-secret \
--cert=server.crt \
--key=server.key \
--namespace=api7 \
--dry-run=client -o yaml > secret.yaml
从 ca.crt 文件生成一个名为 test-ca-secret 的通用 Kubernetes secret,并将清单输出到 api7 命名空间下的 ca-secret.yaml 中:
kubectl create secret generic test-ca-secret \
--from-file=cert=ca.crt \
--namespace=api7 \
--dry-run=client -o yaml > ca-secret.yaml
将这些 secret 配置应用到你的集群中:
kubectl apply -f secret.yaml -f ca-secret.yaml
生成另一个 Kubernetes 资源清单文件,以在网关上配置这些 mTLS secrets:
apiVersion: apisix.apache.org/v2
kind: ApisixTls
metadata:
namespace: api7
name: test-mtls
spec:
ingressClassName: apisix
hosts:
- test.com
secret:
name: test-mtls-secret
namespace: api7
client:
caSecret:
name: test-ca-secret
namespace: api7
depth: 1
将配置应用到你的集群中:
kubectl apply -f client-mtls.yaml
验证客户端与 API7 网关之间的 mTLS
使用客户端证书
- Docker
- Kubernetes
首先,通过端口转发将服务端口暴露到你的本地机器:
kubectl port-forward svc/apisix-gateway 9443:443 &
使用客户端证书向 https://test.com:9443/ip 发送请求,并将 test.com 解析到 127.0.0.1。
curl -ikv --resolve "test.com:9443:127.0.0.1" "https://test.com:9443/ip" \
--cert client.crt --key client.key
类似以下内容的 mTLS 握手信息,可以证明客户端与 API7 网关之间的 mTLS 连接已成功建立:
* Added test.com:9443:127.0.0.1 to DNS cache
* Hostname test.com was found in DNS cache
* Trying 127.0.0.1:9443...
* Connected to test.com (127.0.0.1) port 9443
* ALPN: curl offers h2,http/1.1
Handling connection for 9443
* (304) (OUT), TLS handshake, Client hello (1):
* (304) (IN), TLS handshake, Server hello (2):
* (304) (IN), TLS handshake, Unknown (8):
* (304) (IN), TLS handshake, Request CERT (13):
* (304) (IN), TLS handshake, Certificate (11):
* (304) (IN), TLS handshake, CERT verify (15):
* (304) (IN), TLS handshake, Finished (20):
* (304) (OUT), TLS handshake, Certificate (11):
* (304) (OUT), TLS handshake, CERT verify (15):
* (304) (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / AEAD-AES256-GCM-SHA384 / [blank] / UNDEF
* ALPN: server accepted h2
* Server certificate:
* subject: CN=test.com
* start date: Jul 30 07:40:58 2025 GMT
* expire date: Jul 6 07:40:58 2125 GMT
* issuer: CN=MyTestCA
* SSL certificate verify result: unable to get local issuer certificate (20), continuing anyway.
* using HTTP/2
* [HTTP/2] [1] OPENED stream for https://test.com:9443/ip
* [HTTP/2] [1] [:method: GET]
* [HTTP/2] [1] [:scheme: https]
* [HTTP/2] [1] [:authority: test.com:9443]
* [HTTP/2] [1] [:path: /ip]
* [HTTP/2] [1] [user-agent: curl/8.6.0]
* [HTTP/2] [1] [accept: */*]
> GET /ip HTTP/2
> Host: test.com:9443
> User-Agent: curl/8.6.0
> Accept: */*
>
< HTTP/2 200
HTTP/2 200
...
{
"origin": "127.0.0.1, 101.44.80.31"
}
请注意,API7 网关和客户端在握手过程中成功验证了彼此的证书并建立了连接。
不使用客户端证书
向 https://test.com:9443/ip 发送请求,但不提供客户端证书。
curl -ikv --resolve "test.com:9443:127.0.0.1" "https://test.com:9443/ip"
由于缺少客户端证书,mTLS 握手应该会失败。