VPN over SSH
有几种方法可以通过 SSH 建立虚拟专用网络 (VPN)。请注意,虽然这在某些时候很有用,但它可能无法完全替代常规的 VPN。例如,请参阅 [1]。
使用 badvpn 的 tun2socks
badvpn 是一组用于各种 VPN 相关场景的实用工具集合。
启动 SSH 动态 SOCKS 代理
首先,我们将像往常一样设置一个标准的 SSH 动态 socks 代理
$ ssh -TND 4711 <your_user>@<SSH_server>
设置 badvpn 和隧道接口
随后,我们可以继续设置 TUN。
# ip tuntap add dev tun0 mode tun user <your_local_user> # ip addr replace 10.0.0.1/24 dev tun0 # badvpn-tun2socks --tundev tun0 --netif-ipaddr 10.0.0.2 --netif-netmask 255.255.255.0 --socks-server-addr localhost:4711
现在你拥有一个可用的本地 tun0 接口,它会将所有进入该接口的流量通过你之前设置的 SOCKS 代理进行路由。
将流量引导至隧道
现在剩下的工作就是设置本地路由,以将部分流量引导至其中。让我们设置一个将所有流量都路由到其中的路由。我们需要三条路由:
- 一条低度量 (metric) 的路由,指向用于建立隧道的 SSH 服务器。
- 一条低度量的 DNS 服务器路由(因为 tun2socks 不支持 UDP,而 DNS 需要 UDP)。
- 一条默认路由,用于所有其他流量,其度量值高于上述其他路由。
专门设置度量值的目的是为了确保指向 SSH 服务器的路由始终是直连的,否则流量会再次进入 SSH 隧道,从而导致环路,并导致 SSH 连接断开。除此之外,我们需要设置一个显式的 DNS 路由,因为 tun2socks 不会对 UDP 进行隧道传输(DNS 所需)。我们还需要一个度量值低于原默认路由的新默认路由,这样流量才能进入隧道。话虽如此,让我们开始操作吧。
# ip route add <IP_of_SSH_server> via <IP_of_original_gateway> metric 5 # ip route add <IP_of_DNS_server> via <IP_of_original_gateway> metric 5 # ip route add default via 10.0.0.2 metric 6
现在所有流量(除 DNS 和 SSH 服务器本身外)都应该通过 tun0 传输。
OpenSSH 的内置隧道
OpenSSH 使用 -w<本地-tun-编号>:<远程-tun-编号> 内置支持 TUN/TAP。这里描述的是一个第 3 层/点对点/TUN 隧道。同样也可以创建第 2 层/以太网/TAP 隧道。
为 TUN 设备启用转发
要为 TUN 设备启用转发,请编辑 /etc/ssh/sshd_config 并将 PermitTunnel 设置为 yes、point-to-point 或 ethernet。设置为 yes 将同时启用 point-to-point 和 ethernet 隧道的转发。详情请参阅 sshd_config(5)。
/etc/ssh/sshd_config
... PermitTunnel yes ...
然后 重新加载 sshd.service。
使用 systemd-networkd 创建 tun 接口
/etc/systemd/network/vpn.netdev
[NetDev] Name=tun5 Kind=tun [Tun] User=vpn Group=network
/etc/systemd/network/vpn.network
[Match] Name=tun5 [Address] Address=192.168.200.2/24
创建这些文件后,通过 重启 systemd-networkd.service 来启用它们。
此外,你也可以使用 ip tunnel 命令来管理 tun 接口。
在 SSH 命令中创建接口
SSH 会自动创建这两个接口,但在连接建立后应配置 IP 和路由。
ssh \ -o PermitLocalCommand=yes \ -o LocalCommand="sudo ifconfig tun5 192.168.244.2 pointopoint 192.168.244.1 netmask 255.255.255.0" \ -o ServerAliveInterval=60 \ -w 5:5 vpn@example.com \ 'sudo ifconfig tun5 192.168.244.1 pointopoint 192.168.244.2 netmask 255.255.255.0; echo tun0 ready'
启动 SSH
ssh -f -w5:5 vpn@example.com -i ~/.ssh/key "sleep 1000000000"
如果你处于 NAT 之后,可以添加保持活动 (keep-alive) 选项。
ssh -f -w5:5 vpn@example.com \
-o ServerAliveInterval=30 \
-o ServerAliveCountMax=5 \
-o TCPKeepAlive=yes \
-i ~/.ssh/key "sleep 1000000000"
故障排除
- ssh 应当具有访问 tun 接口的权限或创建它的权限。请检查 tun 接口和/或 /dev/net/tun 的所有者。
- 显然,如果你想访问的是一个网络而非单台机器,你应该在两端正确设置 IP 数据包转发、路由,可能还需要设置 netfilter。
- 如果你没有启用隧道功能,当你尝试使用
-w创建 SSH 隧道时,可能会收到以下错误
channel 0: open failed: connect failed: open failed Tunnel forwarding failed
使用 PPP over SSH
pppd 可以轻松地用于通过 SSH 服务器创建隧道
# pppd updetach noauth silent nodeflate pty "/usr/bin/ssh root@remote-gw /usr/sbin/pppd nodetach notty noauth" ipparam vpn 10.0.8.1:10.0.8.2
VPN 建立后,你可以通过它路由流量。若要访问内部网络:
# ip route add 192.168.0.0/16 via 10.0.8.2
例如,为了在未加密的网络上保护你的通信,将所有互联网流量通过隧道路由,首先通过你的常规网关添加一条到 SSH 服务器的路由
# ip route add <remote-gw> via <current default gateway>
接下来,用隧道替换默认路由
# ip route replace default via 10.0.8.2
辅助脚本
pvpn(软件包 pvpnAUR)是一个围绕 SSH 上的 pppd 编写的封装脚本。