Nginx作为安全LDAP正向代理的证书配置问题及Kubernetes容器化场景咨询
你遇到的证书不匹配报错,核心原因其实是当前Nginx配置只是做了纯TCP流量转发,完全没在代理层处理TLS握手。当你用ldaps://localhost连接时,客户端会直接和后端的db.debian.org:636建立TLS连接——后端返回的证书是db.debian.org的,但客户端访问的是localhost,hostname自然对不上,就触发了校验失败。
下面分两种代理模式给你解决方案,再聊聊Kubernetes容器化场景的处理方式:
一、推荐方案:让Nginx做TLS终止代理
这种模式下,客户端先和Nginx完成TLS握手(Nginx返回匹配客户端访问域名的证书),之后Nginx再和后端LDAP服务器建立TLS连接。这样既解决了证书匹配问题,也能在代理层做一些安全控制。
1. 准备适配的证书
你需要给Nginx配置一张客户端信任的证书,证书的通用名称(CN)或主题备用名称(SAN)必须包含客户端访问的域名:
- 如果是本地测试,要包含
localhost; - 如果是K8s集群内访问,要包含你的Service域名(比如
ldap-proxy.default.svc.cluster.local)。
测试阶段可以自己生成自签名证书,用这条命令就行:
openssl req -x509 -newkey rsa:4096 -keyout nginx-ldap.key -out nginx-ldap.crt -days 365 -nodes -subj "/CN=localhost" -addext "subjectAltName=DNS:localhost,DNS:ldap-proxy.default.svc.cluster.local"
2. 调整Nginx的Stream配置
修改你的Nginx配置,让监听636端口时终止TLS,再代理到后端LDAPS服务:
worker_processes auto; error_log /var/log/nginx/error.log notice; pid /tmp/nginx.pid; events { worker_connections 1024; } http { include /etc/nginx/mime.types; default_type application/octet-stream; access_log /var/log/nginx/access.log main; sendfile on; keepalive_timeout 65; include /etc/nginx/conf.d/*.conf; server { listen 8080; location / { root /usr/share/nginx/html; index index.html; } } } stream { server { listen 636 ssl; # 配置证书和密钥路径 ssl_certificate /path/to/nginx-ldap.crt; ssl_certificate_key /path/to/nginx-ldap.key; # 配置安全的TLS协议和加密套件 ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers HIGH:!aNULL:!MD5; # 代理到后端LDAPS服务 proxy_pass db.debian.org:636; } }
这样配置后,客户端连接ldaps://localhost时,会和Nginx完成TLS握手(证书CN/SAN匹配localhost),不会再报错。Nginx会自动和后端db.debian.org:636建立TLS连接,处理后端的证书校验(生产环境建议让Nginx信任后端的根证书,避免安全风险)。
二、临时方案:纯TCP透传代理(不推荐生产用)
如果只是临时测试,不想改证书,可以保持当前的纯TCP转发配置,但需要客户端关闭证书校验(就是你用LDAPTLS_REQCERT=never的方式),或者修改客户端的hosts文件,把db.debian.org指向localhost,这样客户端访问ldaps://db.debian.org(实际连localhost),证书hostname就匹配了。但这种方式安全性极低,只适合临时测试。
三、Kubernetes容器化场景的处理
当Nginx跑在K8s容器里,并且通过Service暴露为*.svc.cluster.local域名时,需要做这几步:
1. 适配K8s的证书配置
生成证书时,一定要把Service域名加到SAN字段里(比如ldap-proxy.default.svc.cluster.local),然后把证书和密钥创建成K8s Secret:
kubectl create secret tls nginx-ldap-tls --cert=nginx-ldap.crt --key=nginx-ldap.key
2. 在Deployment中挂载证书
修改Nginx的Deployment配置,把刚才创建的Secret挂载到容器里:
apiVersion: apps/v1 kind: Deployment metadata: name: nginx-ldap-proxy spec: replicas: 1 selector: matchLabels: app: nginx-ldap-proxy template: metadata: labels: app: nginx-ldap-proxy spec: containers: - name: nginx image: nginx:alpine volumeMounts: - name: nginx-ldap-tls mountPath: /etc/nginx/tls readOnly: true - name: nginx-config mountPath: /etc/nginx/nginx.conf subPath: nginx.conf ports: - containerPort: 636 volumes: - name: nginx-ldap-tls secret: secretName: nginx-ldap-tls - name: nginx-config configMap: name: nginx-ldap-proxy-config
这里同时把Nginx配置文件通过ConfigMap挂载(你需要提前把调整后的Nginx配置创建成ConfigMap)。
3. 调整Nginx配置中的证书路径
因为K8s的TLS Secret默认挂载的文件名是tls.crt和tls.key,所以Nginx配置里的证书路径要改成:
ssl_certificate /etc/nginx/tls/tls.crt; ssl_certificate_key /etc/nginx/tls/tls.key;
4. 配置Service暴露代理
创建一个ClusterIP类型的Service,让集群内的客户端可以通过域名访问:
apiVersion: v1 kind: Service metadata: name: ldap-proxy spec: selector: app: nginx-ldap-proxy ports: - port: 636 targetPort: 636
集群内的客户端就可以用ldaps://ldap-proxy.default.svc.cluster.local访问,证书的SAN包含这个域名,不会出现匹配问题。
备注:内容来源于stack exchange,提问作者Michael Chudinov

