隐藏在站点下的纯NGINX实现TLS隧道

在之前的文章《纯NGINX实现TLS隧道》中,我们用纯NGINX实现了纯c语言的高效TLS隧道。在这篇文章中,我们将实现类似于之前文章《隐藏在站点下的NGINX前置WSS隧道建设》中的效果,即正常访问域名会被提供正常的网页服务,而使用特定手法(前文为使用特定的路径访问,本篇文章使用NGINX stream map)连接则可以使用到隐藏的服务,而两种访问因为都使用同一端口,使用同一证书,故而很难被探测者/攻击者察觉网站背后的服务。

前提条件

假设你原本有如下的网站配置:

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
http {
server {
listen 80;
server_name my.site;

if ( $host != "my.site" ) {
return 403;
}

return 301 https://my.site$request_uri;
}

server {
listen 443 ssl;
server_name my.site;

ssl_protocols TLSv1.3;
ssl_certificate /etc/certs/cert.pem;
ssl_certificate_key /etc/certs/key.pem;

if ( $host != "my.site" ) {
return 403;
}

location / {
root /etc/www;
index index.html;
}
}
}

TLS隧道解密端

修改刚刚的NGINX配置为:

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
stream {
map $remote_addr $backend {
1.2.3.4 127.0.0.1:33443;
default 127.0.0.1:22443;
}

server {
listen 443;
proxy_pass $backend;
}

server {
listen 33443 ssl;
ssl_protocols TLSv1.3;
ssl_certificate /etc/certs/cert.pem;
ssl_certificate_key /etc/certs/key.pem;
proxy_pass 127.0.0.1:8888;
}
}

http {
server {
listen 80;
server_name my.site;

if ( $host != "my.site" ) {
return 403;
}

return 301 https://my.site$request_uri;
}

server {
listen 22443 ssl;
server_name my.site;

ssl_protocols TLSv1.3;
ssl_certificate /etc/certs/cert.pem;
ssl_certificate_key /etc/certs/key.pem;

if ( $host != "my.site" ) {
return 403;
}

location / {
root /etc/www;
index index.html;
}
}
}

其中,1.2.3.4是TLS隧道加密端的IP。

这样设置之后,在TLS隧道加密端访问本机443端口时,会被分流至33443端口以建立TLS隧道;而在其他IP连接(默认情况)时,会被分流至22443的HTTPS Web服务。

TLS隧道加密端

1
2
3
4
5
6
7
8
9
10
stream {
server {
listen 8888;
proxy_ssl on;
proxy_ssl_protocols TLSv1.3;
proxy_ssl_server_name on;
proxy_ssl_name my.site;
proxy_pass 5.6.7.8:443;
}
}

优点

  • TLS隧道服务变得隐秘。探测者/攻击者除非使用规定的IP访问443端口,不能访问到TLS隧道,而只能接触到Web服务。
  • 效率高,不用担心性能不足导致的带宽不足、延迟波动等问题。

缺点

尽管连接隧道与正常访问Web服务使用相同的端口、相同的证书,但是其不同的TLS Client Hello指纹最可能会出卖我们。

TLS隧道加密端使用的是OpenSSL库,并且已经指定TLS版本为TLS1.3,这使得其发送的TLS Client Hello中带有的加密套件具有很明显的特征,与普通浏览器(如Chrome)发起的TLS Client Hello中带有的加密套件明显不同(后者占整个互联网TLS流量的26%左右)。