stunnel+squid搭建代理服务器

一、网络环境

  • 主机A :192.168.0.11

  • 主机B:66.0.0.6

  • 主机C:4.2.2.2

主机A和B互通,B和C互通,A访问C网络较慢或不通,可以通过stunnel+squid代理跳转访问。

二、squid 安装配置

squid和stunnel可以在主机B上配置,也可在不同主机配置实现网络跳转。这里squid和stunnel server在主机B配置,stunnel client 在客户端主机A配置

  • 安装 yum install squid

  • 配置 vim /etc/squid/squid.conf,主要配置如下两处

acl localnet src 66.0.0.6/32  # 根据实际情况修改,添加允许 stunnel-client 的ip地址
http_port 3128  # squid监听端口

启动服务 service squid start

三、stunnel 配置

  • 安装yum -y install stunnel openssl openssl-devel

1、stunnel server 配置

  • 生成证书认证文件
openssl req -new -x509 -days 365 -nodes -out stunnel.pem -keyout stunnel.pem
openssl gendh 512>> stunnel.pem   #不是必须的
  • 配置

vim /etc/stunnel/stunnel_ser.conf (;;; 注释形式)

cert = /etc/stunnel/stunnel.pem   ;;;# 认证文件
CAfile = /etc/stunnel/stunnel.pem  ;;;# 认证文件
socket = l:TCP_NODELAY=1
socket = r:TCP_NODELAY=1
;;;chroot = /var/run/stunnel
pid = /tmp/stunnel_server.pid
verify = 3
;;; CApath = certs
;;; CRLpath = crls
;;; CRLfile = crls.pem
setuid = web
setgid = web
;;; client=yes
compression = zlib
;;; taskbar = no
delay = no
;;; failover = rr
;;; failover = prio
;;; sslVersion = TLSv1
;;; fips=no
sslVersion = all
;;; options = NO_SSLv2
;;; options = NO_SSLv3
debug = 7
syslog = no
output = /var/logs/stunnel_server.log
client = no  ;;;# 服务端
[sproxy]
accept = 44550  ;;;# 监听端口
connect = 66.0.0.6:3128  ;;;# squid服务连接端口
  • 启动服务 stunnel /etc/stunnel/stunnel_ser.conf

2、squid client 安装配置

yum -y install stunnel openssl openssl-devel
vim  /etc/stunnel/stunnel_cli.conf 

cert = /usr/local/etc/stunnel/stunnel_cli.pem  ;;;#步骤1中生成的stunnel.pem,改了名字而已
CAfile = /usr/local/etc/stunnel/stunnel_cli.pem
socket = l:TCP_NODELAY=1
socket = r:TCP_NODELAY=1

;;;chroot = /var/run/stunnel
pid = /tmp/stunnel.pid
verify = 3

;;; CApath = certs
;;; CRLpath = crls
;;; CRLfile = crls.pem

setuid = web
setgid = web

;;; client=yes
compression = zlib
;;; taskbar = no
delay = no
;;; failover = rr
;;; failover = prio
;;; fips=no
sslVersion = all
;;; options = NO_SSLv2
;;; options = NO_SSLv3

debug = 7
syslog = no
output = /data/logs/stunnel.log
client = yes   ;;;# 客户端

[sproxy]
accept = 0.0.0.0:44550  ;;;# 监听地址
connect = 66.0.0.6:44550  ;;;# stunnel 服务端地址

四、测试及错误解决

  • 测试:配置代理服务器地址:192.168.0.11,端口44550后,可以访问主机C
  • 错误解决:
stunnel 报错:CERT: Verification error: certificate has expired

stunnel客户端连不上服务端,连上几秒就断开了,具体报错信息如下

# stunnel 客户端:
2017.09.25 10:16:19 LOG7[13955:140155381970688]: Starting certificate verification: depth=0, /C=CN/L=Default City/O=Default Company Ltd
2017.09.25 10:16:19 LOG4[13955:140155381970688]: CERT: Verification error: certificate has expired
2017.09.25 10:16:19 LOG4[13955:140155381970688]: Certificate check failed: depth=0, /C=CN/L=Default City/O=Default Company Ltd
2017.09.25 10:16:19 LOG7[13955:140155381970688]: SSL alert (write): fatal: certificate expired
2017.09.25 10:16:19 LOG3[13955:140155381970688]: SSL_connect: 14090086: error:14090086:SSL routines:ssl3_get_server_certificate:certificate verify failed
2017.09.25 10:16:19 LOG5[13955:140155381970688]: Connection reset: 0 byte(s) sent to SSL, 0 byte(s) sent to socket
2017.09.25 10:16:19 LOG7[13955:140155381970688]: Remote socket (FD=13) closed
2017.09.25 10:16:19 LOG7[13955:140155381970688]: Local socket (FD=3) closed
2017.09.25 10:16:19 LOG7[13955:140155381970688]: Service [sproxy] finished (0 left)

# stunnel 服务端:
2017.09.25 10:13:24 LOG7[15546:140344803059456]: SSL state (accept): SSLv3 flush data
2017.09.25 10:13:24 LOG7[15546:140344803059456]: SSL alert (read): fatal: certificate expired
2017.09.25 10:13:24 LOG3[15546:140344803059456]: SSL_accept: 14094415: error:14094415:SSL routines:SSL3_READ_BYTES:sslv3 alert certificate expired
2017.09.25 10:13:24 LOG5[15546:140344803059456]: Connection reset: 0 bytes sent to SSL, 0 bytes sent to socket
2017.09.25 10:13:24 LOG7[15546:140344803059456]: sproxy finished (0 left)

需要安装上面的证书生成命令,重新生成证书后手动更新

openssl req -new -x509 -days 365 -nodes -out stunnel.pem -keyout stunnel.pem

使用Stunnel为MySQL Server建立SSL隧道

笔者接手的一个项目有如下需求:

两台主机A和B,A上运行一个MySQL服务器和一套基于PHP的、以MySQL为数据库的CMS;B上除MySQL服务器外,其他皆与A相同。

要求B能安全地(通过SSL)连接A上的MySQL服务器,而这个CMS本身不支持MySQL over SSL

Stunnel可以提供一个安全的SSL隧道,理论上可以承载任何应用层协议——包括MySQL。

以下操作在A和B上执行

OpenSSL的目录需根据实际情况设置,Stunnel最高支持OpenSSL 1.1.1-dev

tar zxvf stunnel-5.44.tar.gz  
cd stunnel-5.44/

#编译安装至/opt/stunnel,配置文件存放于/etc/stunnel
./configure --prefix= --exec-prefix=/opt/stunnel --with-ssl=/opt/openssl-1.1.0g LDFLAGS="-Wl,--rpath=/opt/openssl-1.1.0g/lib -L/opt/openssl-1.1.0g/lib -lssl -lcrypto"
make  
make install  

建立systemd service文件/lib/systemd/system/stunnel.service,内容如下:

[Unit]
Description=SSL tunnel daemons  
After=network.target  
After=syslog.target

[Install]
WantedBy=multi-user.target  
Alias=stunnel.target

[Service]
Type=forking  
ExecStart=/opt/stunnel/bin/stunnel /etc/stunnel/stunnel.conf  
ExecStop=/bin/kill -TERM $MAINPID  
ExecReload=/bin/kill -USR1 $MAINPID

# Give up if ping don't get an answer
TimeoutSec=600

Restart=always  
PrivateTmp=false  

执行systemctl daemon-reload令更改生效

编辑A机(服务器端)的stunnel配置文件(/etc/stunnel/stunnel.conf,下同)

pid = /var/run/stunnel4/stunnel4.pid  
#只允许使用AES-GCM且具有前向安全性的加密套件
ciphers = ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES128-GCM-SHA256  
#只允许使用TLS 1.2
options = NO_SSLv2  
options = NO_SSLv3  
options = NO_TLSv1  
options = NO_TLSv1.1

options = CIPHER_SERVER_PREFERENCE  
options = DONT_INSERT_EMPTY_FRAGMENTS  
renegotiation = no

[mysql]
#SSL证书与私钥
cert = /path/to/cert_with_intermediate_CA.cer  
key = /path/to/private_key.key  
#SSL监听端口
accept  = 6033  
#MySQL监听地址及端口
connect = 127.0.0.1:3306  

编辑B机的Stunnel配置文件

pid = /var/run/stunnel4/stunnel4.pid

[mysql]
#本地(明文)监听端口
accept  = 127.0.0.1:3306  
#远程(SSL)地址及端口
connect = {A_IP_ADDR}:6033  
#使用客户端模式
client = yes  

编辑完后,A机和B机分别执行systemctl restart stunnel4.service以重启Stunnel

测试:

在B机执行openssl sclient -crlf -connect {AIP_ADDR}:6033,若出现类似如下报文,则A机配置无问题。

j  
5.5.5-10.0.31-MariaDB-0ubuntu0.16.04.2?J%wLÿ? UD6!Clu)iWY9mysql_native_password  

可以在命令后添加-ssl3|-tls1|-tls1_1以测试是否确实禁用低版本SSL协议。

在B机执行telnet 127.0.0.1 3306,若返回类似报文,则B机配置也无问题。

此时,B机上的PHP程序,数据库连接部分保持与A机上的相同即可。

squid+stunnel为docker配置代理服务器

目地

为k8s的docker服务提供http/https代理,解决docker无法pull gcr.io/google_containers 谷歌镜像问题

环境

  • GCE ubuntu 16.04
  • k8s集群机器 ubuntu16.04

简要步骤

一、GCE 搭建squid正向http/https代理服务器

1. 直接使用apt-get install 安装

apt-get install squid3 -y

注意:配置文件在/etc/squid或/etc/squid3下,根据系统不同可能会有一点差异,由于这里进行快速安装,不需要暴露端口给外部使用,也不需要密码,所以配置文件我这里保持默认

二、GCE 安装stunnel代理服务器

1. stunnel主要用来在GCE和k8s机器上代理的数据传输进行加密,否则明文传输很快会被GFW拦截.注意stunnel分为服务端和客户端,GCE上安装服务端,在k8s集群上安装客户端

2. 直接使用apt-get install 安装服务端

apt-get install stunnel4 -y

如果遇到

  • 正试图覆盖 /etc/ppp/ip-down.d,它同时被包含于软件包 resolvconf 1.78ubuntu2
  • dpkg-deb:错误:子进程 粘贴 被信号(断开的管道) 终止了

使用dpkg强制覆盖安装:

cd /var/cache/apt/archives
dpkg -i --force-overwrite xxx.deb

编辑配置文件 vim /etc/stunnel/stunnel.conf

client = no #是否为客户端 这里是服务端填写no
[squid]
accept = 65501
connect = 127.0.0.1:3128 #本地squid服务地址
cert = /etc/stunnel/stunnel.pem #下一步生成的证书地址

openssl生成证书,用户stunnel加密解密

openssl genrsa -out key.pem 2048
openssl req -new -x509 -key key.pem -out cert.pem -days 1095
cat key.pem cert.pem >> /etc/stunnel/stunnel.pem

注意:创建证书时,系统会要求您提供一些国家/地区信息,可随便输入,但是当被要求输入“Common Name”时,您必须输入正确的hostname或IP地址(VPS),我这里输入的ip地址。

通过配置/etc/default/stunnel4文件启用自动启动,vim /etc/default/stunnel4

#将ENABLED更改为1:

ENABLED=1

重新启动Stunnel使配置生效,使用以下命令:

/etc/init.d/stunnel4 restart

三、K8S 集群机器分别搭建stunnel

1. 安装步骤几乎和上面相同

2. scp或其他方法把证书拷贝到k8s集群中

3. 配置文件不同(注意不要#号,systemctl status stunnel4.service)

cert = /etc/stunnel/stunnel.pem #和服务端完全相同的证书
client = yes #声明为客户端
[squid]
accept = 127.0.0.1:65502 #本地代理的端口,即为http/https代理地址
connect = {GCE_IP}:65501 #GCE 服务端ip和端口

四、浏览器SwitchyOmega代理穿透GFW

1. 配置添加http代理127.0.0.1:65502 即可

五、docker添加http/https代理请参考官方文档

https://docs.docker.com/engine/admin/systemd/#start-manually

HTTP/HTTPS自动加密上网方案

这里主要介绍电脑无需任何设置,就能够自动加密代理特定网站的HTTP/HTTPS协议。

 

方案介绍

 

涉及到的软件

  • BIND: 一个流行的域名解析服务器,我们可以设置哪些域名需要走加密线路。
  • Stunnel: 使用TLS对tcp协议进行加密,也就是对tcp建立一条加密线路。
  • SNI Proxy: 代理软件。对于HTTP协议,它可以根据Host请求头解析得出目标站IP;对于HTTPS协议,它可以根据SNI扩展中的域名解析得出目标站IP。

 

此方案优缺点

优点:
无需手动设置任何代理,就能够自动加密代理特定网站的HTTP或HTTPS协议
相对于我们常用的ssh隧道,ssh隧道是单路,而此方案是支持多并发连接,可以极大加速网站访问。

缺点:
对于代理HTTPS协议,需要发起HTTPS连接的客户端,比如浏览器支持TLS的SNI扩展。好消息是目前浏览器几乎都支持此扩展,但对于一些非浏览器的客户端,不支持SNI扩展。我们只能设置正向代理来解决此问题。

 

方案原理

流程图:
服务器安全

原理介绍:

  • 1、首先我们需要准备三台服务器,一台是内网DNS服务器(安装bind),一台是内网代理服务器(安装stunnel),另一台国外服务器(安装stunnel,sniproxy)。
  • 2、我们还需要设置DNS为内网的DNS,并在内网bind dns设置谷歌域名解析的IP为内网代理服务器
  • 3、当我们访问谷歌网站时,首先会向内网DNS服务器发送DNS A记录查询,此时内网DNS服务器会返回内网代理服务器的IP。
  • 4、浏览器得到谷歌域名的解析IP后(即内网代理服务器的IP),会向内网代理服务器发送HTTP或HTTPS请求。
  • 5、此时内网代理服务器(即stunnel),会接收到请求,经过加密,把请求转发到国外服务器(stunnel)的指定端口上。
  • 6、国外服务器(stunnel)接收到来自国内服务器(stunnel)的加密数据后,经过解密,把请求转发到sniproxy。
  • 7、sniproxy再根据HTTP Host请求头或者HTTPS sni扩展的域名解析出谷歌服务器的IP,并把请求转发给谷歌服务器。
  • 8、谷歌服务器收到来自sniproxy发送的请求后,马上返回网页内容给sniproxy,sniproxy再原路返回数据给浏览器。

 

方案实施

由于时间有限,我们仅在Ubuntu server 12.04演示安装。

环境介绍

  • 系统:Ubuntu server 12.04
  • 内网DNS IP: 10.96.153.201(主),10.96.153.204(从)
  • 内网代理服务器: 10.96.153.204
  • 国外服务器IP: 1.2.3.4

安装BIND9

1、在主DNS和从DNS安装bind,即10.96.153.201(主),10.96.153.204(从)。

  1. wget http://www.isc.org/downloads/file/bind-9-10-0b1-2/?version=tar.gz -O bind-9-10-0b1-2.tar.gz
  2. tar xzf bind-9-10-0b1-2.tar.gz
  3. cd bind-9-10-0b1-2
  4. ./configure –prefix=/usr/local/bind
  5. make && make install

2、配置主DNS服务器(10.96.153.201)
2.1、生成/usr/local/bind/etc/rndc.key密钥文件

  1. /usr/local/bind/sbin/rndc-confgen -a -k rndckey -c /usr/local/bind/etc/rndc.key

2.2、编辑/usr/local/bind/etc/named.conf,写入如何内容:

  1. include "/usr/local/bind/etc/rndc.key";
  2. controls { inet 127.0.0.1 port 953 allow { 127.0.0.1; } keys { "rndckey"; }; };
  3. logging {
  4. channel default_syslog { syslog local2; severity notice; };
  5. channel audit_log { file "/var/log/bind.log"; severity notice; print-time yes; };
  6. category default { default_syslog; };
  7. category general { default_syslog; };
  8. category security { audit_log; default_syslog; };
  9. category config { default_syslog; };
  10. category resolver { audit_log; };
  11. category xfer-in { audit_log; };
  12. category xfer-out { audit_log; };
  13. category notify { audit_log; };
  14. category client { audit_log; };
  15. category network { audit_log; };
  16. category update { audit_log; };
  17. category queries { audit_log; };
  18. category lame-servers { audit_log; };
  19. };
  20. options {
  21.     directory "/usr/local/bind/etc";
  22. pid-file "/usr/local/bind/var/run/bind.pid";
  23. transfer-format many-answers;
  24. interface-interval 0;
  25. forward only;
  26. forwarders { 202.96.128.166;202.96.134.133; };
  27. allow-query {any;};
  28. };
  29. zone "google.com" {
  30. type master;
  31. file "google.com.zone";
  32. allow-transfer { 10.96.153.204; };
  33. };

在这个named.conf文件中,我们只需要关心如下内容:
对于options{}区域,202.96.128.166和202.96.134.133这两个是ISP提供的本地DNS,需要修改为自己所在ISP的本地DNS。
对于zone “google.com”{}区域,这里定义了google.com域名的区域文件google.com.zone,还有允许10.96.153.204(即从DNS)同步区域文件。
2.3、建立google.com.zone区域文件

  1. $TTL 3600
  2. @ IN SOA ns1.google.com. hostmaster.google.com. (
  3. 2014072015  ; Serial
  4. 3600 ; Refresh
  5. 900 ; Retry
  6. 3600000 ; Expire
  7. 3600 ) ; Minimum
  8. @ IN NS ns1.google.com.
  9. @ IN NS ns2.google.com.
  10. ns1 IN A 10.96.153.201
  11. ns2 IN A 10.96.153.204
  12. @ IN A 10.96.153.204
  13. * IN A 10.96.153.204

对于这个区域文件,
ns1 IN A 10.96.153.201 指向第一个dns服务器,即主DNS。
ns2 IN A 10.96.153.204 指向第二个dns服务器,即从DNS。
@ IN A 10.96.153.204和* IN A 10.96.153.204指向内网的代理服务器(stunnel)。我们只需要修改这三个地方就好了。

3、配置从DNS服务器(10.96.153.204)
编辑named.conf,写入如下内容

  1. logging {
  2. channel default_syslog { syslog local2; severity notice; };
  3. channel audit_log { file "/var/log/bind.log"; severity notice; print-time yes; };
  4. category default { default_syslog; };
  5. category general { default_syslog; };
  6. category security { audit_log; default_syslog; };
  7. category config { default_syslog; };
  8. category resolver { audit_log; };
  9. category xfer-in { audit_log; };
  10. category xfer-out { audit_log; };
  11. category notify { audit_log; };
  12. category client { audit_log; };
  13. category network { audit_log; };
  14. category update { audit_log; };
  15. category queries { audit_log; };
  16. category lame-servers { audit_log; };
  17. };
  18. options {
  19.     directory "/usr/local/bind/etc";
  20. pid-file "/usr/local/bind/var/run/bind.pid";
  21. transfer-format many-answers;
  22. interface-interval 0;
  23. forward only;
  24. forwarders { 202.96.128.166;202.96.134.133; };
  25. allow-query {any;};
  26. };
  27.  
  28. zone "google.com" {
  29. type slave;
  30. file "google.com.zone";
  31. masters { 10.96.153.201; };
  32. };

配置从DNS就简单得多,只需要写入如上内容到named.conf文件。同样的,
options{}中202.96.128.166和202.96.134.133这两个是当地ISP本地dns。
zone “google.com”{}中10.96.153.201指明主DNS服务器IP。
4、启动bind dns服务器

  1. /usr/local/bind/sbin/named

安装Stunnel

1、在内网代理服务器和国外主机安装stunnel

  1. apt-get install stunnel4

2、内网代理服务器stunnel配置
编辑/etc/default/stunnel4,设置ENABLED=1。
编辑/etc/stunnel/stunnel.conf,内容如下:

  1. client = yes
  2. pid = /etc/stunnel/stunnel.pid
  3. [http]
  4. accept = 80
  5. connect = 1.2.3.4:8082
  6.  
  7. [https]
  8. accept = 443
  9. connect = 1.2.3.4:4433

此配置文件表示,监听了80端口,并把此端口流量转发到1.2.3.4:8082,监听了443端口,并把此端口流量转发到1.2.3.4:4433
3、国外服务器stunnel配置
3.1、生成ssl证书stunnel.pem文件

  1. openssl genrsa -out key.pem 2048
  2. openssl req -new -x509 -key key.pem -out cert.pem -days 1095
  3. cat key.pem cert.pem >> /etc/stunnel/stunnel.pem

3.2、编辑/etc/stunnel/stunnel.conf文件

  1. client = no
  2. [http]
  3. accept = 1.2.3.4:8082
  4. connect = 127.0.0.1:8082
  5. cert = /etc/stunnel/stunnel.pem
  6.  
  7. [https]
  8. accept = 1.2.3.4:4433
  9. connect = 127.0.0.1:4433
  10. cert = /etc/stunnel/stunnel.pem

此配置文件表示,监听了1.2.3.4:8082,并转发此地址流量到127.0.0.1:8082,监听了1.2.3.4:4433,并转发给地址流量到127.0.0.1:4433。
3.3、编辑/etc/default/stunnel4,设置ENABLED=1。
4、启动stunnel

  1. service stunnel4 start

安装sniproxy

sniproxy项目地址:https://github.com/dlundquist/sniproxy
1、安装sniproxy
同样只演示在ubuntu server 12.04安装。
1.1、安装UDNS

  1. mkdir udns_packaging
  2. cd udns_packaging
  3. wget http://archive.ubuntu.com/ubuntu/pool/universe/u/udns/udns_0.4-1.dsc
  4. wget http://archive.ubuntu.com/ubuntu/pool/universe/u/udns/udns_0.4.orig.tar.gz
  5. wget http://archive.ubuntu.com/ubuntu/pool/universe/u/udns/udns_0.4-1.debian.tar.gz
  6. tar xfz udns_0.4.orig.tar.gz
  7. cd udns-0.4/
  8. tar xfz ../udns_0.4-1.debian.tar.gz
  9. dpkg-buildpackage
  10. cd ..
  11. dpkg -i *.deb

1.2、安装sniproxy

  1. apt-get install autotools-dev cdbs debhelper dh-autoreconf dpkg-dev gettext libev-dev libpcre3-dev libudns-dev pkg-config
  2. wget https://github.com/dlundquist/sniproxy/archive/master.zip
  3. unzip master.zip
  4. cd sniproxy-master/
  5. dpkg-buildpackage
  6. cd ..
  7. dpkg -i *.deb

2、配置sniproxy
/etc/sniproxy.conf内容如下:

  1. user daemon
  2. pidfile /var/run/sniproxy.pid
  3. error_log {
  4.     syslog deamon
  5.     priority notice
  6. }
  7. listen 127.0.0.1:8082 {
  8.     proto http
  9.     table http_hosts
  10. }
  11. table http_hosts {
  12.         .*      *:80
  13. }
  14.  
  15. listen 127.0.0.1:4433 {
  16.     proto tls
  17.     table https_hosts
  18. }
  19. table https_hosts {
  20. .* *:443
  21. }

此配置文件表示,监听了127.0.0.1:8082地址,并解析http协议中的Host请求头为IP,然后转发请求到此IP;监听了127.0.0.1:4433地址,并解析TLS中SNI扩展中的域名为IP,并转发请求到此IP。
3、启动sniproxy

  1. sniproxy

 

结束

到目前为止,我们已经搭建完成了整套HTTP/HTTPS加密代理方案。方案中的HTTP明文协议,利用stunnel使用了TLS加密,变成了HTTPS协议,使得数据包无法被解析出明文。方案中的HTTPS协议,本身是加密的,但为了防止SNI扩展的中域名被嗅探,还是走了stunnel的加密通道。对于发送HTTPS请求而不支持SNI扩展的客户端,需要手动设置下代理。下一篇博文我们来介绍加密的正向代理方案。