NGINX最初被设计为反向代理服务器。但是,随着不断发展,NGINX也可以作为实现转发代理的选项之一。转发代理本身并不复杂,它解决的关键问题是如何加密HTTPS流量。本文介绍了使用NGINX作为HTTPS流量转发代理的两种方法,以及它们的应用场景和主要问题。

今天87cloud详谈下阿里云国际如何使用NGINX作为HTTPS转发代理服务器

HTTP/HTTPS 转发代理的分类

首先,让我们仔细看看转发代理的分类。

分类依据:代理对客户是否透明

  • 通用代理:在这里,代理地址和端口是在客户端上的浏览器或系统环境变量中手动配置的。例如,当您在客户端上指定 Squid 服务器的 IP 地址和端口 3128 时。
  • 透明代理:客户端上不需要代理设置。“代理”角色对客户端是透明的。例如,企业网络上的 Web 网关设备是透明代理。

分类依据:代理是否加密HTTPS

  • 隧道代理:这是一个透明传输流量的代理。代理服务器专门通过 TCP 透明地传输 HTTPS 流量。它不会解密或感知其代理流量的特定内容。客户端与目标服务器执行直接 TLS/SSL 交互。本文介绍了与此类型相关的NGINX代理模式。
  • 中间人 (MITM) 代理:代理服务器解密 HTTPS 流量,使用自签名证书完成与客户端的 TLS/SSL 握手,并完成与目标服务器的正常 TLS 交互。在客户端-代理-服务器链接上设置了两个 TLS/SSL 会话。

注意:在这种情况下,客户端在TLS握手过程中实际获取了代理服务器的自签名证书,默认情况下证书链的验证不成功。代理自签名证书中的根 CA 证书必须在客户端上受信任。因此,客户端知道此过程中的代理。如果将自签名根 CA 证书推送到客户端(在企业的内部环境中实现),则可实现透明代理。

转发代理处理 HTTPS 流量时需要特殊处理

在充当反向代理时,代理服务器通常会终止 HTTPS 加密流量并将其转发到后端实例。HTTPS 流量的加密、解密和身份验证发生在客户端和反向代理服务器之间。

另一方面,当充当转发代理并处理客户端发送的流量时,代理服务器不会在客户端请求的URL中看到目标域名,因为HTTP流量已加密并封装在TLS / SSL中,如下图所示。因此,与 HTTP 流量不同,HTTPS 流量在代理实现期间需要一些特殊处理。

NGINX解决方案

根据前面几节中的分类,当NGINX用作HTTPS代理时,代理是透明的传输(隧道)代理,既不解密也不感知上层流量。具体来说,有两种NGINX解决方案可用:第7层(L7)和第4层(L4)。以下各节详细介绍了这些解决方案。

HTTP 连接隧道 (L7 解决方案)

历史背景

早在1998年TLS还没有正式上市的时候,推广SSL协议的网景就提出使用Web代理进行SSL流量的隧道。核心思想是使用HTTP CONNECT请求在客户端和代理之间建立HTTP CONNECT隧道。CONNECT 请求必须指定客户端需要访问的目标主机和端口。互联网草案中的原始图表如下:

有关整个过程的详细信息,请参阅 HTTP:权威指南中的图表。以下步骤简要概述了该过程。

1) 客户端向代理服务器发送 HTTP CONNECT 请求。
2) 代理服务器使用 HTTP CONNECT 请求中的主机和端口信息与目标服务器建立 TCP 连接。
3) 代理服务器向客户端返回 HTTP 200 响应。
4) 客户端与代理服务器建立 HTTP CONNECT 隧道。在 HTTPS 流量到达代理服务器后,代理服务器通过 TCP 连接透明地将 HTTPS 流量传输到远程目标服务器。代理服务器仅透明地传输 HTTPS 流量,不解密 HTTPS 流量。

ngx_http_proxy_connect_module

作为反向代理服务器,NGINX并不正式支持HTTP CONNECT方法。但是,由于NGINX的模块化和可扩展功能,阿里巴巴@chobits提供了连接模块(中文内容)来支持HTTP CONNECT方法,以扩展NGINX作为转发代理。ngx_http_proxy_connect_module

环境建设

以 CentOS 7 環境為例,讓我們詳細看看這過程。

1) 环境安装

对于新环境安装,请参阅安装连接模块的常见安装步骤(中文内容)。ngx_http_proxy_connect_module

安装相应版本的修补程序,并在“configure”命令下添加参数,如以下示例所示。--add-module=/path/to/ngx_http_proxy_connect_module

./configure \
--user=www \
--group=www \
--prefix=/usr/local/nginx \
--with-http_ssl_module \
--with-http_stub_status_module \
--with-http_realip_module \
--with-threads \
--add-module=/root/src/ngx_http_proxy_connect_module

此外,为现有环境添加,如下所示。ngx_http_proxy_connect_module

# 停止NGINX服务
# systemctl stop nginx
# 备份原执行文件
# cp /usr/local/nginx/sbin/nginx /usr/local/nginx/sbin/nginx.bak
# 在源代码路径重新编译
# cd /usr/local/src/nginx-1.16.0
./configure \
--user=www \
--group=www \
--prefix=/usr/local/nginx \
--with-http_ssl_module \
--with-http_stub_status_module \
--with-http_realip_module \
--with-threads \
--add-module=/root/src/ngx_http_proxy_connect_module
# make
# 不要make install
# 将新生成的可执行文件拷贝覆盖原来的nginx执行文件
# cp objs/nginx /usr/local/nginx/sbin/nginx
# /usr/bin/nginx -V
nginx version: nginx/1.16.0
built by gcc 4.8.5 20150623 (Red Hat 4.8.5-36) (GCC)
built with OpenSSL 1.0.2k-fips  26 Jan 2017
TLS SNI support enabled
configure arguments: --user=www --group=www --prefix=/usr/local/nginx --with-http_ssl_module --with-http_stub_status_module --with-http_realip_module --with-threads --add-module=/root/src/ngx_http_proxy_connect_module

2) 配置 nginx.conf 文件

执行以下命令以配置文件。nginx.conf

server {listen  443;# dns resolver used by forward proxyingresolver  114.114.114.114;# forward proxy for CONNECT requestproxy_connect;proxy_connect_allow            443;proxy_connect_connect_timeout  10s;proxy_connect_read_timeout     10s;proxy_connect_send_timeout     10s;# forward proxy for non-CONNECT requestlocation / {proxy_pass http://$host;proxy_set_header Host $host;}}

应用场景

在 L7 解决方案中,HTTP CONNECT 请求必须建立隧道,因此,代理服务器是客户端必须感知的公共代理。在客户端上手动配置 HTTP(S) 代理服务器的 IP 地址和端口。使用 cURL 的“-x”参数访问客户端,如下所示。

# curl https://www.baidu.com -svo /dev/null -x 39.105.196.164:443
* About to connect() to proxy 39.105.196.164 port 443 (#0)
*   Trying 39.105.196.164...
* Connected to 39.105.196.164 (39.105.196.164) port 443 (#0)
* Establish HTTP proxy tunnel to www.baidu.com:443
> CONNECT www.baidu.com:443 HTTP/1.1
> Host: www.baidu.com:443
> User-Agent: curl/7.29.0
> Proxy-Connection: Keep-Alive
>
< HTTP/1.1 200 Connection Established
< Proxy-agent: nginx
<
* Proxy replied OK to CONNECT request
* Initializing NSS with certpath: sql:/etc/pki/nssdb
*   CAfile: /etc/pki/tls/certs/ca-bundle.crtCApath: none
* SSL connection using TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
* Server certificate:
*     subject: CN=baidu.com,O="Beijing Baidu Netcom Science Technology Co., Ltd",OU=service operation department,L=beijing,ST=beijing,C=CN
...
> GET / HTTP/1.1
> User-Agent: curl/7.29.0
> Host: www.baidu.com
> Accept: */*
>
< HTTP/1.1 200 OK
...
{ [data not shown]

由“-v”参数打印的上述详细信息指示客户端首先与代理服务器 39.105.196.164 建立 HTTP CONNECT 隧道。一旦代理回复为“HTTP/1.1 200 连接已建立”,客户端就会启动 TLS/SSL 握手并将流量发送到服务器。

NGINX流(L4解决方案)

由于上层流量是透明传输的,因此这里出现的关键问题是NGINX是否应该充当“L4代理”来实现TCP / UDP以上协议的完全透明传输。答案是肯定的。NGINX 1.9.0或更高版本支持ngx_stream_core_module。默认情况下不生成此模块。在命令下添加选项以启用此模块。-- with-streamconfigure

常见问题

使用NGINX流作为TCP层HTTPS流量的代理,会导致本文开头提到的相同问题:代理服务器未获得客户端要访问的目标域名。发生这种情况是因为在 TCP 层获得的信息仅限于 IP 地址和端口,而不获取域名。要获取目标域名,代理必须能够从上层数据包中提取域名。因此,NGINX流不是严格意义上的L4代理,它必须寻求上层的帮助才能提取域名。

ngx_stream_ssl_preread_module

为了在不解密HTTPS流量的情况下获取HTTPS流量的目标域名,唯一的方法是在TLS/SSL握手期间使用第一个ClientHello数据包中包含的SNI字段。从版本1.11.5开始,NGINX支持ngx_stream_ssl_preread_module。此模块有助于从 ClientHello 数据包获取 SNI 和 ALPN。对于L4转发代理,从ClientHello数据包中提取SNI的能力至关重要,否则NGINX流解决方案将无法实现。但是,这也带来了一个限制,即在 TLS/SSL 握手期间,所有客户端都必须在 ClientHello 数据包中包含 SNI 字段。否则,NGINX流代理将不知道客户端需要访问的目标域名。

环境建设

1) 环境安装

对于新安装的环境,请参考常用安装步骤(中文内容),直接在“configure”命令下添加、、和选项。请考虑以下示例以更好地理解。--with-stream--with-stream_ssl_preread_module--with-stream_ssl_module

./configure \
--user=www \
--group=www \
--prefix=/usr/local/nginx \
--with-http_ssl_module \
--with-http_stub_status_module \
--with-http_realip_module \
--with-threads \
--with-stream \
--with-stream_ssl_preread_module \
--with-stream_ssl_module

为已安装和已编译的环境添加上述三个与流相关的模块,如下所示。

# 停止NGINX服务
# systemctl stop nginx
# 备份原执行文件
# cp /usr/local/nginx/sbin/nginx /usr/local/nginx/sbin/nginx.bak
# 在源代码路径重新编译
# cd /usr/local/src/nginx-1.16.0
# ./configure \
--user=www \
--group=www \
--prefix=/usr/local/nginx \
--with-http_ssl_module \
--with-http_stub_status_module \
--with-http_realip_module \
--with-threads \
--with-stream \
--with-stream_ssl_preread_module \
--with-stream_ssl_module
# make
# 不要make install
# 将新生成的可执行文件拷贝覆盖原来的nginx执行文件
# cp objs/nginx /usr/local/nginx/sbin/nginx
# nginx -V
nginx version: nginx/1.16.0
built by gcc 4.8.5 20150623 (Red Hat 4.8.5-36) (GCC)
built with OpenSSL 1.0.2k-fips  26 Jan 2017
TLS SNI support enabled
configure arguments: --user=www --group=www --prefix=/usr/local/nginx --with-http_ssl_module --with-http_stub_status_module --with-http_realip_module --with-threads --with-stream --with-stream_ssl_preread_module --with-stream_ssl_module

2) 配置 nginx.conf 文件

与HTTP不同,在流块中配置NGINX流。但是,命令参数与HTTP块的命令参数类似。以下代码段显示了主配置。

stream {resolver 114.114.114.114;server {listen 443;ssl_preread on;proxy_connect_timeout 5s;proxy_pass $ssl_preread_server_name:$server_port;}
}

应用场景

作为L4转发代理,NGINX基本上透明地将流量传输到上层,并且不需要HTTP CONNECT来建立隧道。因此,L4 解决方案适用于透明代理模式。例如,当目标域名通过 DNS 解析定向到代理服务器时,需要通过绑定到客户端来模拟透明代理模式。/etc/hosts

以下代码段显示了客户端上的命令:

cat /etc/hosts
...
# 把域名www.baidu.com绑定到正向代理服务器39.105.196.164
39.105.196.164 www.baidu.com# 正常利用curl来访问www.baidu.com即可。
# curl https://www.baidu.com -svo /dev/null
* About to connect() to www.baidu.com port 443 (#0)
*   Trying 39.105.196.164...
* Connected to www.baidu.com (39.105.196.164) port 443 (#0)
* Initializing NSS with certpath: sql:/etc/pki/nssdb
*   CAfile: /etc/pki/tls/certs/ca-bundle.crtCApath: none
* SSL connection using TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
* Server certificate:
*     subject: CN=baidu.com,O="Beijing Baidu Netcom Science Technology Co., Ltd",OU=service operation department,L=beijing,ST=beijing,C=CN
*     start date: 5月 09 01:22:02 2019 GMT
*     expire date: 6月 25 05:31:02 2020 GMT
*     common name: baidu.com
*     issuer: CN=GlobalSign Organization Validation CA - SHA256 - G2,O=GlobalSign nv-sa,C=BE
> GET / HTTP/1.1
> User-Agent: curl/7.29.0
> Host: www.baidu.com
> Accept: */*
>
< HTTP/1.1 200 OK
< Accept-Ranges: bytes
< Cache-Control: private, no-cache, no-store, proxy-revalidate, no-transform
< Connection: Keep-Alive
< Content-Length: 2443
< Content-Type: text/html
< Date: Fri, 21 Jun 2019 05:46:07 GMT
< Etag: "5886041d-98b"
< Last-Modified: Mon, 23 Jan 2017 13:24:45 GMT
< Pragma: no-cache
< Server: bfe/1.0.8.18
< Set-Cookie: BDORZ=27315; max-age=86400; domain=.baidu.com; path=/
<
{ [data not shown]
* Connection #0 to host www.baidu.com left intact

常见问题

现在,让我们快速看一下有关L4解决方案的关键问题。

1) 由于客户端上的手动代理设置,访问尝试失败。

L4 转发代理透明地传输上层 HTTPS 流量,不需要 HTTP CONNECT 即可建立隧道。因此,没有必要在客户端上设置 HTTP(S) 代理。关键问题是,在客户端上手动设置 HTTP(S) 代理是否可确保访问尝试成功。使用 cURL 的“-x”参数设置转发代理服务器并测试对此服务器的访问。以下代码段显示了结果。

# curl https://www.baidu.com -svo /dev/null -x 39.105.196.164:443
* About to connect() to proxy 39.105.196.164 port 443 (#0)
*   Trying 39.105.196.164...
* Connected to 39.105.196.164 (39.105.196.164) port 443 (#0)
* Establish HTTP proxy tunnel to www.baidu.com:443
> CONNECT www.baidu.com:443 HTTP/1.1
> Host: www.baidu.com:443
> User-Agent: curl/7.29.0
> Proxy-Connection: Keep-Alive
>
* Proxy CONNECT aborted
* Connection #0 to host 39.105.196.164 left intact

结果指示客户端尝试在 NGINX 之前建立 HTTP CONNECT 隧道。但是,由于NGINX透明地传输流量,因此CONNECT请求直接转发到目标服务器。目标服务器不接受 CONNECT 方法。因此,“代理连接中止”反映在上面的代码段中,导致访问失败。

2) 访问尝试失败,因为客户端在 ClientHello 数据包中不包含 SNI。

如前所述,当NGINX流用作转发代理时,使用从ClientHello中提取SNI字段至关重要。如果客户端在 ClientHello 数据包中未包含 SNI,则代理服务器将不知道目标域名,从而导致访问失败。ngx_stream_ssl_preread_module

在透明代理模式(由手动绑定主机模拟)下,使用 OpenSSL 在客户端上进行模拟。

# openssl s_client -connect www.baidu.com:443 -msg
CONNECTED(00000003)
>>> TLS 1.2  [length 0005]16 03 01 01 1c
>>> TLS 1.2 Handshake [length 011c], ClientHello01 00 01 18 03 03 6b 2e 75 86 52 6c d5 a5 80 d7a4 61 65 6d 72 53 33 fb 33 f0 43 a3 aa c2 4a e347 84 9f 69 8b d6 00 00 ac c0 30 c0 2c c0 28 c024 c0 14 c0 0a 00 a5 00 a3 00 a1 00 9f 00 6b 006a 00 69 00 68 00 39 00 38 00 37 00 36 00 88 0087 00 86 00 85 c0 32 c0 2e c0 2a c0 26 c0 0f c005 00 9d 00 3d 00 35 00 84 c0 2f c0 2b c0 27 c023 c0 13 c0 09 00 a4 00 a2 00 a0 00 9e 00 67 0040 00 3f 00 3e 00 33 00 32 00 31 00 30 00 9a 0099 00 98 00 97 00 45 00 44 00 43 00 42 c0 31 c02d c0 29 c0 25 c0 0e c0 04 00 9c 00 3c 00 2f 0096 00 41 c0 12 c0 08 00 16 00 13 00 10 00 0d c00d c0 03 00 0a 00 07 c0 11 c0 07 c0 0c c0 02 0005 00 04 00 ff 01 00 00 43 00 0b 00 04 03 00 0102 00 0a 00 0a 00 08 00 17 00 19 00 18 00 16 0023 00 00 00 0d 00 20 00 1e 06 01 06 02 06 03 0501 05 02 05 03 04 01 04 02 04 03 03 01 03 02 0303 02 01 02 02 02 03 00 0f 00 01 01
140285606590352:error:140790E5:SSL routines:ssl23_write:ssl handshake failure:s23_lib.c:177:
---
no peer certificate available
---
No client certificate CA names sent
---
SSL handshake has read 0 bytes and written 289 bytes
⋯

默认情况下,OpenSSL 不包括 SNI。如代码段所示,在发送 ClientHello 后,前面的请求将在 TLS/SSL 握手阶段终止。发生这种情况是因为代理服务器不知道应将 ClientHello 转发到的目标域名。s_client

使用带有“服务器名称”参数的 OpenSSL 来指定 SNI,将产生成功的访问。

# openssl s_client -connect www.baidu.com:443 -servername www.baidu.com

结论

本文介绍了使用NGINX作为HTTPS流量转发代理的两种方法。它总结了NGINX使用HTTP CONNECT隧道和NGINX流充当HTTPS转发代理的解决方案的原则,环境构建要求,应用场景和关键问题。本文在各种情况下使用NGINX作为转发代理时用作参考,具体情况参考www.87cloud.com

阿里云国际版如何使用NGINX作为HTTPS转发代理服务器相关推荐

  1. 阿里云国际版云服务器防火墙设置

    入侵防御页面为您实时展示云防火墙拦截流量的源IP.目的IP.阻断应用.阻断来源和阻断事件详情等信息.本文介绍了入侵防御页面展示的信息和相关操作,下面和87cloud一起来了解阿里云国际版云服务器防火墙 ...

  2. Unirech阿里云国际版云服务器代充-使用Python批量创建实例

    ​ 当您对海外云服务器有需求时,可选择Unirech阿里云代充,那么在注册好账户之后,下面将介绍如何通过RunInstances批量创建实例接口,可以帮助您一次创建多个阿里云国际版海外云服务器实例来完 ...

  3. 阿里云国际版云计算应用在企业领域的优势有哪些

    云服务提供商是提供基于云的平台.基础设施.应用程序或存储服务的第三方公司.企业可根据业务需求对所需的云服务按需付费. 1. 阿里云国际版云计算应用在企业领域的优势 (1)成本 云计算无需投资软硬件以及 ...

  4. 阿里云国际版查看云服务器ecs实例系统日志和截图-Unirech

    阿里云国际版云服务器ecs是一个基于云的虚拟化服务,无法接入显示设备或手动截屏,但是云服务器ecs缓存了最后一次启动.重启或关闭时的系统日志,并支持实时捕获实例屏幕截图.用户则可以使用这些功能来分析和 ...

  5. 阿里云国际版云服务器自助诊断系统-Unirech

    可以帮助用户在云产品出现问题时,一键提交问题的诊断信息,快速获取诊断结果. 背景信息 该自动诊断系统具有以下功能优点: 1.一键反馈,降低问题反馈沟通成本. 2.智能处理,实现秒级自动回复. 3.快速 ...

  6. 什么是GPU计算?阿里云国际版有没有GPU计算?-Unirech

    GPU计算是指使用GPU(图形处理单元)作为协处理器来加速CPU,以加速科学.分析.工程.消费者和企业应用程序的运行.GPU加速器由NVIDIA®于2007年首次推出,现在已经支持了世界各地的政府实验 ...

  7. 云服务器哪家好?阿里云国际版还是华为云国际版?

    云服务器是一种按需计费的云产品,具有高可用.弹性扩容的计算服务资源,每台云服务器以虚拟机的形式运行,包含CPU.内存.硬盘等基础服务器硬件.现在市面上有很多云厂商,比如阿里云.华为云.谷歌云.微软云等 ...

  8. 阿里云国际版购买了服务器后如何下载发票?

    很多阿里云国际版的用户,不了解如何下载发票,今天一起和87cloud了解阿里云国际版购买了服务器后如何下载发票: 阿里云国际版的发票是按照我们账户的支付方式来决定的: 比如,我们使用的是信用卡和Pay ...

  9. 企业为什么要选择阿里云国际版上云?

    云计算产业的不断发展,云计算概念的普及和人们对云计算技术认知的加深,企业用户开始对云计算有越来越多的需求,使得越来越多的传统企业开始转型,所以现在云计算已经成为了很多企业的刚需. 1. 什么是&quo ...

最新文章

  1. C4D运动图形基本训练学习教程
  2. 今天的面试官是个锤子,Spring为什么建议使用构造器来注入?
  3. 趣学python3(23)-队列
  4. 74LS138译码器
  5. AOSP 源码整编单编
  6. 鸿蒙十系统更新机型,高歌猛进,鸿蒙系统升级机型再次确认,花粉:终等到!...
  7. python 自动收集经济数据_完结】数据分析思维案例实战92 用Python自动办公,做职场高手【更新中】91.一课经济...
  8. Git:本地Git仓库连接码云并新建分支提交
  9. [JNI]开发实例(1)封装libjpeg库 保证图片质量压缩图片
  10. php barcode设置黑条宽度,打印机字体的安装问题 BarCode Pro 6.0修改线条宽度 BarCode pro打EAN13条形码...
  11. 北斗sdk_北斗定位终端开发技术方案.pdf
  12. 计算机网络哈勃,NASA已基本确认哈勃故障原因 出在电源控制单元上
  13. win10怎么用计算机二进制,教你如何使用Win10计算器进行进制转换?
  14. java 调用 CXF 报错 java.lang.NoSuchMethodError: javax.wsdl.xml.WSDLReader.readWSDL
  15. 计算机硬件日语,求一些电脑硬件的日语说法(比如显卡等)
  16. 伯努利(Bernoulli)数学习笔记
  17. 宾得常用镜头群[转自东河寒梅]
  18. javaweb JSP JAVA 电影院在线订票系统(电影购票系统 电影售票 电影票预订系统)
  19. iphone 手机尺寸_iPhone是新的黑莓手机
  20. 《Python源码剖析》读书笔记

热门文章

  1. MPC5744P-CAN模块
  2. android 访问data目录、6.0模拟器读写sdcard、相关sdcard路径
  3. 记一次windows 10 无法升级问题的处理(系统盘MBR转GPT)
  4. 金刚菩提子开裂自动修复此计算机,金刚菩提子裂了怎么办 教你这些修复方法...
  5. ntp 服务 --Centos
  6. 临床医疗运营数据分析
  7. 钢条切割算法python实现
  8. 本地idea连接Linux上Redis出现RedisConnectionException: Unable to connect to xxx.xxx.xxx.xxx:6379的问题
  9. 定时任务Alarm的深入理解
  10. 网络安全加固的必要性