2

Stunnel配置小记

 1 year ago
source link: https://chenjiehua.me/linux/stunnel-config-note.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Stunnel配置小记 

最近部署代理用到了 Stunnel,经过一番折腾尝试才终于把配置搞定,在此记录一下~

近日发现长期使用的 socks5 代理突然有点异常,访问国内网站正常,但访问 google 却总是提示“连接已重置”。查看日志发现:

2022-07-05 17:38:00 INFO     connecting www.google.com.hk:443 from xx.xx.xx.xx:42008
2022-07-05 17:38:00 ERROR    [Errno 104] Connection reset by peer

既然能正常访问国内网站,说明代理并未被直接屏蔽;但却又不能访问 google,说明部分流量遭到拦截过滤。

由于 socks5 工作在OSI七层模型中第5层(会话层),处于应用层之下、传输层之上,所以它能够对各种应用协议数据进行处理(比如:HTTP/HTTPS,SMTP,POP3等)。然而,socks5 代理只是简单的对数据包进行转发处理,并不进行任何加密;即便是访问 HTTPS 网站,连接过程中的数据虽然经过 SSL/TLS 加密无法被抓包,但连接建立之初的握手仍然是明文传输,所以会被拦截干扰。

既然如此,有没有什么办法可以对整个连接进行加密呢?答案就是 stunnel,官方对其介绍:

Stunnel is a proxy designed to add TLS encryption functionality to existing clients and servers without any changes in the programs’ code.

对于本身无法进行TLS或SSL通信的客户端及服务器,Stunnel可提供加密安全连接。Stunnel依赖openssl,使用基于X.509数字证书的公开密钥加密算法来保证SSL的安全连接。

image.png

由于 stunnel 需要 X.509 证书,我们先用 openssl 命令生成一个自签名的数字证书:

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

参数简要说明:

  • -new:生成一个新的密钥;
  • -x509:生成一个自签名的X509证书;
  • -days:证书有效期,3650即10年;
  • -nodes:私钥不需要输入密码;
  • -out:证书的保存文件;
  • -keyout:私钥的保存文件;

生成证书时需要输入证书的一些相关信息,由于我们是自己使用也不需要第三方CA签名,所以全部使用默认值即可。

查看生成的 stunnel.pem 文件内容,可以发现其中包含了证书(CERTIFICATE)和私钥(PRIVATE KEY):

$ cat stunnel.pem
-----BEGIN PRIVATE KEY-----
MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC58ae6ts2Y0qtu
……中间部分省略……
UdXQnd9i/j/rQp4kMDYY72OldxyPElBcWChuj8cjrkK/H1e3aum98Bge4yD00pqC
v/S1yVvT0CZAl4B5mH7yhvoBKw==
-----END PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
MIIDfzCCAmegAwIBAgIULLUUTjgqQt0u1kbay0iKLR8sPUIwDQYJKoZIhvcNAQEL
……中间部分省略……
w94HeU1EUyzGbZSqtELCjWDccq0OomspIgqsNRyFZ03yFGI=
-----END CERTIFICATE-----

服务端配置

首先我们需要进行 Stunnel 的服务端配置,在 Ubuntu 上可以直接用 apt 进行安装(如果你的系统没有提供stunnel软件包,可以到官网下载):

$ sudo apt install stunnel4
// 查看配置示例
$ cat /usr/share/doc/stunnel4/examples/stunnel.conf-sample

然后创建一个配置文件:

$ sudo vim /etc/stunnel/stunnel.conf

;分号表示注释
debug = 7
output = /var/log/stunnel.log
cert = /etc/stunnel/stunnel.pem
key = /etc/stunnel/stunnel.pem

[proxy]
accept = 2345
connect = 127.0.0.1:1080

配置简要说明:

  • debug:日志等级,7输出最详细,出现异常时方便定位问题;
  • output:日志位置;
  • cert:证书文件,前面用openssl生成自签名数字证书;
  • key:私钥文件,如果cert指定的证书已经包含了私钥,则可以省略;
  • [proxy]:服务名字,可以自定义;
  • accept:监听的地址;
  • connect:连接的后端服务地址;

客户端配置

接下来进行 Stunnel 的客户端配置,与服务端类似,先安装 stunnel,然后创建一个配置文件:

$ sudo vim /etc/stunnel/stunnel.conf

client = yes
output = /var/log/stunnel.log

[proxy]
accept = 1080
connect = stunnel server:2345

配置简要说明:

  • client:表示这是客户端配置;
  • accept:本地监听的地址;
  • connect:stunnel服务端的地址;

配置完成后,分别在服务端和客户端启动 stunnel 服务:

$ sudo service stunnel4 restart

这样子,本地客户端请求到 1080 端口的流量都会经过 stunnel 以 TLS 加密后发送到服务端,其中 stunnel 通信的端口 2345 可以自定义。

可以使用 curl 测试一下代理是否正常:

$ curl -x socks5h://127.0.0.1:1080 https://www.google.com.hk

校验服务器

首先我们来思考一下 SSL 连接是怎样建立的,以及证书在这个过程中是如何发挥作用?

当一个 SSL 客户端连接到 SSL 服务器时,服务器会返回一个数字证书,以此证明我确实是你要请求的服务器,并非他人伪装。这个数字证书需要由证书认证中心(Certificate Authority,即所谓的 CA)签发,CA一般是第三方认证机构,他们也有自己的数字证书,并由根CA签发;客户端操作系统会预装一些受信任的根证书。

SSL 客户端收到服务器的数字证书后,它会进行校验:

  • 证书跟服务器使用的私钥相匹配(证书本质上是一个公钥,可以解密私钥加密的内容);
  • 证书由CA签发;
  • 客户端信任该CA;

由于我们采用的是自签名的证书,只满足第一个条件,而 stunnel 默认情况下并不对证书进行强制校验,因此按照上面的配置有可能遭到中间人攻击(Man in the middle, MITM),启动 stunnel 客户端时也可以从日志中看到提示:

2022.07.06 00:45:28 LOG4[ui]: Service [proxy] needs authentication to prevent MITM attacks

因此,我们可以在 stunnel 客户端配置中增加对服务器证书进行校验:

$ sudo vim /etc/stunnel/stunnel.conf

client = yes
output = /var/log/stunnel.log

[proxy]
accept = 1080
connect = stunnel server:2345
CAfile = /etc/stunnel/stunnel.pem
verifyPeer = yes

参数简要说明:

  • CAfile:CA证书文件,如果有多个CA可以直接将内容合并在同一个文件中,或者用CApath指定目录(但文件名有要求,配置比较麻烦);
  • verifyPeer:要求强制校验证书;

需要注意的是,CAfile 指定的文件只需要包含证书,前面使用 openssl 生成数字证书时可以分开证书和私钥,只把证书发给客户端,避免私钥泄露

$ openssl req -new -x509 -days 3650 -nodes -out stunnel_cert.pem -keyout stunnel_key.pem

$ cat stunnel_cert.pem
-----BEGIN CERTIFICATE-----
MIIDazCCAlOgAwIBAgIUTc+th1R/9vyC38ailL6E/RxIhWswDQYJKoZIhvcNAQEL
……省略……
IDXc+w7wyAE9kceMFx0clCI/ERR/7/ic8tnf2sFoDwmChCz0P6fEohIp6+IMdnNr
j9OEbGjaK84IIv1fRyod
-----END CERTIFICATE-----

$ cat stunnel_key.pem
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDXcgHx5pegoNne
……省略……
MRlcp4bCi5xgYOtfLdQEznshzyxvjNHw+3fOdSrUzKMqANwvxOLANZofFF4elEGW
gnUs+echxaA4S7zxKY9jS6c=
-----END PRIVATE KEY-----

如果服务器与客户端的证书不匹配,则无法正常建立连接:

$ curl -x socks5h://127.0.0.1:1080 https://www.google.com.hk
curl: (97) Unable to receive initial SOCKS5 response.

stunnel 客户端日志报错:

2022.07.06 11:43:23 LOG4[0]: CERT: Pre-verification error: self-signed certificate
2022.07.06 11:43:23 LOG4[0]: Rejected by CERT at depth=0: C=CN, ST=State, L=City, O=Internet Widgits Pty Ltd
2022.07.06 11:43:23 LOG3[0]: SSL_connect: ../ssl/statem/statem_clnt.c:1883: error:0A000086:SSL routines::certificate verify failed
2022.07.06 11:43:23 LOG5[0]: Connection reset: 0 byte(s) sent to TLS, 0 byte(s) sent to socket

校验客户端

虽然客户端完成了服务器校验(防止中间人攻击),但你可能也留意到任何客户端都可以连接到我们服务器,这简直太危险了!所以我们还需要配置 stunnel 服务器对客户端进行校验。

与服务端类似,stunnel 客户端在连接时也可以提供证书给服务器进行校验(即 peer certificate),证书的生成方式与服务端完全一致。

$ openssl req -new -x509 -days 3650 -nodes -out server_cert.pem -keyout server_key.pem
$ openssl req -new -x509 -days 3650 -nodes -out client_cert.pem -keyout client_key.pem

服务端配置:

output = /var/log/stunnel.log
cert = /etc/stunnel/server_cert.pem
key = /etc/stunnel/server_key.pem

[proxy]
accept = 2345
connect = 127.0.0.1:1080
CAfile = /etc/stunnel/client_cert.pem
verifyPeer = yes

客户端配置:

client = yes
output = /var/log/stunnel.log
cert = /etc/stunnel/client_cert.pem
key = /etc/stunnel/client_key.pem

[proxy]
accept = 1080
connect = stunnel server:2345
CAfile = /etc/stunnel/server_cert.pem
verifyPeer = yes

配置说明:cert、key、CAfile、verifyPeer等配置项可以作为全局使用,可以针对每个服务单独设置。

如果 stunnel 客户端提供了不匹配的证书,则无法正常建立连接:

$ curl -x socks5h://127.0.0.1:1080 https://www.google.com.hk
curl: (97) Unable to receive initial SOCKS5 response.

stunnel 服务端日志报错:

2022.07.06 12:11:52 LOG4[0]: CERT: Pre-verification error: self signed certificate
2022.07.06 12:11:52 LOG4[0]: Rejected by CERT at depth=0: C=AU, ST=Some-State, O=Internet Widgits Pty Ltd
2022.07.06 12:11:52 LOG3[0]: SSL_accept: ../ssl/statem/statem_srvr.c:3701: error:1417C086:SSL routines:tls_process_client_certificate:certificate verify failed
2022.07.06 12:11:52 LOG5[0]: Connection reset: 0 byte(s) sent to TLS, 0 byte(s) sent to socket

stunnel 客户端日志提示:

2022.07.06 12:11:52 LOG3[1]: SSL_read: ../ssl/record/rec_layer_s3.c:1584: error:0A000418:SSL routines::tlsv1 alert unknown ca

此外,Stunnel也支持 PSK 验证,具体配置可以参考文档

经过一番配置之后,理论上来说所有流量都经过 TLS 加密了,那要怎么验证一下呢?祭出大杀器 tcpdump。

直连 socks5 代理进行请求抓包(先执行tcpdump,再用curl请求),可以看到部分明文数据:

// 终端2
$ curl -x socks5h://my_socks5_server:1080 https://www.baidu.com

// 终端1
$ sudo tcpdump port 1080 -A | grep -i baidu
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on ens160, link-type EN10MB (Ethernet), snapshot length 262144 bytes
...9.   ...3.....=.<.5./.....u.........www.baidu.com.........
.0Beijing Baidu Netcom Science Technology Co., Ltd1.0...U...    baidu.com0.."0..        *.H.............0..
........+.b1......GFZjY"w..t0.'.g06p$.F.l...+Vu....lJ....^i.jW..X..=.'.V...q.rs....Bq....#h...@._..uu....`VI;.....n;hu3..9........[AX.+...owc' ..0...7..3...0....Hg..n=..2X...".5.k...M.\.....[G....(...\...*t..4.~D... ..t...^.....0..KNE.A.U...#(.W...]..... ..m..m.........0...0...U...........0....+..........0.0D..+.....0..8http://secure.globalsign.com/cacert/gsrsaovsslca2018.crt07..+.....0..+http://ocsp.globalsign.com/gsrsaovsslca20180V..U. .O0M0A.       +.....2..0402..+........&https://www.globalsign.com/repository/0...g.....0  ..U....0.0?..U...80604.2.0..http://crl.globalsign.com/gsrsaovsslca2018.crl0..a..U.....X0..T.        baidu.com..click.hm.baidu.com..cm.pos.baidu.com..log.hm.baidu.com..update.pan.baidu.com..wn.pos.baidu.com..*.91.com..*.aipage.cn..*.aipage.com..*.apollo.auto..*.baidu.com..*.baidubce.com..*.baiducontent.com..*.baidupcs.com..*.baidustatic.com..*.baifubao.com..*.bce.baidu.com..*.bcehost.com..*.bdimg.com..*.bdstatic.com..*.bdtjrcv.com..*.bj.baidub

经过 stunnel 加密转发的抓包,已经看不到明文数据了:

// 终端2
$ curl -x socks5h://127.0.0.1:1080 https://www.baidu.com

// 终端1(监听stunnel端口)
$ sudo tcpdump port 2345 -A | grep -i baidu
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on ens160, link-type EN10MB (Ethernet), snapshot length 262144 bytes

另外,经过 stunnel 加密后的 socks5 代理也能正常访问 google 了,说明流量也没有被拦截了。

至此,大功告成~


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK