使用nginx做正向代理
https://github.com/chobits/ngx_http_proxy_connect_module
HTTP/HTTPS代理服务器
nginx作为一个反向代理工具,刚开始的时候是不支持正向代理的, 后来支持了。代理本身不复杂 ,有一个难点在于如何加密https的流量。解决这个问题有两种方法。
-
代理服务器不处理加密
它在客户机和服务器之间建立一个TUNNEL, 客户机然后直接与服务器通信。本文件讨论的nginx正向代理都用的是这种不解密的方式。
-
“中间人”代理服务器,Man-in-the-Middle (MITM):
代理服务器用一个自签名的证书完成与客户机的TLS handshake,然后转头再去和服务器完成TLS 握手。 在这种配置中, 代理服务器上用的那个自签名证书的CA得被客户机信任。 这种代理通常以Web Gateway的方式存在企业环境中,对用户来说是透明的,用户也不需要配置客户机的代理服务器环境变量或者相关配置。
Nginx提供了两种方案来解决这个问题, 即L7和L4方案。
HTTP CONNECT (L7 方案)
Nginx设计出来是做反向代理的,所以它天生不支持HTTP CONNECT这个Method, github上有个大哥写了这么个插件 https://github.com/chobits/ngx_http_proxy_connect_module,需要通过编译的方式来安装nginx.
-
下载nginx源码
-
下载ngx_http_proxy_connect_module源码
-
根据nginx的版本patch connect module中带的指定patch文件,这在github的Readme中有说明,比如本文举例子用的nginx-1.16.1对应的是 proxy_connect_rewrite_101504.patch
$ cd /root $ git clone https://github.com/chobits/ngx_http_proxy_connect_module $ wget http://nginx.org/download/nginx-1.16.1.tar.gz $ tar zxvf nginx-1.16.1.tar.gz $ cd nginx-1.16.1 $ patch -p1 < ../ngx_http_proxy_connect_module/patch/proxy_connect_rewrite_101504.patch
-
编译时指定connect module的源码位置
$ yum update $ yum -y install gcc pcre pcre-devel zlib zlib-devel openssl openssl-devel#添加用户和组 $ groupadd www $ useradd -g www www $ cd /root/nginx-1.16.1 $ ./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/ngx_http_proxy_connect_module## 如果已经有nginx环境了,可以不执行make install, make之后把./objs/nginx二进行文件拷贝出来就能用, 执行nginx -V可以显示编译的参数 $ make && make install
-
nginx.conf 的配置样例
server {listen 8001;# 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;}}
- 测试
## 在局域网中找另外一台机器,执行下面的命令, 把http://192.168.1.100:8001配置到环境变量里面就可以正常工作了 curl https://www.baidu.com -svo /dev/null -x 192.168.1.100:8001About to connect() to proxy 192.168.1.100 port 8001 (#0) * Trying 192.168.1.100... * Connected to 192.168.1.100 (192.168.1.100) port 8001 (#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 ...
NGINX Stream (L4方案)
在L4方案中, nginx可以做透明代理, nginx需要的编译模块和L7的不一样, 不需要下载connect module,只需要下载nginx的源码,并使用以下
- 编译参数
$ ./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.conf 配置例
stream {resolver 114.114.114.114;server {listen 443;ssl_preread on;proxy_connect_timeout 5s;proxy_pass $ssl_preread_server_name:$server_port;}
}
- DNS劫持
使用这个“代理”需要使用DNS的力量, 把需要走代理的域名解析到nginx代理所在的服务器地址。PS: 这个用法好奇怪 ,不知道在啥环境下能用起来。
注册nginx到Systemd
vi /etc/systemd/system/nginx.service[Unit]
Description=The NGINX HTTP and reverse proxy server
After=network.target[Service]
Type=forking
ExecStart=/usr/local/nginx/sbin/nginx # Nginx 可执行文件的路径
ExecReload=/usr/local/nginx/sbin/nginx -s reload
ExecStop=/usr/local/nginx/sbin/nginx -s stop
PIDFile=/var/run/nginx.pid # Nginx 的 PID 文件路径
PrivateTmp=true[Install]
WantedBy=multi-user.target