利用Nginx正向代理加速代码编译外网依赖下载速度

一个典型场景:

       之前在ci涉及项目代码构建过程中的依赖都需要到国外下载,例如GitHub,谷歌等等,最初的解决方案是在公司国外服务器节点上安装gitlab-runner,并register到gitlab,再将代码编译阶段job的tags指向为该runner的tag。

      但代码编译对服务器cpu/内存的消耗是非常高的,这就影响到同一服务器上其他应用的正常运行。

      而运维工作中,nginx主要作为反向代理服务器使用,但其也具备正向代理功能,这里我们借助其这一功能,在ci的pipeline中将正向代理http_proxy 以ENV环境变量的方式定义到代码编译job基于的基础镜像的Dockerfile中,来加速代码构建过程中的依赖下载速度。      

正向代理和反向代理的区别:参考这篇文章

正向代理本身并不复杂,而如何代理加密的https流量是正向代理需要解决的主要问题

为什么正向代理处理发送的https请求需要特殊处理?

反向代理中,https流量的加解密和认证校验过程发生在客户端和反向代理服务器之间。代理服务器通常终止https加密流量,再转发给后端应用

而作为正向代理在处理客户端发过来的流量时,HTTP 加密封装在了 TLS / SSL 中,代理服务器无法看到客户端请求的 URL 中想要访问的域名

正向代理分类:

以客户端是否感知区分

1.普通代理

客户端需要在浏览器中或者系统环境变量手动设置代理的地址和端口如linux中设置http_proxy来定义代理服务器 IP 和端口

2.透明代理

客户端不需要做任何代理设置,代理服务器对于客户端是透明的。如灰度发布利用的web网关

以代理是否解密 HTTPS 分类

1.隧道代理

 即透传代理。代理服务器只是在TCP协议上透传HTTPS流量,对于其代理的流量的具体内容不解密不感知。客户端和其访问的目的服务器做直接TLS/SSL交互。本文中讨论的NGINX代理方式属于这种

2.中间人代理

TO DO...

Nginx七层正向代理  HTTP CONNECT

说明: 这里应用层(7 层)通过 HTTP CONNECT 来建立隧道,属于客户端有感知的普通代理方式

大致过程:

1.客户端给代理服务器发送HTTP CONNECT请求;

2.代理服务器利用HTTP CONNECT请求中的主机和端口与目的服务器建立TCP连接

3.代理服务器给客户端返回HTTP 200响应

4.客户端和代理服务器建立起HTTP CONNECT隧道,HTTPS流量到达代理服务器后,直接通过TCP透传给远端目的服务器。代理服务器的角色是透传HTTPS流量,并不需要解密HTTPS

nginx官方目前没有支持http connect 方法的模块,百度了一下,网上都是通过阿里提供的ngx_http_proxy_connect_module模块,来实现 http connect

github上关于ngx_http_proxy_connect_module项目的地址:https://github.com/chobits/ngx_http_proxy_connect_module

这里直接对原有nginx服务进行升级来添加该模块

选取一台香港nginx节点作为正向代理服务器

查看现nginx版本可以看出没有ngx_http_proxy_connect_module模块

[root@form-nginx-hk ~]# /usr/local/nginx/sbin/nginx -V
nginx version: nginx/1.14.2
built by gcc 4.8.5 20150623 (EulerOS 4.8.5-4) (GCC) 
built with OpenSSL 1.0.2k-fips  26 Jan 2017
TLS SNI support enabled
configure arguments: --prefix=/usr/local/nginx --with-http_stub_status_module --with-http_ssl_module --with-http_realip_module --with-stream

 

[root@form-nginx-hk ~]# wget https://github.com/chobits/ngx_http_proxy_connect_module/archive/master.zip
[root@form-nginx-hk ~]# unzip master.zip
[root@form-nginx-hk ~]# wget http://nginx.org/download/nginx-1.14.2.tar.gz
[root@form-nginx-hk ~]# tar -xvf http://nginx.org/download/nginx-1.14.2.tar.gz
[root@form-nginx-hk ~]# cd  nginx-1.14.2
[root@form-nginx-hk ~]# patch -p1 <  /root/ngx_http_proxy_connect_module-master/patch/proxy_connect_rewrite_1014.patch
[root@form-nginx-hk ~]# ./configure --prefix=/usr/local/nginx \
 --with-http_stub_status_module \
 --with-http_ssl_module \
 --with-http_realip_module \ 
 --with-stream \
 --add-module=/root/ngx_http_proxy_connect_module-master

[root@form-nginx-hk ~]# make  #编译,不要 make install

平滑升级nginx

#备份原来的nginx启动脚本
[root@form-nginx-hk ~]# cd /usr/local/nginx/sbin/
[root@form-nginx-hk sbin]# mv nginx nginx_old

#拷贝刚刚编译nginx的目录下的可执行文件即obj目录下的nginx到nginx的sbin目录下
[root@form-nginx-hk sbin]# cp /root/nginx-1.14.2/objs/nginx /usr/local/nginx/sbin/nginx 
[root@form-nginx-hk sbin]# /usr/local/nginx/sbin/nginx -V 
nginx version: nginx/1.14.2
built by gcc 4.8.5 20150623 (EulerOS 4.8.5-4) (GCC) 
built with OpenSSL 1.0.2k-fips  26 Jan 2017
TLS SNI support enabled
configure arguments: --prefix=/usr/local/nginx --with-http_stub_status_module --with-http_ssl_module --with-http_realip_module --with-stream --add-module=/root/ngx_http_proxy_connect_module-master/
#可以发现ngx_http_proxy_connect_module模块已添加

#最后完成平滑升级
#此时会在/var/run/下生成一个nginx.pid.oldbin的pid文件
[root@form-nginx-hk sbin]# kill -USR2 $(cat /var/run/nginx.pid)   #发送升级信号给master进程号
#此时会在/var/run/下生成一个nginx.pid.oldbin的pid文件

[root@form-nginx-hk sbin]# kill -QUIT $(cat /var/run/nginx.pid.oldbin)  #优雅退出旧进程

 

[root@test-ads-01 ~]# cat /usr/local/nginx/conf/conf.d/github.conf
server {
     listen  2828;                           #监听端口
    
     # dns resolver used by forward proxying
     resolver  8.8.8.8 ipv6=off;             #设定dns
     # forward proxy for CONNECT request
     proxy_connect;                          #启用 CONNECT HTTP方法
     proxy_connect_allow            443 80;  #指定代理CONNECT方法可以连接的端口号或范围的列表
     proxy_connect_connect_timeout  20s;     #定义客户端与代理服务器建立连接的超时时间  
     proxy_connect_read_timeout     20s;     #定义客户端从代理服务器读取响应的超时时间
     proxy_connect_send_timeout     20s;     #设置客户端将请求传输到代理服务器的超时时间
     # forward proxy for non-CONNECT request
     location / {
         proxy_pass $scheme://$http_host$request_uri;
     }
 }

[root@form-nginx-hk ~]# /usr/local/nginx/sbin/nginx -t
nginx: the configuration file /usr/local/nginx/conf/nginx.conf syntax is ok
nginx: configuration file /usr/local/nginx/conf/nginx.conf test is successful

[root@form-nginx-hk ~]# /usr/local/nginx/sbin/nginx -s reload

其他可设置参数:

proxy_connect_address address | off 指定被代理服务器的IP地址,相当于白名单,默认off

proxy_connect_bind address[transparent] | off 指定被代理服务器请求的端口,默认none

 

linux系统下终端客户端设置代理地址

export proxy="http://${proxy_server_ipaddr}:${listen_port}"
export http_proxy=$proxy
export https_proxy=$proxy

客户端访问github

[root@test-ads-01 ~]# curl https://github.com -svo /dev/null
* About to connect() to github.com port 443 (#0)
*   Trying 13.229.188.59...
* Connected to github.com (18.211.18.69) port 443 (#0)
* Initializing NSS with certpath: sql:/etc/pki/nssdb
*   CAfile: /etc/pki/tls/certs/ca-bundle.crt
  CApath: none
* SSL connection using TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
* Server certificate:
* 	subject: CN=github.com,O="GitHub, Inc.",L=San Francisco,ST=California,C=US,serialNumber=5157550,incorporationState=Delaware,incorporationCountry=US,businessCategory=Private Organization
* 	start date: May 08 00:00:00 2018 GMT
* 	expire date: Jun 03 12:00:00 2020 GMT
* 	common name: github.com
* 	issuer: CN=DigiCert SHA2 Extended Validation Server CA,OU=www.digicert.com,O=DigiCert Inc,C=US
> GET / HTTP/1.1
> User-Agent: curl/7.29.0
> Host: github.com
> Accept: */*
> 
< HTTP/1.1 200 OK
< Date: Thu, 19 Dec 2019 11:41:36 GMT
< Content-Type: text/html; charset=utf-8
< Transfer-Encoding: chunked
< Server: GitHub.com
< Status: 200 OK
...
< 
{ [data not shown]
* Connection #0 to host github.com left intact

可以看到客户端先往代理服务器18.211.18.69建立了http connect隧道,代理回复 HTTP / 1.1 200 连接建立后就开始交互 TLS / SSL 握手和流量了。

 

nginx四层正向代理 NGINX STREAM

因为在TCP的层面获取的信息仅限于IP和端口层面,没有任何机会拿到域名信息,利用nginx stream在TCP层面上代理HTTPS流量会出现代理服务器无法获取客户端想要访问的目的域名。所以如果代理服务器要拿到目的域名,必须要有拆上层报文获取域名信息的能力,所以NGINX stream的方式需要借助ngx_stream_ssl_preread_module模块。

ngx_stream_ssl_preread_module

该模块用于获取TLS/SSL握手的第一个Client Hello报文中的扩展地址SNI (Server Name Indication),来获取客户端需要访问的目的域名。

部署:

对于新安装的环境,直接在nginx编译时加上--with-stream,--with-stream_ssl_preread_module和--with-stream_ssl_module选项即可。

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

1.nginx.conf文件配置

需要在stream块中进行配置

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

2.客户端实现:

在客户端直接将github.com域名绑定到正向代理服务器18.211.18.69即可

[root@registry-code jump]#echo 'github.com 18.211.18.69' >>  /etc/hosts

 

你可能感兴趣的:(Nginx,正向代理)