故障现象
线上某kubernetes集群环境,使用nginx-ingress-controller暴露了一个service,为一个API服务,其中在ingress对象中使用了TLS证书,使用浏览器输入ingress对应的域名访问这个API service,请求正常,但是某程序使用SDK调用此service,始终无法拿到结果。
ingress的yaml文件类似如下
排查
-
sdk调用ingress时nginx-ingress-controller的日志信息,发现有如下报错
2020/04/01 04:49:01 [error] 12173#12173: *6506108 [lua] cert.lua:58: no cert found for 443, context: ssl_certificate_by_lua*, client: x.x.x.x, server: 0.0.0.0:443 2020/04/01 04:49:01 [crit] 12173#12173: *6506107 SSL_do_handshake() failed (SSL: error:1417A179:SSL routines:tls_post_process_client_hello:cert cb error) while SSL handshaking, client: x.x.x.1x6, server: 0.0.0.0:443
google上述信息,没有找到相关case。
-
使用tcpdump对nginx-ingress-controller抓包,分析sdk请求时候的数据流,拿到抓包文件后用wireshark打开,发现如下错误信息。
分析此数据包,可以看出SDK客户端发出client_hello请求后,服务器端立马返回了报错,SSL握手未完成。
3. 正常SSL握手过程如下图,结合抓包结果,可以发现服务器端nginx-ingress-controller没有返回server_hello信息给客户端,而是直接报错,日志信息则是提示未发现证书。
4. 分析此ingress对应的yaml文件一切正常,同时从现象看,浏览器请求正常的,只是sdk调用异常,问题应该和客户端关联,于是对sdk请求时和浏览器请求时分别抓包的client_hello包进行对比,发现不同,sdk的client_hello包,extension字段未传递server_name.在openresty也搜到如下信息
5. 综上,SDK客户端为非标准客户端,访问不带sni及server_name信息,导致nginx-ingress-controller无法通过sni和server_name找到对应的证书。
解决
长远看,需要SDK客户端解决请求不带server_name的问题。在nginx-ingress-controller无法通过sni和server_name找到对应的证书时,其会使用默认的端口证书,临时通过配置默认端口证书解决。在nginx-ingress-controller的启动参数,增加default-ssl-certificate指向此ingress引用的证书。