Canary Workshop

Whatever is worth doing at all is worth doing well

使用 HAProxy 实现单端口多服务

我们知道,默认情况下 HTTPS 服务监听443端口,而 SSH 监听22端口。是否可以将二者合在一个端口呢?例如,这样来规避仅能访问80或443端口的网络的限制。之前有使用 SNI Proxy 实现这个功能,即能识别为 HTTPS 的包传输给 nginx 而剩下的传输给 sshd 。那么,假如又增加了某种“没有特征”的流量转发工具呢?是不是无法使用一个端口了?

这里就要祭出一大神器 HAProxy 。虽然这样的转发不是其主要使用场景,但是使用 HAProxy 的功能是完全可以达到目的的。

实现的原理不论是 SSH 服务还是 HTTPS 服务,都拥有十分强烈的特征。如果对每个 TCP 数据包进行识别,按照一定特征对其进行转发,而完全无法识别的发送给特定程序,目的就实现了。

操作环境: Debian 9 x64

安装 HAProxy

直接 sudo apt install haproxy 即可。

环境

我使用的环境如下:

  • sshd: 127.0.0.1:22
  • nginx: 127.0.0.1:443
  • 某无特征服务: 127.0.0.1:23333
  • HAproxy: [::]:80

准备工作

这里寻找各个协议的特征。 SSH 和 HTTPS 协议的每个数据包的开头都是完全相同的,我们可以利用这一点来确定其特征。

可以在远程服务器或本地执行: nc -l -p 2333 | hexdump -C 来观察 SSH 协议的特征。然后用 SSH 客户端连接 2333 端口,同时观察 nc 的输出。

我这里使用 PuTTY 得到的输出如下:
53 53 48 2d 32 2e 30 2d 50 75 54 54 59 5f 52 65 |SSH-2.0-PuTTY_Re|
输出最开头的 53 53 48 2d 便可以用作特征值。

同理,得到 HTTPS 服务的特征值是 16 03 01

配置 HAProxy

直接上已经可用的配置文件:
/etc/haproxy/haproxy.cfg

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
global
tune.ssl.default-dh-param 2048
defaults
timeout connect 5000
timeout client 50000
timeout server 50000
frontend ssl
mode tcp
bind *:443
tcp-request inspect-delay 5s
tcp-request content accept if HTTP
acl is_ssh req.payload(0,3) -m bin 535348
acl is_ssl req.payload(0,3) -m bin 160301
use_backend ssh if is_ssh
use_backend ssl if is_ssl
default_backend something
backend ssl
mode tcp
server servername 127.0.0.1:443
backend something
mode tcp
server something 127.0.0.1:23333
backend ssh
mode tcp
server ssh 127.0.0.1:22

之后便可以启动 HAProxy 服务进行测试。