如何配置K8s Nginx Ingress的Proxy SSL #
背景 #
我们在部署K8s的服务中,有一个Pod是开源的框架,其后端接口要求必须配置SSL证书。 在不修改源代码的前提下(主要是为了向后兼容),我们尝试在Ingress层面对后端的Pods的SSL证书 进行兼容。 因此,我们在理顺相关的流程后,将相关的过程和经验撰写为本文。
证书生成 #
我们的开源后端有三个配置(排查很久后才发现的3,最开始只知晓1和2):
- Cert:证书的公钥
- Key:证书的私钥
- CA:CA证书(证书颁发机构证书)
我们是通过如下命令生成的相关证书(自颁发):
openssl req -x509 -nodes -newkey ec \
-pkeyopt ec_paramgen_curve:secp521r1 \
-pkeyopt ec_param_enc:named_curve \
-subj '/CN=eng.example.com' \
-keyout ./tls-key.pem -out ./tls-cert.pem -sha256 -days 3650 \
-addext "subjectAltName = DNS:localhost,DNS:example.com,DNS:*.example.com,DNS:eng.example.com,DNS:demo1.example-eng.svc.cluster.local" \
-addext "extendedKeyUsage = serverAuth,clientAuth" \
-addext "keyUsage = digitalSignature, keyCertSign, keyAgreement"
避坑点 #
需要使用clientAuth
#
当作为Client(Ingress Controller的角色)的时候,如果你要使用相关的证书,是需要声明为clientAuth的。如若不然,则会出现报错。
需要设置多个subjectAltName
#
因为我们在Pod中部署,同时在调试的时候,可能需要使用不同的Hostname(主机名)来测试Endpoint的可用性。
因此,我们需要设置多个DNS来确保我们可以使用这些地址,比如上述命令中的:
localhost,example.com,*.example.com,eng.example.com还有demo1.example-eng.svc.cluster.local
最后一个是我们在Pod中进行测试Service表示的Pod所用到的。
因此,通过设置多个DNS,我们可以使用不同的Hostname,来测试访问同一个服务。
Nginx Ingress Controller Proxy SSL相关设置 #
我们的Kubernetes的Ingress设置如下:
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: example-ingress
labels:
app: example
annotations:
nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
nginx.ingress.kubernetes.io/ssl-verify: "true"
nginx.ingress.kubernetes.io/proxy-ssl-secret: "example-eng/example-ingress-proxy-ssl"
nginx.ingress.kubernetes.io/proxy-ssl-verify: "on"
nginx.ingress.kubernetes.io/proxy-ssl-verify-depth: "1"
nginx.ingress.kubernetes.io/proxy-ssl-name: "eng.example.com"
nginx.ingress.kubernetes.io/proxy-ssl-server-name: "on"
spec:
ingressClassName: nginx
rules:
- host: eng.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: example
port:
name: http
我们逐一来进行介绍:
nginx.ingress.kubernetes.io/backend-protocol: "HTTPS":表明,我们的后端(Pod)的接口是使用的HTTPS协议。nginx.ingress.kubernetes.io/ssl-verify: "true"以及nginx.ingress.kubernetes.io/proxy-ssl-verify: "on":表示我们希望启用SSL的验证(Ingress Controller于后端Pod之间的网络流量认证。nginx.ingress.kubernetes.io/proxy-ssl-secret: "example-eng/example-ingress-proxy-ssl": 这个是表明,我们的Ingress Controller所使用的相关证书对应的K8s Secret,格式为namespace/secret-name。(后面我们展开讲)nginx.ingress.kubernetes.io/proxy-ssl-name: "eng.example.com"以及nginx.ingress.kubernetes.io/proxy-ssl-server-name: "on":表示,我们希望采用的后端的名称(类似上述证书中对应的DNS)。
生成secret #
我们是通过下述代码来生成我们需要的密钥的:
kubectl create secret generic example-ingress-proxy-ssl \
--from-file=tls.crt=tls-cert.pem \
--from-file=tls.key=tls-key.pem \
--from-file=ca.crt=tls-cert.pem \
-n example-eng \
--dry-run=client -o yaml | kubectl apply -f -
其中,tls.crt我们Client的公钥,tls.key是我们的私钥,ca.crt是我们信任的根证书(这里我们使用了和我们公钥同样的配置,因为我们自签发的证书)
小结 #
通过使用上面的配置,我们就告诉了 Ingress Controller,我们希望使用相关的配置,与我们的Pod后端进行SSL的认证。
问题排查 #
深入到Ingress Controller中 #
我们在排查问题的时候,可以深入到Ingress Controller的Pod中,默认在nginx-ingress命名空间中。
我们通过:
kubectl -n nginx-ingress get pods
上面的命令可以查看到我们目前的Ingress的情况,如结果:
NAME READY STATUS RESTARTS AGE
ingress-nginx-controller-9756f5bd9-vr9dd 1/1 Running 0 19d
在排查过程中,我们一方面可以通过kubectl logs命令来监控Ingress的Log输出:
kubectl -n nginx-ingress logs -f ingress-nginx-controller-9756f5bd9-vr9dd
另一方面,我们可以深入到Ingress Controller的Pod中。
深入到Pod中 #
通过命令:
kubectl -n nginx-ingress exec -it ingress-nginx-controller-9756f5bd9-vr9dd -- bash
我们可以登陆到我们的Pod中,然后我们检查一下我们的Secret的配置,默认secret在:
/etc/ingress-controller/ssl
这个文件夹中,如果我们配置正确,我们应该可以看到上面提到的secret:
example-ingress-proxy-ssl。
通过vi或者cat命令,我们可以检查我们之前配置的tls.crt等是否正确被加载到了Controller中。
检查Ingress Config #
除了查看SSL的情况,我们也可以通过打开
/etc/nginx/nginx.conf
文件,确定我们的相关Ingress声明,是否有被正确的转换为Nginx配置:
cat /etc/nginx/nginx.conf | grep example-eng -A 100
我们可以看到相关输出:
proxy_ssl_trusted_certificate /etc/ingress-controller/ssl/example-eng-example-ingress-proxy-ssl.pem;
proxy_ssl_ciphers DEFAULT;
proxy_ssl_protocols TLSv1.2;
proxy_ssl_verify on;
proxy_ssl_verify_depth 1;
proxy_ssl_name eng.example.com;
proxy_ssl_server_name on;
proxy_ssl_certificate /etc/ingress-controller/ssl/example-eng-example-ingress-proxy-ssl.pem;
proxy_ssl_certificate_key /etc/ingress-controller/ssl/example-eng-example-ingress-proxy-ssl.pem;
这说明,我们的配置是生效的,Nginx正在使用相关的配置来和我们的Pod服务器进行SSL的沟通。
测试访问 #
我们为了继续排查,可以在Ingress Controller中,对相关的后端服务进行测试,如下命令;
curl -v --tlsv1.3 \
--cacert ./ca \
--cert ./cert \
--key ./key \
-vvv \
https://demo1.example-eng.svc.cluster.local:4443
请注意,上面的./ca, ./cert以及./key等文件,需要创建好进行测试。
其他 #
由于我们最前端的CDN层,我们使用的是CloudFlare,因此,我们对外使用的是CloudFlare提供的SSL证书。 然后由其将流量转发给我们的后端Kubernetes集群,再由Ingress转发给我们的Pod,最终由Pod对外提供服务。
因此,在我们修复了上述问题后,我们可以顺利使用Cloudflare来访问我们的后端资源。 (如果SSL异常,我们大概率会看到502的报错信息)
参考 #
总结 #
我们本文主要介绍了,如何配置使用Nginx Ingress Controller 来与必须兼顾SSL的Pod后端接口进行通信。 因为网上相关材料较少,我们整理出来以便大家参考。