使用Nginx+Tinyproxy 来搭建HTTPS Proxy

by FISHERMAN

HTTPS Proxy 是指普通HTTP Proxy经过TLS加密,Chrome 和 Firefox可直连的代理,也称作HTTP over SSL Proxy。优点是无需独立客户端程序,浏览器内置支持,轻量级,支持PAC配置。缺点是服务端配置复杂,客户端只能在浏览器上使用,或者使用MEOW 或 Stunnel 去掉SSL 加密当作普通HTTP 代理。
原理就是后端搞一个普通的HTTP 代理,Squid 或Tinyproxy 都可以,然后用nghttpx或Nginx做前端,进行反向代理和TLS加密。麻烦在于证书的申请和配置,可以参考文末的文档。
这里使用Nginx 和Tinyproxy 的组合进行配置。系统使用debian/ubuntu(或centos)。
编译Nginx ,注意:编译时,要带上参数–with-stream –with-stream_ssl_module 。
 --with-stream --with-stream_ssl_module是必须的。

wget http://nginx.org/download/nginx-1.15.5.tar.gz
tar zxvf nginx-1.15.5.tar.gz
cd nginx-1.15.5
./configure –prefix=/usr/local/nginx –with-http_ssl_module –with-http_dav_module –with-http_flv_module –with-http_realip_module –with-http_gzip_static_module –with-http_stub_status_module –with-mail –with-mail_ssl_module –with-http_mp4_module –with-stream –with-stream_ssl_module

显示:
nginx path prefix: “/usr/local/nginx”
  nginx binary file: “/usr/local/nginx/sbin/nginx”
  nginx modules path: “/usr/local/nginx/modules”
  nginx configuration prefix: “/usr/local/nginx/conf”
  nginx configuration file: “/usr/local/nginx/conf/nginx.conf”

  然后,
  make && make install

成功编译后,进入/usr/local/nginx,可以看到4个目录:

conf  html  logs  sbin
其中,

  1. conf:放置nginx相关的配置文件,最核心的是nginx.conf
  2. html:默认的网站根目录
  3. logs:日志文件目录(访问日志,错误日志,运行时的进程id cat logs/nginx.pid)
  4. sbin:主程序(nginx)目录

一些常用的命令:

    1、启动: /usr/local/nginx/sbin/nginx
2、关闭: /usr/local/nginx/sbin/nginx -s stop
3、重启: /usr/local/nginx/sbin/nginx -s reload
4、指定(另外的)配置文件并启动(如果已经启动则报端口占用的错误): /usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/new.conf
5、查看pid: cat /usr/local/nginx/logs/nginx.pid,可以用于kill等操作
6、查看安装时候的参数: /usr/local/nginx/sbin/nginx -V

参考链接–如何在安装成功的nginx中添加未编译的安装模块(nginx添加模块需要重新编译nginx,而不是像Apache一样配置文件引用动态链接库):

  1. 下载一个第三方库用于测试,wget http://labs.frickle.com/files/ngx_cache_purge-2.3.tar.gz,参考链接中是2.0的版本,在我的服务器make生成的时候报错了,于是改成了最新版的2.3
  2. 解压到主目录中,tar -xzvf ngx_cache_purge-2.3.tar.gz -C ~
  3. 进入nginx源码所在的目录,我的源码放在/usr/src/nginx中,cd /usr/src/nginx
  4. 查看当前的nginx安装使用了哪一些选项,用于重新编译,/usr/local/nginx/sbin/nginx -V,我的选项是 --prefix=/usr/local/nginx --with-ipv6 --with-http_ssl_module --with-http_realip_module --with-http_addition_module --with-http_dav_module --with-http_flv_module --with-http_mp4_module --with-http_gzip_static_module --with-http_perl_module --with-mail --with-mail_ssl_module
  5. 在选项后添加需要添加的第三方模块选项,添加--add-module=/root/ngx_cache_purge-2.3,注意/root是我的主目录
  6. (重要的一步!)只需要make生成主程序而不安装(在此之前可以查看一下objs目录中的nginx文件的最后修改时间),make,保证没有报错(看到objs/nginx的最后修改时间已经变化)
  7. 替换启动文件,cp /usr/local/nginx/sbin;mv nginx nginx.bak;cp /usr/src/nginx-1.11.2/objs/nginx .
  8. 启动或重启nginx

编译选项参考

配置文件解析

参考链接1
参考链接2

链接:https://www.jianshu.com/p/4134a44a09c2

检查模块 nginx -V
/etc/nginx/nginx.conf 配置片段:
stream {
upstream http_proxy {
server 127.0.0.1:8888;
}

server {
listen 8443 ssl;

ssl_certificate /etc/letsencrypt/live/example.net/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.net/privkey.pem;
include /etc/nginx/options-ssl-proxy.conf;
ssl_dhparam /etc/nginx/ssl-dhparams.pem;

proxy_pass http_proxy;
}
}
12345678910111213141516

SSL 配置由Let’ Encrypt 生成,这里复制过来,修改了ssl_session_cache 避免命名冲突。
/etc/nginx/options-ssl-proxy.conf
ssl_session_cache shared:proxy_nginx_SSL:1m;
ssl_session_timeout 1440m;

ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;

ssl_ciphers "ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS";
1234567

其中证书使用Let’t Encrypt申请的免费证书,在此感谢Let’t Encrypt的卓越服务。
接下来安装Tinyproxy,并启动服务:
apt-get install tinyproxy
systemctl enable tinyproxy
systemctl start tinyproxy
systemctl status tinyproxy
systemctl enable nginx
systemctl restart nginx
systemctl status nginx
配置文件在/etc/tinyproxy/tinyproxy.conf,使用默认配置就可以,默认监听127.0.0.1:8888。
测试:
curl --version,输出Features应该包含HTTPS-proxy,这样才支持HTTPS Proxy的测试。
curl https://httpbin.org/ip -x http://127.0.0.1:8888 -v,应该输出服务器IP,证明Tinyproxy 运行正常。
curl https://httpbin.org/ip -x https://your-domain.name:8443 -v,应该输出同样的内容,证明Nginx 运行正常。
如果测试正常,HTTPS Proxy基本上可用了,你可以到此为止。

但是Tinyproxy不支持用户认证,也就是说proxy authorization 不能使用。那么可以使用SSL的双向认证,在Nginx上设置ssl_verify_client on。在 server 段中加入以下配置:
ssl_client_certificate /etc/nginx/certs/client.crt;
ssl_verify_client on
12

客户端证书与网站证书不同,可以使用Easy-RSA 自己申请和管理,不需要第三方认证,client.crt 就是自己生成的Root CA。
客户端使用CA签发的证书与服务器连接,没有证书无法连接,Chrome 支持使用 AutoSelectCertificateForUrls 策略自动选证书。
 ./easyrsa init-pki
./easyrsa build-ca
./easyrsa gen-req proxy.example.name
./easyrsa sign-req client proxy.example.name
1234

以上命令会询问CN,即你的域名。基本上Root CA即执行init-pki 时填写example.com,gen-req 时填写一个子域名,proxy.example.comexample.com 根据需要替换。
这时生成了pki 文件夹,里面要用的文件是pki/ca.crt,把内容写入/etc/nginx/certs/client.crt:
cat pki/ca.crt > /etc/nginx/certs/client.crt
接下来要把proxy.example.com 的证书转换为Chrome 可识别的p12 格式:
openssl pkcs12 -export -clcerts -in pki/issued/proxy.example.com.crt -inkey pki/privae/proxy.example.com.key -out proxy.example.com.p12
执行完会生成proxy.example.com.p12 文件,可以导入Chrome 使用,Linux 下进入 chrome://settings/certificates 执行导入,Windows 下双击打开,导入系统,Chrome 使用系统的证书管理程序。
客户端证书认证必须配合AutoSelectCertificateForUrls, Chrome 不会主动提示代理需要选择证书。
Linux 下创建/etc/opt/chrome/policies/managed/policies.json 文件,写入以下内容:
{
"AutoSelectCertificateForUrls": ["{"pattern":"https://proxy.example.com","filter":{"ISSUER":{"CN":"example.com"}}}"]
}
123

Windows 下导入以下注册表:
Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINESOFTWAREPoliciesGoogleChromeAutoSelectCertificateForUrls]
"1"="{"pattern":"https://proxy.example.com","filter":{"ISSUER":{"CN":"example.com"}}}"
1234

chrome://policy/ 应该会出现对应的值。
如果以上步骤完成,那么使用SwitchyOmega 配置一个 HTTPS 代理, 服务器填写 proxy.example.com, 端口 8443,访问 https://httpbin.org/ip 应该能得到服务器IP。
以上就是所有步骤,增加客户端可以使用相同的证书进行导入,也可以使用Easy-RSA申请新的证书,只需执行gen-req和sign-req即可。不要忘记导入AutoSelectCertificateForUrls 的配置,查看chrome://policy/ 检查配置是否生效。

以上完成了HTTP1.1 over TLS 代理,这在大部分情况下工作良好,只是在高延迟的网络条件下有点慢。

现在Chrome和Firefox对HTTP2代理也有内置支持,可以使用nghttpx将代理转换为HTTP2.
先编译nghttp2.

配置:/etc/nghttpx/nghttpx.conf
frontend=unix:/var/run/nghttpx-proxy.sock
backend=127.0.0.1,3128
private-key-file=/etc/letsencrypt/live/example.net/privkey.pem
certificate-file=/etc/letsencrypt/live/example.net/fullchain.pem
verify-client-cacert=/etc/nginx/certs/client.crt
verify-client=yes
http2-proxy=yes
frontend-http2-window-size=1048575
frontend-http2-connection-window-size=1073741823
workers=1
12345678910

这里frontend=unix:/var/run/nghttpx-proxy.sock是为了使用nginx监听同一个8443端口支持http1.1和http2代理,并转发h2代理流量到nghttpx,nginx内部处理http1.1代理。一般来说没有必要,可以直接写成frontend=0.0.0.0,443;tls,那么nginx就不能占用8443端口。
如果使用nginx 转发需要配置nghttpx-proxy.sock文件的权限为666。
systemctl edit nghttpx
填入:
[Service]
ExecStartPost=/usr/bin/chmod 666 /var/run/nghttpx-proxy.sock
12

/etc/nginx/nginx.conf的stream配置如下:
stream {
upstream http_proxy {
server 127.0.0.1:8888;
}
upstream nginx_proxy {
server unix:/var/run/nginx-proxy.sock;
}
upstream nghttpx_proxy {
server unix:/var/run/nghttpx-proxy.sock;
}

map $ssl_preread_alpn_protocols $proxy {
~/bh2/b nghttpx_proxy;
~/bhttp/1.1/b nginx_proxy;
}

server {
listen 8443;
ssl_preread on;

proxy_pass $proxy;
}

server {
listen unix:/var/run/nginx-proxy.sock ssl;

ssl_certificate /etc/letsencrypt/live/example.net/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.net/privkey.pem;
include /etc/nginx/options-ssl-proxy.conf;
ssl_dhparam /etc/nginx/ssl-dhparams.pem;

proxy_pass http_proxy;
}
}
12345678910111213141516171819202122232425262728293031323334

这样在8443端口就同时支持http1.1和http2代理了。Chrome浏览器在服务器支持h2的时候会自动选择h2代理。使用 –disable-http2启动参数可以禁用h2。因为在使用h2代理进行大流量下载的同时浏览网页会导致网页无法连接,这与http2的多路复用与流控有关,使用http1.1代理没有类似问题,因为每个连接都是独立的tcp连接。

Shared via Inoreader