我在尝试使用 Nginx 作为反向代理时遇到问题,它似乎没有将客户端证书发送到后端资源。
这是服务器块配置:
server {
listen 443;
server_name my.server.tld;
location / {
proxy_pass https://my.realserver.tld;
proxy_redirect off;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_pass_request_body on;
proxy_pass_request_headers on;
}
include ssl_params;
ssl_client_certificate /etc/ssl/client-ca.pem;
ssl_verify_client optional;
}
SSL参数:
ssl on;
ssl_certificate /etc/ssl/private/fullchain.pem;
ssl_certificate_key /etc/ssl/private/privkey.pem;
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:50m;
ssl_session_tickets off;
ssl_protocols TLSv1.2;
ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256';
ssl_prefer_server_ciphers on;
add_header Strict-Transport-Security max-age=15768000;
nginx 日志显示后端资源出现 502,并且后端显示错误:exception reported by IO thread: null cert chain Caused by javax.net.ssl.SSLHandshakeException: null cert chain
后端是Tomcat服务器。
编辑:
我已经从源代码构建了 nginx 以尝试使用 ssl_preread 功能,但失败了。
以下是构建信息:
./nginx -V
nginx version: nginx/1.11.10
built by gcc 4.9.2 (Debian 4.9.2-10)
built with OpenSSL 1.0.1t 3 May 2016
TLS SNI support enabled
configure arguments: --with-stream_ssl_preread_module --with-cc-opt='-g -O2 -fstack-protector-strong -Wformat -Werror=format-security -D_FORTIFY_SOURCE=2' --with-ld-opt=-Wl,-z,relro --prefix=/usr/share/nginx --conf-path=/etc/nginx/nginx.conf --http-log-path=/var/log/nginx/access.log --error-log-path=/var/log/nginx/error.log --lock-path=/var/lock/nginx.lock --pid-path=/run/nginx.pid --http-client-body-temp-path=/var/lib/nginx/body --http-fastcgi-temp-path=/var/lib/nginx/fastcgi --http-proxy-temp-path=/var/lib/nginx/proxy --http-scgi-temp-path=/var/lib/nginx/scgi --http-uwsgi-temp-path=/var/lib/nginx/uwsgi --with-debug --with-pcre-jit --with-ipv6 --with-http_ssl_module --with-http_stub_status_module --with-http_realip_module --with-http_auth_request_module --with-http_addition_module --with-http_dav_module --with-http_geoip_module --with-http_gzip_static_module --with-http_image_filter_module --with-http_sub_module --with-http_xslt_module --with-mail --with-mail_ssl_module
我正在使用 Debian Jessie 8.7。
这是尝试使用 Alexey 的示例时失败的情况:
./nginx -t
nginx: [emerg] unknown directive "stream" in /etc/nginx/nginx.conf:11
nginx: configuration file /etc/nginx/nginx.conf test failed
答案1
此配置有效,但需要使用以下工具构建 nginx:ngx_stream_ssl_preread_module。
stream {
upstream yandex {
server 93.158.134.3:443;
}
upstream google {
server 64.233.164.113:443;
}
map $ssl_preread_server_name $upstream {
hostnames;
default yandex;
.google.com google;
}
server {
listen 12345;
ssl_preread on;
proxy_pass $upstream;
}
}
测试:
$ openssl s_client -quiet -connect localhost:12345 -servername yandex.ru
depth=3 C = PL, O = Unizeto Sp. z o.o., CN = Certum CA
verify return:1
depth=2 C = PL, O = Unizeto Technologies S.A., OU = Certum Certification Authority, CN = Certum Trusted Network CA
verify return:1
depth=1 C = RU, O = Yandex LLC, OU = Yandex Certification Authority, CN = Yandex CA
verify return:1
depth=0 C = RU, O = Yandex LLC, OU = ITO, L = Moscow, ST = Russian Federation, CN = *.wfarm.yandex.net
verify return:1
^C
$ openssl s_client -quiet -connect localhost:12345 -servername google.com
depth=3 C = US, O = Equifax, OU = Equifax Secure Certificate Authority
verify return:1
depth=2 C = US, O = GeoTrust Inc., CN = GeoTrust Global CA
verify return:1
depth=1 C = US, O = Google Inc, CN = Google Internet Authority G2
verify return:1
depth=0 C = US, ST = California, L = Mountain View, O = Google Inc, CN = *.google.com
verify return:1
^C
答案2
现在你已经将 Nginx 配置为第 7 层负载均衡器。Nginx 将终止 HTTPS 连接,然后创建另一个到后端服务器的连接。Nginx 可以访问客户端证书,但没有理由 Nginx 会选择传递客户端证书,除非它被告知,假设它有这个能力。
您需要的是第 4 层负载均衡器,这样 TCP 连接就会传递到后端服务器。这可以在 Nginx、HAProxy 或其他服务器上完成。Nginx 看起来就像这样(取自本文档)
stream {
server {
listen 127.0.0.1:12345;
proxy_pass backend.example.com:12345;
}
}
链接的文章向您展示如何在多个后端服务器之间进行负载平衡。
更新
您添加了一个新要求,即根据域名进行负载平衡。您不能使用 SNI,因为这是 HTTP(第 7 层)功能。在这种情况下,一种选择是为服务器分配多个 IP,并让客户端向正确的 IP 发出请求。
stream {
server {
listen 127.0.0.1:12345;
proxy_pass backend.example.com:12345;
}
server {
listen 127.0.0.2:12345;
proxy_pass backend2.example.com:12345;
}
}
替代解决方案
回答这个问题建议在您的代理块中执行以下操作可能会有效。值得一试。
proxy_set_header X-SSL-CERT $ssl_client_cert;