配置 APISIX 和上游之间的双向 TLS
双向 TLS (mTLS) 是一种双向 TLS,其中客户端和服务器相互验证身份。它通常在高安全性环境中实施,以防止未经授权的访问并加强安全性。
本指南将向你介绍如何在 APISIX 和上游服务之间配置 mTLS,使用 NGINX 作为示例上游服务。
前置条件
生成证书和密钥
生成证书颁发机构 (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")
配置上游服务
- Docker
- Kubernetes
在与 APISIX 相同的 Docker 网络中启动 NGINX 服务器作为示例上游服务:
docker run -d \
--name quickstart-nginx \
--network=apisix-quickstart-net \
-p 8443:8443 \
nginx
将 CA 证书、服务器证书公钥和私钥复制到 NGINX 中:
docker cp ca.crt quickstart-nginx:/var/ca.crt
docker cp server.crt quickstart-nginx:/var/server.crt
docker cp server.key quickstart-nginx:/var/server.key
在 NGINX 配置文件中配置一个监听 /hello 和端口 8443 的 HTTPS 服务器:
http {
# ...
server {
listen 8443 ssl;
// Annotate 1
server_name test.com;
// Annotate 2
ssl_certificate /var/server.crt;
// Annotate 3
ssl_certificate_key /var/server.key;
// Annotate 4
ssl_client_certificate /var/ca.crt;
// Annotate 5
ssl_verify_client on;
location /hello {
return 200 "Hello APISIX!";
}
}
}
❶ server_name:设置为 test.com 以与服务器证书 CN 值一致。
❷ ssl_certificate:配置服务器证书公钥 server.crt 的路径。
❸ ssl_certificate_key:配置服务器证书私钥 server.key 的路径。
❹ ssl_client_certificate:配置 CA 证书公钥 ca.crt 的路径。
❺ ssl_verify_client:设置为 on 以验证客户端证书。
重新加载 NGINX 服务器以应用配置更改:
docker exec quickstart-nginx nginx -s reload
为 NGINX 示例上游服务创建 Kubernetes secret:
kubectl create secret generic nginx-certs \
--from-file=server.crt=server.crt \
--from-file=server.key=server.key \
--from-file=ca.crt=ca.crt \
--namespace=ingress-apisix
为 NGINX 示例上游服务创建 ConfigMap:
apiVersion: v1
kind: ConfigMap
metadata:
namespace: ingress-apisix
name: nginx-config
data:
default.conf: |
server {
listen 8443 ssl;
// Annotate 1
server_name test.com;
// Annotate 2
ssl_certificate /etc/nginx/ssl/server.crt;
// Annotate 3
ssl_certificate_key /etc/nginx/ssl/server.key;
// Annotate 4
ssl_client_certificate /etc/nginx/ssl/ca.crt;
// Annotate 5
ssl_verify_client on;
location /hello {
return 200 "Hello APISIX!";
}
}
❶ server_name:设置为 test.com 以与服务器证书 CN 值一致。
❷ ssl_certificate:配置服务器证书公钥 server.crt 的路径。
❸ ssl_certificate_key:配置服务器证书私钥 server.key 的路径。
❹ ssl_client_certificate:配置 CA 证书公钥 ca.crt 的路径。
❺ ssl_verify_client:设置为 on 以验证客户端证书。
为 NGINX 示例上游服务部署和服务创建 Kubernetes 清单文件:
apiVersion: v1
kind: Service
metadata:
namespace: ingress-apisix
name: quickstart-nginx
spec:
type: NodePort
selector:
app: quickstart-nginx
ports:
- name: https
port: 8443
targetPort: 8443
---
apiVersion: apps/v1
kind: Deployment
metadata:
namespace: ingress-apisix
name: quickstart-nginx
spec:
replicas: 1
selector:
matchLabels:
app: quickstart-nginx
template:
metadata:
labels:
app: quickstart-nginx
spec:
containers:
- name: nginx
image: nginx
ports:
- containerPort: 8443
volumeMounts:
- name: config
mountPath: /etc/nginx/conf.d
- name: certs
mountPath: /etc/nginx/ssl
volumes:
- name: config
configMap:
name: nginx-config
- name: certs
secret:
secretName: nginx-certs
将配置应用到你的集群:
kubectl apply -f nginx-cm.yaml -f nginx.yaml
将 NGINX 服务端口转发到本地计算机的端口以进行验证:
kubectl port-forward service/quickstart-nginx 8443:8443 &
为了验证 NGINX 实例配置正确,请使用客户端证书和密钥向 Nginx 服务的路由发送请求:
curl -ik "https://127.0.0.1:8443/hello" --cert client.crt --key client.key
你应该收到 HTTP/1.1 200 OK 响应并看到以下消息:
Hello APISIX!
如果你在没有任何客户端证书或密钥的情况下向 Nginx 服务的路由发送请求:
curl -ik "https://127.0.0.1:8443/hello"
你应该收到 HTTP/1.1 400 Bad Request 响应。
为 APISIX 配置 mTLS
你可以选择将 client.crt 和 client.key 的内容加载到环境变量中:
client_cert=$(cat client.crt)
client_key=$(cat client.key)
- Admin API
- Ingress Controller
创建到 NGINX 服务器的路由:
curl -i "http://127.0.0.1:9180/apisix/admin/routes" -X PUT -d '
{
"id": "mtls-nginx",
"uri": "/hello",
"upstream": {
// Annotate 1
"scheme": "https",
"nodes": {
// Annotate 2
"quickstart-nginx:8443":1
},
"tls": {
// Annotate 3
"client_cert": "'"${client_cert}"'",
// Annotate 4
"client_key": "'"${client_key}"'"
},
"type": "roundrobin"
}
}'
❶ scheme:设置为 https。
❷ nodes:设置为 NGINX 服务器主机名 quickstart-nginx 和端口 8443。
❸ tls.client_cert:配置证书公钥 client.crt。
❹ tls.client_key:配置证书私钥 client.key。
- Gateway API
- APISIX CRD
APISIX Ingress Controller 目前不支持使用 Gateway API 配置上游 mTLS。
为 APISIX 创建 Kubernetes secret:
kubectl create secret tls test-mtls-secret \
--cert=client.crt \
--key=client.key \
--namespace=ingress-apisix
为到 NGINX 服务器的路由创建 Kubernetes 清单文件:
apiVersion: apisix.apache.org/v2
kind: ApisixUpstream
metadata:
namespace: ingress-apisix
name: quickstart-nginx # 应与 Service 名称保持一致
spec:
ingressClassName: apisix
scheme: https
tlsSecret:
name: test-mtls-secret
namespace: ingress-apisix
---
apiVersion: apisix.apache.org/v2
kind: ApisixRoute
metadata:
namespace: ingress-apisix
name: mtls-nginx
spec:
ingressClassName: apisix
http:
- name: mtls-nginx
match:
paths:
- /hello
backends:
- serviceName: quickstart-nginx
servicePort: 8443
将配置应用到你的集群:
kubectl apply -f upstream-mtls-route.yaml
验证 APISIX 和上游服务之间的 mTLS
向路由发送请求:
curl -ikv "http://127.0.0.1:9080/hello"
你应该收到 HTTP/1.1 200 OK 响应并看到以下消息:
Hello APISIX!
这验证了 APISIX 和上游服务之间已成功建立 mTLS。
下一步
你已经了解了如何在 APISIX 和上游服务之间设置 mTLS。APISIX 还支持客户端和 APISIX 之间的 mTLS。请参阅 配置客户端和 APISIX 之间的 mTLS 以了解更多信息。