日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

使用NGINX作为HTTPS正向代理服务器

發(fā)布時間:2024/8/23 编程问答 52 豆豆
生活随笔 收集整理的這篇文章主要介紹了 使用NGINX作为HTTPS正向代理服务器 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

NGINX主要設(shè)計作為反向代理服務(wù)器,但隨著NGINX的發(fā)展,它同樣能作為正向代理的選項之一。正向代理本身并不復(fù)雜,而如何代理加密的HTTPS流量是正向代理需要解決的主要問題。本文將介紹利用NGINX來正向代理HTTPS流量兩種方案,及其使用場景和主要問題。

HTTP/HTTPS正向代理的分類

簡單介紹下正向代理的分類作為理解下文的背景知識:

按客戶端有無感知的分類

  • 普通代理:在客戶端需要在瀏覽器中或者系統(tǒng)環(huán)境變量手動設(shè)置代理的地址和端口。如squid,在客戶端指定squid服務(wù)器IP和端口3128。
  • 透明代理:客戶端不需要做任何代理設(shè)置,“代理”這個角色對于客戶端是透明的。如企業(yè)網(wǎng)絡(luò)鏈路中的Web Gateway設(shè)備。

按代理是否解密HTTPS的分類

  • 隧道代理?:也就是透傳代理。代理服務(wù)器只是在TCP協(xié)議上透傳HTTPS流量,對于其代理的流量的具體內(nèi)容不解密不感知。客戶端和其訪問的目的服務(wù)器做直接TLS/SSL交互。本文中討論的NGINX代理方式屬于這種模式。
  • 中間人(MITM, Man-in-the-Middle)代理:代理服務(wù)器解密HTTPS流量,對客戶端利用自簽名證書完成TLS/SSL握手,對目的服務(wù)器端完成正常TLS交互。在客戶端-代理-服務(wù)器的鏈路中建立兩段TLS/SSL會話。如Charles,簡單原理描述可以參考文章。
    注:這種情況客戶端在TLS握手階段實際上是拿到的代理服務(wù)器自己的自簽名證書,證書鏈的驗證默認不成功,需要在客戶端信任代理自簽證書的Root CA證書。所以過程中是客戶端有感的。如果要做成無感的透明代理,需要向客戶端推送自建的Root CA證書,在企業(yè)內(nèi)部環(huán)境下是可實現(xiàn)的。

為什么正向代理處理HTTPS流量需要特殊處理?

作為反向代理時,代理服務(wù)器通常終結(jié) (terminate) HTTPS加密流量,再轉(zhuǎn)發(fā)給后端實例。HTTPS流量的加解密和認證過程發(fā)生在客戶端和反向代理服務(wù)器之間。

而作為正向代理在處理客戶端發(fā)過來的流量時,HTTP加密封裝在了TLS/SSL中,代理服務(wù)器無法看到客戶端請求URL中想要訪問的域名,如下圖。所以代理HTTPS流量,相比于HTTP,需要做一些特殊處理。

NGINX的解決方案

根據(jù)前文中的分類方式,NGINX解決HTTPS代理的方式都屬于透傳(隧道)模式,即不解密不感知上層流量。具體的方式有如下7層和4層的兩類解決方案。

HTTP CONNECT隧道 (7層解決方案)

歷史背景

早在1998年,也就是TLS還沒有正式誕生的SSL時代,主導(dǎo)SSL協(xié)議的Netscape公司就提出了關(guān)于利用web代理來tunneling SSL流量的INTERNET-DRAFT。其核心思想就是利用HTTP CONNECT請求在客戶端和代理之間建立一個HTTP CONNECT Tunnel,在CONNECT請求中需要指定客戶端需要訪問的目的主機和端口。Draft中的原圖如下:

整個過程可以參考HTTP權(quán)威指南中的圖:

  • 客戶端給代理服務(wù)器發(fā)送HTTP CONNECT請求。
  • 代理服務(wù)器利用HTTP CONNECT請求中的主機和端口與目的服務(wù)器建立TCP連接。
  • 代理服務(wù)器給客戶端返回HTTP 200響應(yīng)。
  • 客戶端和代理服務(wù)器建立起HTTP CONNECT隧道,HTTPS流量到達代理服務(wù)器后,直接通過TCP透傳給遠端目的服務(wù)器。代理服務(wù)器的角色是透傳HTTPS流量,并不需要解密HTTPS。
  • NGINX ngx_http_proxy_connect_module模塊

    NGINX作為反向代理服務(wù)器,官方一直沒有支持HTTP CONNECT方法。但是基于NGINX的模塊化、可擴展性好的特性,阿里的@chobits提供了ngx_http_proxy_connect_module模塊,來支持HTTP CONNECT方法,從而讓NGINX可以擴展為正向代理。

    環(huán)境搭建

    以CentOS 7的環(huán)境為例。

    1) 安裝
    對于新安裝的環(huán)境,參考正常的安裝步驟和安裝這個模塊的步驟,把對應(yīng)版本的patch打上之后,在configure的時候加上參數(shù)--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

    對于已經(jīng)安裝編譯安裝完的環(huán)境,需要加入以上模塊,步驟如下:

    # 停止NGINX服務(wù) # systemctl stop nginx # 備份原執(zhí)行文件 # 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 # 將新生成的可執(zhí)行文件拷貝覆蓋原來的nginx執(zhí)行文件 # 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文件配置

    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;}}

    使用場景

    7層需要通過HTTP CONNECT來建立隧道,屬于客戶端有感知的普通代理方式,需要在客戶端手動配置HTTP(S)代理服務(wù)器IP和端口。在客戶端用curl 加-x參數(shù)訪問如下:

    # 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參數(shù)打印出的細節(jié),可以看到客戶端先往代理服務(wù)器39.105.196.164建立了HTTP CONNECT隧道,代理回復(fù)HTTP/1.1 200 Connection Established后就開始交互TLS/SSL握手和流量了。

    NGINX stream (4層解決方案)

    既然是使用透傳上層流量的方法,那可不可做成“4層代理”,對TCP/UDP以上的協(xié)議實現(xiàn)徹底的透傳呢?答案是可以的。NGINX官方從1.9.0版本開始支持ngx_stream_core_module模塊,模塊默認不build,需要configure時加上--with-stream選項來開啟。

    問題

    用NGINX stream在TCP層面上代理HTTPS流量肯定會遇到本文一開始提到的那個問題:代理服務(wù)器無法獲取客戶端想要訪問的目的域名。因為在TCP的層面獲取的信息僅限于IP和端口層面,沒有任何機會拿到域名信息。要拿到目的域名,必須要有拆上層報文獲取域名信息的能力,所以NGINX stream的方式不是完全嚴格意義上的4層代理,還是要略微借助些上層能力。

    ngx_stream_ssl_preread_module模塊

    要在不解密的情況下拿到HTTPS流量訪問的域名,只有利用TLS/SSL握手的第一個Client Hello報文中的擴展地址SNI (Server Name Indication)來獲取。NGINX官方從1.11.5版本開始支持利用ngx_stream_ssl_preread_module模塊來獲得這個能力,模塊主要用于獲取Client Hello報文中的SNI和ALPN信息。對于4層正向代理來說,從Client Hello報文中提取SNI的能力是至關(guān)重要的,否則NGINX stream的解決方案無法成立。同時這也帶來了一個限制,要求所有客戶端都需要在TLS/SSL握手中帶上SNI字段,否則NGINX stream代理完全沒辦法知道客戶端需要訪問的目的域名。

    環(huán)境搭建

    1) 安裝
    對于新安裝的環(huán)境,參考正常的安裝步驟,直接在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

    對于已經(jīng)安裝編譯安裝完的環(huán)境,需要加入以上3個與stream相關(guān)的模塊,步驟如下:

    # 停止NGINX服務(wù) # systemctl stop nginx # 備份原執(zhí)行文件 # 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 # 將新生成的可執(zhí)行文件拷貝覆蓋原來的nginx執(zhí)行文件 # 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文件配置
    NGINX stream與HTTP不同,需要在stream塊中進行配置,但是指令參數(shù)與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;} }

    使用場景

    對于4層正向代理,NGINX對上層流量基本上是透傳,也不需要HTTP CONNECT來建立隧道。適合于透明代理的模式,比如將訪問的域名利用DNS解定向到代理服務(wù)器。我們可以通過在客戶端綁定/etc/hosts來模擬。

    在客戶端:

    cat /etc/hosts ... # 把域名www.baidu.com綁定到正向代理服務(wù)器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

    常見問題

    1) 客戶端手動設(shè)置代理導(dǎo)致訪問不成功
    4層正向代理是透傳上層HTTPS流量,不需要HTTP CONNECT來建立隧道,也就是說不需要客戶端設(shè)置HTTP(S)代理。如果我們在客戶端手動設(shè)置HTTP(s)代理是否能訪問成功呢? 我們可以用curl -x來設(shè)置代理為這個正向服務(wù)器訪問測試,看看結(jié)果:

    # 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 tunnel,但是由于NGINX是透傳,所以把CONNECT請求直接轉(zhuǎn)發(fā)給了目的服務(wù)器。目的服務(wù)器不接受CONNECT方法,所以最終出現(xiàn)"Proxy CONNECT aborted",導(dǎo)致訪問不成功。

    2) 客戶端沒有帶SNI導(dǎo)致訪問不成功
    上文提到用NGINX stream做正向代理的關(guān)鍵因素之一是利用ngx_stream_ssl_preread_module提取出Client Hello中的SNI字段。如果客戶端客戶端不攜帶SNI字段,會造成代理服務(wù)器無法獲知目的域名的情況,導(dǎo)致訪問不成功。

    在透明代理模式下(用手動綁定hosts的方式模擬),我們可以在客戶端用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 s_client默認不帶SNI,可以看到上面的請求在TLS/SSL握手階段,發(fā)出Client Hello后就結(jié)束了。因為代理服務(wù)器不知道要把Client Hello往哪個目的域名轉(zhuǎn)發(fā)。

    如果用openssl帶servername參數(shù)來指定SNI,則可以正常訪問成功,命令如下:

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

    總結(jié)

    本文總結(jié)了NGINX利用HTTP CONNECT隧道和NGINX stream兩種方式做HTTPS正向代理的原理,環(huán)境搭建,使用場景和主要問題,希望給大家在做各種場景的正向代理時提供參考。


    原文鏈接
    本文為云棲社區(qū)原創(chuàng)內(nèi)容,未經(jīng)允許不得轉(zhuǎn)載。

    總結(jié)

    以上是生活随笔為你收集整理的使用NGINX作为HTTPS正向代理服务器的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。