runsisi's

technical notes

SSH over HTTP

2019-09-18 runsisi#proxy

chisel 是一个 TCP over HTTP 隧道工具,这里我们仅用它来转发 SSH 流量。

方案一

chisel 类似于一个端口映射工具,访问 chisel 客户端的监听地址的流量会通过 chisel 服务端转发至定义的地址。

服务端

外网机器(IP 地址为 123.123.123.123)启动 chisel 服务端,在 80 端口进行监听,指定客户端连接所需的用户名和密码:

$ chisel server --port 80 --auth userabc:pass123

客户端

chisel 的客户端命令行参数如下所示:

$ chisel client -h

  Usage: chisel client [options] <server> <remote> [remote] [remote] ...

其中 remote 的定义为 <local-host>:<local-port>:<remote-host>:<remote-port>

内网机器启动 chisel 客户端连接服务端,在本地监听指定的端口并依据 remote 定义的规则将 TCP 流量进行转发:

$ chisel client --proxy http://proxy.example.com:80 --auth userabc:pass123 123.123.123.123:80 3344::22
2019/09/18 13:56:22 client: Connecting to ws://123.123.123.123:80 via http://proxy.example.com:80
2019/09/18 13:56:22 client: proxy#1:0.0.0.0:3344=>0.0.0.0:22: Listening
2019/09/18 13:56:22 client: Fingerprint 73:6e:f2:0c:ad:cc:92:96:37:da:68:ba:8a:c7:ac:03
2019/09/18 13:56:22 client: Connected (Latency 35.535686ms)

上述命令行的参数解释如下:

--proxy 选项指定 chisel 客户端通过 HTTP 代理 http://proxy.example.com:80 连接外网机器的 chisel 服务端;

--auth 选项指定 chisel 客户端连接服务端所需的用户名和密码;

chisel 服务端监听地址为 123.123.123.123:80;

chisel 客户端监听地址为 :3344,并将流量转发至 chisel 服务端的 22 端口。

此时访问 chisel 客户端监听的地址就会访问 chisel 服务端的 22 端口:

$ ssh root@localhost -p 3344
root@localhost's password: 
Last login: Wed Sep 18 13:32:20 2019 from 127.0.0.1

方案二

显然,上述类似端口映射的方案非常不灵活,我们更希望 chisel 以类似代理服务器的形式访问外网。

服务端

外网机器启动 chisel 服务端,在 80 端口进行监听,指定客户端连接所需的用户名和密码,并启动内部的 socks5 服务:

$ chisel server --port 80 --auth userabc:pass123 --socks5

客户端

$ chisel client --proxy http://proxy.example.com:80 --auth userabc:pass123 123.123.123.123:80 3344::socks
2019/09/18 15:16:24 client: Connecting to ws://123.123.123.123:80 via http://proxy.example.com:80
2019/09/18 15:16:24 client: proxy#1:127.0.0.1:3344=>socks: Listening
2019/09/18 15:16:24 client: Fingerprint 29:bf:81:9b:e1:c0:a5:3f:83:52:91:d5:b1:2e:89:ba
2019/09/18 15:16:24 client: Connected (Latency 38.31468ms)

注意,我们把前面方案一中的远端端口 22 替换成了 socks

此时需要把 chisel 客户端监听的地址作为 socks5 代理地址进行访问:

$ ssh -o ProxyCommand='nc -X 5 -x localhost:3344 %h %p' runsisi@234.234.234.234 -p 22
The authenticity of host '234.234.234.234 (<no hostip for proxy command>)' can't be established.
ECDSA key fingerprint is SHA256:hnMK72IdBxGQWTPVnpoZpZPsURyGcQ4Do+LLP43KZGg.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '234.234.234.234' (ECDSA) to the list of known hosts.
runsisi@234.234.234.234's password: 
Last login: Wed Sep 18 15:24:36 2019 from 127.0.0.1

如果不想在 ssh 命令行上显式指定 ProxyCommand 选项(man ssh_config),可以在配置文件中进行指定:

$ cat ~/.ssh/config
Host 234.234.234.234
    # ProxyCommand nc -X connect -x proxy.example.com:80 %h %p
    ProxyCommand nc -X 5 -x localhost:3344 %h %p

注意:socat 不支持 SOCKS5 代理,因此这里无法用 socat 替代 nc。

netcat

需要注意的是 Ubuntu 下的 nc(netcat-openbsd)和 CentOS 下的 nc(nmap-ncat)是两个完全不同的实现:

$ lsb_release -i
Distributor ID: Ubuntu
$ dpkg -L netcat-openbsd
/.
/bin
/bin/nc.openbsd
...
$ ll /bin/nc
lrwxrwxrwx 1 root root 20 Sep  9  2016 /bin/nc -> /etc/alternatives/nc*
$ ll /etc/alternatives/nc
lrwxrwxrwx 1 root root 15 Sep  9  2016 /etc/alternatives/nc -> /bin/nc.openbsd*
$ lsb_release -i
Distributor ID: CentOS
$ rpm -ql nmap-ncat
/usr/bin/nc
/usr/bin/ncat
...

因此,在 CentOS 下 ProxyCommand 的选项如下(上面给出的 ProxyCommand 的配置都仅适用于 Ubuntu):

nc -4 --proxy-type socks5 --proxy localhost:3344 %h %p

注意:CentOS 下需要显式指定 -4 选项以使用 IPv4 地址。