使用SSH Tunnel+Docker实现类似VPN的功能

想从家里访问公司局域网内的服务器,最直接的方式就是搭建VPN,我尝试过Ubuntu下使用PPTPD搭建VPN,实际使用下来并不稳定,经常断线,后来有类似需求的时候我都是通过SSH Tunnel完成的。
先说一下环境,公司服务器在局域网192.168.1.0/24网段,公司网关路由器192.168.1.1上通过端口映射暴露了192.168.1.99的22端口:

<公司公网IP>:20742 ->192.168.1.99:22

我要从家里访问公司局域网内的 192.168.1.99:5432 和 192.168.1.166:6379
我们可以通过

1
2
autossh -M 0 -CNg -L 5432:192.168.1.99:5432 root@<公司公网IP> -p 20742
autossh -M 0 -CNg -L 6379:192.168.1.166:6379 root@<公司公网IP> -p 20742

可以先开通本地对跳板机192.168.1.99的密钥登录,然后增加-f参数直接在后台运行,详请搜索SSH密钥登录,使用密码登录时不能加-f

这样就建立了端口转发

localhost:5432 -> (公司局域网)192.168.1.99:5432
localhost:6379 -> (公司局域网)192.168.1.166:6379

我有一些程序和脚本里写好了访问的服务器地址就是192.168.1.99 和192.168.1.166,我不想修改成localhost或者127.0.0.1,
我们可以通过

1
2
ifconfig enp1s0:0 192.168.1.99 netmask 255.255.255.255 up
ifconfig enp1s0:1 192.168.1.166 netmask 255.255.255.255 up

创建虚拟网卡并把IP设置为192.168.1.99和192.168.1.166,这样这两个IP都指向了本地。
至此需求就解决了。

但是更进一步,如果我想访问的两个服务器上的相同端口,例如 192.168.1.99:6379 和 192.168.1.166:6379 怎么办呢?
我想到了做两个容器分别去建立SSH Tunnel,并把两个容器的IP设置分别设置为 192.168.1.99和192.168.1.166
首先我们创建一个docker network在 192.168.1.0/24网段

1
docker network create --gateway=192.168.1.1 --subnet=192.168.1.0/24 company

创建一个ubuntu容器加入company网络并指定IP地址为192.168.1.99,同时暴露6379端口

1
docker run -dit --name proxy99 -h proxy99 --net company --ip 192.168.1.99 --expose 6379 daimingzhuang/ubuntu:18.04 bash

连上去

1
docker exec -it proxy99 bash

和前面一样建立SSH Tunnel

1
autossh -M 0 -CNg -L 6379:192.168.1.99:6379 root@<公司公网IP> -p 20742

在宿主机上通过redis-cli连接192.168.1.99:6379

1
redis-cli -h 192.168.1.99

成功
192.168.1.166:6379 也同样操作
总结起来就是用docker创建跳板机容器,用ssh tunnel做端口映射。

我做了一个docker镜像(daimingzhuang/ssh_proxy)来方便进行上面的操作
Dockerfile

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
FROM ubuntu:18.04
MAINTAINER DaiMingzhuang "kyo86.dai@gmail.com"
ENV REFRESHED_AT=2022-04-07
RUN echo deb http://mirrors.aliyun.com/ubuntu/ bionic main restricted universe multiverse>/etc/apt/sources.list\
&& echo deb-src http://mirrors.aliyun.com/ubuntu/ bionic main restricted universe multiverse>>/etc/apt/sources.list\
&& echo deb http://mirrors.aliyun.com/ubuntu/ bionic-security main restricted universe multiverse>>/etc/apt/sources.list\
&& echo deb-src http://mirrors.aliyun.com/ubuntu/ bionic-security main restricted universe multiverse>>/etc/apt/sources.list\
&& echo deb http://mirrors.aliyun.com/ubuntu/ bionic-updates main restricted universe multiverse>>/etc/apt/sources.list\
&& echo deb-src http://mirrors.aliyun.com/ubuntu/ bionic-updates main restricted universe multiverse>>/etc/apt/sources.list\
&& echo deb http://mirrors.aliyun.com/ubuntu/ bionic-backports main restricted universe multiverse>>/etc/apt/sources.list\
&& echo deb-src http://mirrors.aliyun.com/ubuntu/ bionic-backports main restricted universe multiverse>>/etc/apt/sources.list
RUN apt update -yqq \
&& apt install -y vim-tiny autossh expect \
&& rm -rf /var/lib/apt/lists/* \
&& ln -s /usr/bin/vim.tiny /usr/bin/vim

EXPOSE 22 80 8080 1433 3306 6379 9092 5432 27017

ADD setup.sh /root/setup.sh
WORKDIR "/root/"
CMD ["bash"]

/root/setup.sh

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#!/bin/bash

user='root'
passwd='跳板机密码'
LAN_IP='跳板机局域网IP'
WAN_IP='网关的公网IP'
WAN_PORT=跳板机通过端口映射暴露在公网的SSH端口
ports=(22 80 8080 1433 3306 6379 9092 5432 27017)

for((i=0;i<${#ports[@]};i++)); do

/usr/bin/expect <<-EOF
set time 10
spawn autossh -M 0 -CNg -L ${ports[$i]}:$LAN_IP:${ports[$i]} $user@$WAN_IP -p $WAN_PORT
expect {
"*(yes/no)?" { send "yes\n"; exp_continue }
"*password:" { send "$passwd\n" }
}

expect eof
EOF

done