• 正文
  • 相关推荐
申请入驻 产业图谱

Nginx | HTTPS 加密传输:Nginx 反向代理与上游服务 SSL 双向认证实践

21小时前
87
加入交流群
扫码加入
获取工程师必备礼包
参与热点资讯讨论

大家好,我是 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 中的客户端,以及上游服务端缓相关指令参数、响应头做介绍与实践,请大家多多关注!

加入:作者【全栈工程师修炼指南】知识星球

相关推荐