在AWS NLB后的NGINX Ingress中实现客户端证书认证
解决NLB终止TLS时NGINX Ingress的客户端证书认证问题
问题背景
我在EKS中部署了NGINX Ingress Controller,前端使用AWS NLB完成TLS终止,NGINX负责路由到后端服务。之前通过Ingress注解成功实现了HTTP基本认证,但切换到客户端证书认证时,配置的注解完全不生效:
生效的HTTP基本认证注解
nginx.ingress.kubernetes.io/auth-realm: Authentication Required nginx.ingress.kubernetes.io/auth-secret: basic-auth nginx.ingress.kubernetes.io/auth-type: basic nginx.ingress.kubernetes.io/proxy-body-size: 8m nginx.ingress.kubernetes.io/rewrite-target: /loki/$1text
不生效的客户端证书认证注解
nginx.ingress.kubernetes.io/auth-tls-error-page: http://www.example.com/error-cert.html nginx.ingress.kubernetes.io/auth-tls-pass-certificate-to-upstream: false nginx.ingress.kubernetes.io/auth-tls-secret: default/ca-secret nginx.ingress.kubernetes.io/auth-tls-verify-client: on nginx.ingress.kubernetes.io/auth-tls-verify-depth: 1
问题核心:NGINX的auth-tls-*注解依赖自身完成TLS握手以获取客户端证书,但当前TLS终止在NLB,NGINX接收的是明文HTTP请求,自然无法执行证书验证。同时我既不能在NLB层面配置客户端证书认证,也无法将TLS终止转移到NGINX Ingress。
可行解决方案:NLB转发客户端证书+NGINX自定义Lua验证
步骤1:配置NLB转发客户端证书
在AWS控制台或CLI中修改NLB的HTTPS监听:
- 找到对应HTTPS端口的监听配置
- 开启Forward client certificates选项,选择
Forward to target as HTTP header - 保留默认头名称
X-Forwarded-Client-Cert(自定义头需同步修改后续NGINX配置) - 保存配置后,NLB会将客户端证书的PEM编码通过该HTTP头传递给后端NGINX
步骤2:修改Ingress配置,添加Lua验证逻辑
放弃原有的auth-tls-*注解,改用server-snippet注入自定义NGINX配置,通过Lua脚本解析并验证NLB转发的证书:
apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: your-ingress-name annotations: # 保留原有业务必要注解 nginx.ingress.kubernetes.io/proxy-body-size: 8m nginx.ingress.kubernetes.io/rewrite-target: /loki/$1text # 添加客户端证书验证的自定义Lua配置 nginx.ingress.kubernetes.io/server-snippet: | # 预加载CA证书到共享内存 lua_shared_dict ca_certs 1m; init_by_lua_block { local f = assert(io.open("/etc/nginx/ssl/default/ca-secret/tls.crt", "r")) local ca_cert_data = f:read("*a") f:close() local ssl = require "ngx.ssl" local ca_cert, err = ssl.parse_pem_cert(ca_cert_data) if not ca_cert then ngx.log(ngx.ERR, "Failed to parse CA certificate: ", err) return end ngx.shared.ca_certs:set("ca_cert", ca_cert) } # 访问阶段验证客户端证书 access_by_lua_block { local client_cert_header = ngx.var.http_x_forwarded_client_cert -- 无证书直接拒绝 if not client_cert_header then ngx.exit(403) end local ssl = require "ngx.ssl" -- 解析头中的客户端证书 local client_cert, err = ssl.parse_pem_cert(client_cert_header) if not client_cert then ngx.log(ngx.ERR, "Failed to parse client certificate: ", err) ngx.exit(403) end -- 获取预加载的CA证书 local ca_cert = ngx.shared.ca_certs:get("ca_cert") if not ca_cert then ngx.log(ngx.ERR, "CA certificate not found") ngx.exit(500) end -- 验证证书签名合法性 local ok, err = ssl.verify_client_cert(client_cert, {ca_cert}) if not ok then ngx.log(ngx.ERR, "Client certificate verification failed: ", err) ngx.exit(403) end } spec: tls: # 此处TLS配置仅用于Ingress资源标识,实际TLS终止在NLB - hosts: - your-domain.com secretName: dummy-tls-secret # 任意存在的TLS Secret即可(无需有效证书) rules: - host: your-domain.com http: paths: - path: /(.*) pathType: Prefix backend: service: name: your-service-name port: number: 80
关键说明
/etc/nginx/ssl/default/ca-secret/tls.crt是NGINX Ingress自动挂载Secret的路径,格式为/etc/nginx/ssl/<namespace>/<secret-name>/tls.crt,需根据你的ca-secret所在命名空间调整- 官方NGINX Ingress镜像默认已包含
ngx_http_lua_module,无需额外编译 - 测试时使用带有合法客户端证书的请求,无证书或非法证书的请求会返回403
内容的提问来源于stack exchange,提问作者alarobric
相关产品推荐
相关产品推荐

