目录
在医院项目中,由于安全性和其他网络策略的考虑,经常会遇到某些服务器无法直接访问公网的情况。而 Nginx 服务器,因为充当了公网用户和后端服务器之间的媒介(反向代理),是可以与公网进行通信的。为了克服这种局限性,使得相关的服务器(如应用服务器)能访问到互联网,我们一般会引入正向代理的解决方案,也就是在 Nginx 服务器上安装其他透明的正向代理软件,以避免业务上出现异常。
有人可能会问,为什么不直接用 Nginx 服务器上的 Nginx 进行正向代理呢?其实,Nginx 做 HTTP 的正向代理是完全没问题的,配置起来也非常简单。只是,它无法很好地支持 HTTPS 请求的正向代理。
这里展开讲一下正向代理 HTTPS 流量的机制——
CONNECT
请求,请求与目标服务器建立一个 TCP 隧道
1 2 |
CONNECT example.com:443 HTTP/1.1 Host: example.com:443 |
1 |
HTTP/1.1 200 Connection Established |
Nginx 就麻烦在它默认不支持 CONNECT 方法,因为 Nginx 的设计初衷主要是作为 HTTP 服务器和反向代理来用的,而不是作为正向代理。
所以,通常我们会使用 Squid 来做正向代理,毕竟人家是专业干这个的~
Squid 是一款流行的、高性能的正向代理和缓存服务器,非常适合我们的问题场景。
1 |
docker pull sameersbn/squid:latest |
1 |
mkdir -p /opt/docker/squid/conf |
将定制的 squid.conf
文件(如下方所示)放入此目录中。
1 2 3 4 |
docker run --name squid -d \ --publish 3128:3128 \ --volume /opt/docker/squid/conf/squid.conf:/etc/squid/squid.conf \ sameersbn/squid:latest |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
# 设置 squid 运行的端口(可以改) http_port 3128 # 定义“安全”的端口 acl Safe_ports port 80 # http acl Safe_ports port 443 # https # 定义哪些方法是允许的 acl allowed_http_methods method CONNECT acl allowed_http_methods method GET HEAD POST OPTIONS # 限制非“安全”的端口和方法(引用了上面的定义) http_access deny !Safe_ports http_access deny !allowed_http_methods # 允许来自于本地的 Squid 管理请求,拒绝来自于其他服务器的 http_access allow localhost manager http_access deny manager # 允许所有其他请求 http_access allow all # 设置日志文件位置(末尾的 squid 表示日志格式名,意思类似于 nginx 的 main 日志格式) access_log /var/log/squid/access.log squid # 推荐的内存缓存设置 cache_mem 128 MB # DNS 服务器(定义了阿里和腾讯的 3 个 DNS 服务器,squid 所在的服务器要能访问得通这些 DNS 服务器,不然无法解析域名) dns_nameservers 223.5.5.5 223.6.6.6 119.29.29.29 # 隐藏请求里的某些信息 forwarded_for off request_header_access Via deny all request_header_access X-Forwarded-For deny all |
1 |
docker run -e http_proxy=http://NGINX_SERVER_IP:3128 -e https_proxy=http://NGINX_SERVER_IP:3128 -e no_proxy=INTERNAL_DOMAINS ... |
/etc/environment
文件(如果改了后不生效,需要 source /etc/environment 一下),添加以下内容:
1 2 3 |
http_proxy=http://NGINX_SERVER_IP:3128 https_proxy=http://NGINX_SERVER_IP:3128 no_proxy=INTERNAL_DOMAINS |
然后在 Docker 运行参数里,可以直接引用咱们在这个文件里定义的环境变量,如下:
1 |
docker run -e http_proxy=$http_proxy -e https_proxy=$https_proxy -e no_proxy=$no_proxy ... |
此处的 NGINX_SERVER_IP
代表 Nginx 服务器的内网 IP 地址。而 INTERNAL_DOMAINS
代表不应通过代理访问的内部域名,多个域名之间用英文的逗号分隔开,比如 localhost,127.0.0.1,xxx.xxx.com,这个 xxx.xxx.com 是我们项目本身的域名,访问项目本身的域名我们不希望它被 Squid 正向代理,而是希望它被 Nginx 反向代理(当然,需要先在应用服务器的 hosts 文件里配好项目域名到 Nginx 服务器内网 IP 地址的解析)!
对于已存在的 Docker 容器,直接设置环境变量是无效的。最佳实践是使用新的环境变量,重建容器,步骤如下:
1 |
docker commit <container_id> new_image_name |
1 |
docker run -e http_proxy=$http_proxy -e https_proxy=$https_proxy -e no_proxy=$no_proxy ... new_image_name |
通过 Squid 正向代理的部署和配置,我们可以为无法直接访问公网的应用服务器提供一个桥梁,从而允许其访问公网服务(比如腾讯云 COS、TRTC 的一些服务,以及我们公司的一些 SaaS 公共服务)。这一策略避免了在应用级别做任何代码上的修改,同时确保了访问的安全性和可控性。
最后,最坏的情况是如果 Nginx 服务器要访问公网服务得指定 IP,而不能直接不限制所有域名,那只能整理一下要使用的公网服务的 IP 了,虽然这些 IP 很可能是动态的,那也没办法……
目前整理如下,有增加的话会及时补充进来——
腾讯云 COS 的 API 对应的 IP ,开通 TCP 443
58.217.250.104
58.217.250.16
58.217.250.17
58.217.250.18
58.217.250.19
58.217.250.27
180.111.196.17
公司 Nginx 1 的 IP,开通 TCP 443、8888、9999
1*.228.1.36
公司 Nginx 2 的 IP,开通 TCP 443
131.53.47.16
阿里云及腾讯云的 DNS 的 IP,开通 UDP 53
119.29.29.29
223.5.5.5
223.6.6.6