大家好,我是 WeiyiGeek,一名深耕安全运维开发(SecOpsDev)领域的技术从业者,致力于探索DevOps与安全的融合(DevSecOps),自动化运维工具开发与实践,企业网络安全防护,欢迎各位道友一起学习交流、一起进步 ,若此文对你有帮助,一定记得点个关注⭐与小红星❤️或加入到作者知识星球『 全栈工程师修炼指南』,转发收藏学习不迷路 。
Nginx 反向代理中与上游服务端 SSL 双向认证指令浅析与配置
描述:上一篇《Nginx | HTTPS 加密传输:客户端与Nginx服务端 SSL 双向认证实践》中,讲解了客户端与 Nginx 服务端双向认证相关指令参数并进行了实践,以保证访问身份验证以及数据安全性,这也是针对某些有点对点通讯安全要求的场景,另外还有一种场景,比如在 Nginx 反向代理中与上游服务端 SSL 双向认证,通常应用于某些敏感的 API 接口,同样为了保证身份合法性和数据传输的安全性,就需要在 Nginx 与上游服务端之间建立双向认证,这将是本小节要讲解的内容。
温馨提示:若文章代码块中存在乱码或不能复制,请联系作者,也可通过文末的阅读原文链接,加入知识星球中阅读,原文链接:https://articles.zsxq.com/id_wwnjk3rmwroa.html
同样,在 Nginx 反向代理添加 PROXY_SSL 相关指令与上游服务器连接认证,如下所示:
proxy_ssl_certificate
- : 指定带有PEM格式证书的文件,该证书用于向代理HTTPS服务器进行身份验证。
Syntax: proxy_ssl_certificate file;
Default: —
Context: http, server, location
proxy_ssl_certificate_key
- : 指定带有PEM格式的密钥文件,用于向代理HTTPS服务器进行身份验证。
Syntax: proxy_ssl_certificate_key file;
Default: —
Context: http, server, location
proxy_ssl_certificate_cache
- : 指定用于存储SSL会话的共享内存区域,以加快后续连接的速度。
Syntax: proxy_ssl_certificate_cache off;
proxy_ssl_certificate_cache max=N [inactive=time] [valid=time];
Default: proxy_ssl_certificate_cache off;
Context: http, server, location
# 参数说明:
max 设置该高速缓存中元素的最大数量,当缓存达到最大数量时,它将根据inactive参数删除最少使用(LRU)的元素
inactive 定义一个时间,如果元素在该时间内未被访问,则该元素将从该高速缓存中删除,缺省该时间为10秒
valid 定义一个时间,在此时间内,该高速缓存中的元素被认为是有效的,并且可以重用,缺省该时间为60秒
off 禁用该高速缓存
# 示例演示
proxy_ssl_certificate_cache max=1000 inactive=20s valid=1m;
proxy_ssl_password_file
- : 指定包含PEM格式密钥文件密码的文件,其中每个密码都在单独的行上指定,在加载密钥时使用。
Syntax: proxy_ssl_password_file file;
Default: —
Context: http, server, location
proxy_ssl_protocols
- : 为对代理HTTPS服务器的请求启用指定的协议。
Syntax: proxy_ssl_protocols [SSLv2] [SSLv3] [TLSv1] [TLSv1.1] [TLSv1.2] [TLSv1.3];
Default: proxy_ssl_protocols TLSv1.2 TLSv1.3;
Context: http, server, location
proxy_ssl_ciphers
- : 指定用于对代理HTTPS服务器进行请求的加密套件。
Syntax: proxy_ssl_ciphers ciphers;
Default: proxy_ssl_ciphers DEFAULT;
Context: http, server, location
proxy_ssl_name
- : 允许覆盖服务器名称,该服务器名称用于验证代理HTTPS服务器的证书,并在与代理HTTPS服务器建立连接时通过SNI传递。
Syntax: proxy_ssl_name name;
Default: proxy_ssl_name $proxy_host;
Context: http, server, location
proxy_ssl_server_name
- : 启用或禁用在与代理HTTPS服务器建立连接时通过TLS服务器名称指示扩展(SNI,RFC 6066)传递服务器名称。
Syntax: proxy_ssl_server_name on | off;
Default: proxy_ssl_server_name off;
Context: http, server, location
proxy_ssl_trusted_certificate
- : 指定PEM格式的可信CA证书文件,用于验证代理HTTPS服务器的证书。
Syntax: proxy_ssl_trusted_certificate file;
Default: —
Context: http, server, location
proxy_ssl_verify
- : 启用或禁用对代理HTTPS服务器证书的验证。
Syntax: proxy_ssl_verify on | off;
Default: proxy_ssl_verify off;
Context: http, server, location
proxy_ssl_verify_depth
- : 设置验证代理HTTPS服务器证书时使用的深度。
Syntax: proxy_ssl_verify_depth number;
Default: proxy_ssl_verify_depth 1;
Context: http, server, location
proxy_ssl_session_reuse
- : 确定在使用代理服务器时是否可以重用SSL会话。如果日志中出现错误“摘要检查失败”,请尝试禁用会话重用。
Syntax: proxy_ssl_session_reuse on | off;
Default: proxy_ssl_session_reuse on;
Context: http, server, location
proxy_ssl_crl
- : 指定PEM格式的具有吊销证书(CRL)的文件,用于验证代理HTTPS服务器的证书。
Syntax: proxy_ssl_crl file;
Default:—
Context: http, server, location
proxy_ssl_conf_command
- : 在与代理HTTPS服务器建立连接时设置任意OpenSSL配置命令。
Syntax: proxy_ssl_conf_command name value;
Default: —
Context: http, server, location
proxy_ssl_key_log
- : 指启用代理HTTPS服务器连接SSL密钥的日志记录并指定密钥日志文件的路径, 密钥以与Wireshark兼容的SSLKEYLOGFILE格式记录(注意:商业版本才支持)
Syntax: proxy_ssl_key_log path;
Default: —
Context: http, server, location
示例演示
步骤 01.作者将使用 213 和 214 两台主机做为演示,其中 213 作为上游服务端,214 作为反向代理服务器,先将前面发布的《Nginx | 核心知识150讲,百万并发下性能优化之SSL证书签发与HTTPS加密传输实践笔记》文章中生成的 ca、server、client 证书和私钥文件拷贝到 Nginx 的 /usr/local/nginx/certs/ 目录下,如下所示:
# 214 主机
cp /tmp/certs/ca.crt /tmp/certs/ca.key /usr/local/nginx/certs/
cp /tmp/certs/server.crt /tmp/certs/server.key /tmp/certs/server_encrypted.key /tmp/certs/ssl_password.txt /usr/local/nginx/certs/
cp /tmp/certs/client.crt /tmp/certs/client.key /usr/local/nginx/certs/
# 213 主机
cp /tmp/certs/ca.crt /tmp/certs/ca.key /usr/local/nginx/certs/
cp /tmp/certs/app.crt /tmp/certs/app.key /usr/local/nginx/certs/
步骤 02.首先在上游服务器213主机中,分别监听 8443、9443 端口并启用 SSL,Nginx 配置文件内容:如下所示:
tee /usr/local/nginx/conf.d/upstream_server.conf <<'EOF'
server {
# 监听 443 端口,启用 SSL
listen 8443 ssl;
# 虚拟主机服务器名称
server_name app1.weiyigeek.top;
charset utf-8;
default_type text/plain;
# 开起 HTTP/2 支持
http2 on;
# 日志文件
access_log /var/log/nginx/app1.log main;
error_log /var/log/nginx/app1.err.log debug;
# SSL 证书文件
ssl_certificate /usr/local/nginx/certs/app.crt;
ssl_certificate_key /usr/local/nginx/certs/app.key;
# 配置加密的 SSL 证书密钥文件(根据需求选择)
# ssl_certificate_key /usr/local/nginx/certs/app1_encrypted.key;
# ssl_password_file /usr/local/nginx/certs/ssl_password.txt;
# 配置可信的 CA 证书文件
# ssl_trusted_certificate /usr/local/nginx/certs/ca.crt;
# 配置使用ca证书来验证客户端证书
ssl_client_certificate /usr/local/nginx/certs/ca.crt;
# 若使用 optional 参数将请求客户端证书并验证证书是否存在,即使不存在也可访问,其次ssl_client_verify 变量值为 NONE
ssl_verify_client on;
# 指定客户端证书到根证书的深度
ssl_verify_depth 2;
# 支持的 SSL/TLS 协议版本
ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3;
# 支持的 SSL/TLS 加密套件
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE:ECDH:AES:HIGH:EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:!NULL:!aNULL:!eNULL:!EXPORT:!PSK:!ADH:!DH:!DES:!MD5:!RC4;
# 优先使用服务器端支持的加密套件
ssl_prefer_server_ciphers on;
# SSL 会话缓存
ssl_session_cache shared:SSL:10m;
# 注意点:SSL 会话超时时间,由于是内网反向代理请求,所以这里设置为1小时用不着几分钟就超时一次。
ssl_session_timeout 1h;
# SSL 会话票据复用
ssl_session_tickets on;
# 强制使用 HTTPS 访问
add_header Strict-Transport-Security "max-age=31536000;includeSubDomains;preload" always;
location /certificate {
return 200 'proxy ssl request upstream $host -> $server_host:$server_portnssl_client_verify: $ssl_client_verifynssl_server_name: $ssl_server_namenssl_alpn_protocol: $ssl_alpn_protocolnssl_protocol: $ssl_protocolnssl_client_fingerprint: $ssl_client_fingerprintnssl_cipher: $ssl_ciphernssl_client_i_dn: $ssl_client_i_dnnssl_client_s_dn: $ssl_client_s_dnnssl_client_v_start: $ssl_client_v_startnssl_client_v_end: $ssl_client_v_endnssl_client_v_remain: $ssl_client_v_remainnssl_session_id: $ssl_session_idnssl_client_escaped_cert: $ssl_client_escaped_certnssl_client_raw_cert:n $ssl_client_raw_certnssl_client_cert:n $ssl_client_cert';
}
}
server {
# 监听 443 端口,启用 SSL
listen 9443 ssl;
# 虚拟主机服务器名称
server_name app2.weiyigeek.top;
charset utf-8;
default_type text/plain;
# 开起 HTTP/2 支持
http2 on;
# 日志文件
access_log /var/log/nginx/app2.log main;
error_log /var/log/nginx/app2.err.log debug;
# SSL 证书文件
ssl_certificate /usr/local/nginx/certs/app.crt;
ssl_certificate_key /usr/local/nginx/certs/app.key;
# 配置加密的 SSL 证书密钥文件(根据需求选择)
# ssl_certificate_key /usr/local/nginx/certs/app2_encrypted.key;
# ssl_password_file /usr/local/nginx/certs/ssl_password.txt;
# 配置可信的 CA 证书文件
# ssl_trusted_certificate /usr/local/nginx/certs/ca.crt;
# 配置使用ca证书来验证客户端证书
ssl_client_certificate /usr/local/nginx/certs/ca.crt;
ssl_verify_client on;
# 指定客户端证书到根证书的深度
ssl_verify_depth 2;
# 支持的 SSL/TLS 协议版本
ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3;
# 支持的 SSL/TLS 加密套件
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE:ECDH:AES:HIGH:EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:!NULL:!aNULL:!eNULL:!EXPORT:!PSK:!ADH:!DH:!DES:!MD5:!RC4;
# 优先使用服务器端支持的加密套件
ssl_prefer_server_ciphers on;
# SSL 会话缓存
ssl_session_cache shared:SSL:10m;
# SSL 会话超时时间
ssl_session_timeout 1h;
# SSL 会话票据复用
ssl_session_tickets on;
# 强制使用 HTTPS 访问
add_header Strict-Transport-Security "max-age=31536000;includeSubDomains;preload" always;
location /certificate {
return 200 'proxy ssl request upstream $host -> $server_host:$server_portnssl_client_verify: $ssl_client_verifynssl_server_name: $ssl_server_namenssl_alpn_protocol: $ssl_alpn_protocolnssl_protocol: $ssl_protocolnssl_client_fingerprint: $ssl_client_fingerprintnssl_cipher: $ssl_ciphernssl_client_i_dn: $ssl_client_i_dnnssl_client_s_dn: $ssl_client_s_dnnssl_client_v_start: $ssl_client_v_startnssl_client_v_end: $ssl_client_v_endnssl_client_v_remain: $ssl_client_v_remainnssl_session_id: $ssl_session_idnssl_client_escaped_cert: $ssl_client_escaped_certnssl_client_raw_cert:n $ssl_client_raw_certnssl_client_cert:n $ssl_client_cert';
}
}
EOF
# 重启 Nginx 服务
nginx -s reload
重启完成后,在访问主机中添加解析,并分别访问 https://app1.weiyigeek.top:8443 和 https://app2.weiyigeek.top:9443,如下图所示:
weiyigeek.top-上游主机SSL配置图
步骤 03.在 214 主机中的 Nginx 配置文件中添加使用 SSL 反向代理到上游服务的配置,如下所示:
tee /usr/local/nginx/conf.d/proxy_ssl_server.conf <<'EOF'
# 创建上游服务器
upstream backend_server {
# 定义共享内存区,用于在工作进程间同步负载信息,可根据后端服务器数量调整。
zone backend_zone 64k;
# 多个上游服务组,缺省使用轮询负载均衡算法
server app1.weiyigeek.top:8443 max_fails=2 fail_timeout=10s;;
server app2.weiyigeek.top:9443 max_fails=2 fail_timeout=10s;;
# 设置与上游服务器的长连接,最多保持10个空闲的保活连接。
keepalive 10;
keepalive_timeout 60s; # 设置与上游服务器的长连接,空闲连接的超时时间。
}
server {
listen 80;
# 监听 443 端口,启用 SSL (为了演示 http 访问先临时禁用)
# listen 443 ssl;
# 虚拟主机服务器名称
server_name server.weiyigeek.top;
charset utf-8;
default_type text/plain;
# 开起 HTTP/2 支持
http2 on;
# 日志文件
access_log /var/log/nginx/server.log main;
error_log /var/log/nginx/server.err.log debug;
# SSL 证书文件
ssl_certificate /usr/local/nginx/certs/server.crt;
ssl_certificate_key /usr/local/nginx/certs/server.key;
# 配置加密的 SSL 证书密钥文件(根据需求选择)
# ssl_certificate_key /usr/local/nginx/certs/server_encrypted.key;
# ssl_password_file /usr/local/nginx/certs/ssl_password.txt;
# 配置可信的 CA 证书文件
# ssl_trusted_certificate /usr/local/nginx/certs/ca.crt;
# 配置客户端证书验证
ssl_client_certificate /usr/local/nginx/certs/ca.crt;
ssl_verify_client on;
# 指定客户端证书到根证书的深度
ssl_verify_depth 2;
# 支持的 SSL/TLS 协议版本
ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3;
# 支持的 SSL/TLS 加密套件
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE:ECDH:AES:HIGH:EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:!NULL:!aNULL:!eNULL:!EXPORT:!PSK:!ADH:!DH:!DES:!MD5:!RC4;
# SSL 会话缓存
ssl_session_cache shared:SSL:10m;
# SSL 会话超时时间
ssl_session_timeout 10m;
# 优先使用服务器端支持的加密套件
ssl_prefer_server_ciphers on;
# SSL 会话票据复用
ssl_session_tickets on;
# 强制使用 HTTPS 访问 (为了演示 http 访问先临时禁用)
# add_header Strict-Transport-Security "max-age=31536000;includeSubDomains;preload" always;
location / {
root /usr/local/nginx/html;
index index.html;
}
location /api/ {
# 启用反向代理的 URL 重写功能,将 /api/ 前缀去掉。
rewrite ^/api/(.*)$ /$1break;
# 反向代理到上游服务器
proxy_pass https://backend_server;
# 设置请求头部
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# 启用长连接
proxy_set_header Connection "";
proxy_http_version 1.1;
# 在反向代理启用与上游服务器 SSL 相互认证
proxy_ssl_certificate /usr/local/nginx/certs/client.crt;
proxy_ssl_certificate_key /usr/local/nginx/certs/client.key;
proxy_ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3;
proxy_ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE:ECDH:AES:HIGH:EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:!NULL:!aNULL:!eNULL:!EXPORT:!PSK:!ADH:!DH:!DES:!MD5:!RC4;
# 启用并设置在与代理HTTPS服务器建立连接时通过TLS服务器名称指示扩展
proxy_ssl_server_name on;
proxy_ssl_name app.weiyigeek.top;
proxy_ssl_verify_depth 2;
}
}
EOF
步骤 04.同样配置完成后,重启 Nginx 服务,使用浏览器访问 http://server.weiyigeek.top/api/certificate ,注意不带 https 协议,如下所示:
weiyigeek.top-验证Nginx与上游服务双向SSL认证图
步骤 05.如将主机 214 中的 Nginx 配置文件中proxy_ssl 相关指令注释掉便无法与上游服务建立正常的请求响应,便会显示 400 响应码以及 400 Bad Request No required SSL certificate was sent 页面提示,如下所示:
weiyigeek.top-模拟Nginx无法与上游服务创建SSL通信通道图
至此,完成Nginx 反向代理与上游服务双向 SSL 认证配置,相信大家实践后都有所收货吧!
好了,本章讲解了在 Nginx 中配置 SSL 安全认证三种场景,基本涵盖我们生产环境中常用的场景,希望对你有所帮助,如有疑问,欢迎留言讨论。
下一章中,作者将对 Nginx 中的客户端,以及上游服务端缓相关指令参数、响应头做介绍与实践,请大家多多关注!
87