GKE Nginx Ingress指定Host后出现400无SSL证书错误排查
我来帮你拆解并解决这个问题——你遇到的400错误(提示“未发送所需SSL证书”),核心是自签名客户端证书与Nginx Ingress的SNI(Server Name Indication)配置不兼容导致的,以下是具体分析和解决步骤:
问题根源分析
当你在Ingress中指定rules.host: example.com时,Nginx Ingress Controller会为这个域名生成独立的SNI Server块。此时客户端请求通过Host: example.com触发SNI匹配,而你的自签名客户端证书缺少关键配置(比如与目标域名匹配的SAN字段),或者Nginx在SNI场景下没有正确加载客户端证书验证规则;而不指定Host时,请求匹配到Nginx的默认Server块,验证逻辑不受SNI限制,所以能正常执行。另外Cloudflare证书自带正确的SAN配置,因此不会出现这个问题。
具体解决步骤
1. 检查并修复客户端证书的SAN字段
Nginx在SNI场景下会验证客户端证书的**Subject Alternative Name(SAN)**是否与请求的Host匹配,自签名证书默认不会添加这个字段,这是最常见的原因。
验证当前证书的SAN:
openssl x509 -in my-cert.pem -text -noout | grep -A 3 "Subject Alternative Name"
如果没有输出,说明证书缺少SAN字段。
重新生成带SAN的客户端证书:
- 创建一个扩展配置文件
client-ext.cnf:
[client] extendedKeyUsage = clientAuth subjectAltName = DNS:example.com, IP:YOUR_MY_IP_VALUE # 替换为你的实际IP
- 修改你的证书生成脚本,引入这个配置:
#!/bin/bash # 使用方式: ./build-client-cert.sh <证书名称> <有效期天数> openssl genrsa -out client.key 4096 openssl req -new -key client.key -out client.req -config client-ext.cnf # 添加-config参数 openssl x509 -req -in client.req -CA rootCA.crt -CAkey rootCA.key -set_serial 101 -extensions client -extfile client-ext.cnf -days $2 -outform PEM -out client.cer # 添加-extfile参数 openssl pkcs12 -export -inkey client.key -in client.cer -out $1.p12 rm client.key client.cer client.req
重新生成证书后,用之前的命令验证SAN字段是否存在。
2. 确认Ingress的CA证书Secret配置正确
确保存储可信CA的Secretclient-cert-api-ingress中的rootCA.crt是完整且格式正确的:
- 检查Secret内容:
kubectl get secret client-cert-api-ingress -n development -o yaml
确认tls.crt字段是你的rootCA.crt的Base64编码(可以用base64 -d解码验证)。
- 如果配置有误,重新创建Secret:
kubectl delete secret client-cert-api-ingress -n development kubectl create secret generic client-cert-api-ingress -n development --from-file=tls.crt=rootCA.crt
3. 验证Nginx Controller的Server块配置
进入Nginx Controller Pod,检查对应example.com的Server块是否加载了客户端证书验证规则:
# 替换为你的Controller Pod名称 kubectl exec -it <nginx-ingress-controller-pod-name> -n ingress-nginx -- cat /etc/nginx/nginx.conf | grep -A 20 "server_name example.com"
确保输出中包含以下配置:
ssl_client_certificate /etc/ingress-controller/ssl/development-client-cert-api-ingress.pem; ssl_verify_client on; ssl_verify_depth 1;
如果没有这些配置,说明Ingress的Annotations没有正确应用,尝试删除并重新创建Ingress资源:
kubectl delete ingress api-ingress -n development kubectl apply -f your-ingress-config.yaml
同时查看Controller日志排查错误:
kubectl logs <nginx-ingress-controller-pod-name> -n ingress-nginx
4. 优化curl请求参数
使用--resolve参数让curl正确发送SNI请求,避免手动Host头可能的冲突:
curl -k --cert my-cert.pem --key my-cert.key https://example.com/ --resolve example.com:443:MY_IP
这个命令会强制curl将example.com:443解析到你的MY_IP,同时自动发送正确的SNI和Host头。
总结
这个问题的核心是自签名客户端证书缺少与请求Host匹配的SAN字段,导致Nginx在SNI场景下拒绝证书。通过添加SAN字段、确认CA证书配置、验证Nginx Server块这几个步骤,就能解决指定Host时的400错误。
内容的提问来源于stack exchange,提问作者Montoya

