VPN(一): UDP隧道


虚拟专用网络(VPN)用于创建计算机通信的专用通信域,或为专用网络到不安全网络(如Internet)的安全扩展。VPN是一种被广泛使用的安全技术。在IPSecTLS/SSL上构建VPN是完全不同的两种方法。在本博客以及后续的VPN实验博客中,只关注基于TLS/SSL的VPN。所谓 TLS/SSL,即 传输层安全性/安全套接字层。

写在前面

在实际环境中,节点只能访问到和其同处一个网络中的其他节点,无法访问到处于它之外的内网中的节点,如下:

我们使用 docker 网络 ”intranet“ 将 HostV 与 VPN 服务器网关的内网口连接,模拟内部局域网。此时,HostU是无法直接访问HostV的。而本实验的目的,就是在HostU和VM之间建立VPN隧道,使其能够通过 Internet 直接访问到HostV。

TUN/TAP 技术

TLS/SSL VPN中使用了TUN/TAP技术,其在现代操作系统中已被广泛使用。TUN和TAP是虚拟网络内核驱动程序,它们可以实现完全由软件支持的网络设备。TAP模拟以太网设备,处理链路帧,TUN模拟网络层,处理IP数据报。我们可以用 TAP/TUN 创建虚拟网络接口

用户空间程序访问 TUN/TAP 虚拟网络接口,操作系统通过 TUN/TAP 网络接口将数据包传送到用户空间程序。另一方面,程序通过 TUN/TAP 网络接口发送的数据包被注入操作系统的网络栈。在操作系统看来,数据包是通过虚拟网络接口的外部源进来的

当程序从TUN/TAP接口读取数据时,计算机发送到此接口的IP数据包将被传送给程序。另一方面,程序发送到接口的IP数据包将被传送到计算机中,就好像这些数据包通过这个虚拟网络接口从外部来的一样。程序可以使用标准的read()和write()系统调用来接口或发送数据包到虚拟接口。

VPN客户端会运行 vpnclient,VPN服务端会运行 vpnserver,两个程序分别是VPN隧道的两端,它们使用 TCP 或 UDP 协议,这篇博客用的UDP。VPN客户端和服务端程序通过TUN接口连接到主机系统,做以下两件事:

  • 从主机系统获取IP数据包,因此数据包可以通过隧道发送;
  • 从隧道获取IP数据包,然后将其转发到主机系统,主机系统将数据包转发到其最终目的地。

以我的理解,TUN接口就是对IP进行了重造,使原本无法转发的数据包能被转发,具体的流程如下图所示:

知道了基本流程后,开始做实验。

实验

实验环境为:

  • VMware® Workstation 15 Pro
  • SeedUbuntu 16.04
  • Docker 1.10.3

1. 配置网络拓扑

我们将在计算机(客户端)和网关之间创建 VPN 隧道,允许计算机通过网
关安全地访问专用网络。我们使用虚拟机本身作为 VPN 服务器网关(VM),并
创建两个容器分别作为 VPN 客户端(HostU)和专用网络中的主机(HostV)。
网络设置如下图:

在实际环境中,VPN 客户端(HostU)和 VPN 服务器网关的外网口通过
Internet 连接。简单起见,我们在本实验中将这两台机器直接连接到同一 docker
网络“extranet”,模拟 Internet。第三台机器 HostV 是内部局域网的计算机。Internet 中的主机 HostU 上的用户希望通过 VPN 隧道与内部局域网的主机 HostV 通信。为模拟此设置,我们使用 docker 网络“intranet”将 HostV 与 VPN 服务器网关的内网口连接,模拟内部局域网。在这种设置中,HostV 不能直接从 Internet 访问,即不能直接从 HostU 访问。为实现上述的网络环境配置,我们需要执行以下操作:

# 在VM上创建 docker 网络 extranet
seed@VM:~$ sudo docker network create --subnet=10.0.2.0/24 --gateway=10.0.2.8 --opt "com.docker.network.bridge.name"="docker1" extranet
# 在VM上创建 docker 网络 intranet
seed@VM:~$ sudo docker network create --subnet=192.168.60.0/24 --gateway=192.168.60.1 --opt "com.docker.network.bridge.name"="docker2" intranet

# 创建并运行容器HostU
seed@VM:~$ sudo docker run -it --name=HostU --hostname=HostU --net=extranet --ip=10.0.2.7 --privileged "seedubuntu" /bin/bash
# 创建并运行容器HostV
seed@VM:~$ sudo docker run -it --name=HostV --hostname=HostV --net=intranet --ip=192.168.60.101 --privileged "seedubuntu" /bin/bash

实际上,完成上述工作后,HostU 和 HostV 是可以相互 ping 通的,为了模拟二者不可见的情况,需要将它们的默认路由都删掉:

# 删除默认路由
root@HostU:/# route delete default
root@HostV:/# route delete default

2. 运行 VPN 服务器

我们首先在服务器 VM 上运行 VPN 服务器程序 vpnserver。程序运行后,系
统中将出现一个虚拟 TUN 网络接口(我们可以使用 ifconfig -a 命令看到它)
在大多数情况下,接口的名称将是 tun0,但它们可以是 tunX,其中 X 是一个数
字。

此新接口尚未配置,因此我们需要通过为其提供 IP 地址来配置它。我们使用 192.168.53.1 作为此接口的 IP 地址。运行以下命令。第一个命令将启动服务器程序,第二个命令将一个 IP 地址分配给 tun0 接口,然后激活它。由于 ./vpnserver 时守护进程,每次有数据包时都会打印信息,所以开两个终端分别执行。

# VM 启动 vpnserver
seed@VM:~/vpn$ sudo ./vpnserver
# 在 VM 上配置 tun0 虚拟 IP 地址并激活接口
seed@VM:~$ sudo ifconfig tun0 192.168.53.1/24 up

除非特别配置,否则计算机将仅充当主机,而不充当网关。VPN Server 需要在内网和隧道之间转发报文,因此需要作为网关。我们需要为计算机启用 IP 转发,使其行为类似于网关。由于 VM 上的 iptables 规则可能阻断转发报文,还需要清除 iptables 规则。

# VM 启动 IP转发
seed@VM:~/vpn$ sudo sysctl net.ipv4.ip_forward=1
# VM 清除 iptables 规则
seed@VM:~/vpn$ sudo iptables -F

3. 运行 VPN 客户端

在 HostU 上运行下面两个命令,第一个命令将启动 VPN 客户端以连接到10.0.2.8 上运行的 VPN 服务器程序,第二个容器在容器 HostU 中配置 tun0 虚拟 IP 地址并激活接口。依然在两个终端分别执行。

# 启动 VPN 客户端
root@HostU:/vpn# ./vpnclient 10.0.2.8
# 配置 tun0 虚拟 IP 地址并激活接口
root@HostU:/# ifconfig tun0 192.168.53.5/24 up

然后 HostU 中也会出现 tun0 接口:

4. 在 HostU 设置路由

完成上述两个步骤后,隧道将会建立。在我们使用隧道之前,我们需要在 HostU 和 VPN 服务器上设置路由,以指示通过隧道的预期流量。在 HostU 上,我们需要将所有进入专用网络(192.168.60.0/24)的数据包定向到 tun0 接口,从该接口可以通过 VPN 隧道转发数据包。如果没有此设置,我们将无法访问专用
网络。

使用 route 命令添加路由条目,下述命令会将 192.168.60.0/24 数据包定向到接口tun0:

# 增加路由条目
root@HostU:/vpn# route add -net 192.168.60.0/24 tun0

至此,HostU 和 VM 之间就建立了 VPN 隧道,HostU 发向 192.168.60.X 的报文都会通过 tun0 转发给 VM 。VM 在将 tun0 解析出的报文转发至 docker2 。

此时,HostU 的报文已经可以到达 HostV 了,可以尝尝试 ping 一下 HostV,然后用 wireshark 截包,会发现 docker1,tun0,docker2 三个网卡均有报文。

其中,tun0 和 docker2 截获的都是从 192.168.53.5 发往 192.168.60.101 的 ICMP ,因此可以确认,经过tun0 的封装,源 IP 变为了 192.168.53.5 。

值得注意的是,只捕获到了 HostU 发往 HostV 的报文,没有任何 HostV 发往 HostU 的回应。这是因为,前面已经删了 HostV 的默认路由(除了内网之外的所有目的 IP 向网关转发),所以不会对发往 192.168.53.5 的报文进行处理,所以 ping 不同。

5. 在 HostV 上设置路由

得知了源 IP 为 192.168.53.5 之后,可以对这个网段的目的报进行 route 配置,使发往 192.168.53.0/24 网段的报文都均转向网关 192.168.60.1 即可。运行下面命令:

root@HostV:/# route add -net 192.168.53.0/24 gw 192.168.60.1 eth0

这样 HostV 就可以把响应发给 VM 了。那 VM 需不需要另做配置呢?实际上,只需要一个,就是把发送192.168.53.0/24 网段的报文转给 tun0 接口,但这个路由配置在 tun0 刚创建时已经自动配置好了,所以不需要我们另外做什么配置。

至此,不仅配好了 HostU 和 VM 之间的 VPN 隧道,还配好了 HostU 发往隧道的路由配置与 HostV 回应的路由配置,两者就可以相互通信啦。

重新 ping 一下看看:

image-20220509214058761

可以看到,ping通了,VPN 隧道里也做转发。

telnet 一下看看(记得 HostV 要开启 telnet 服务):

image-20220509214547296

很明显,telnet也成功了,VPN 隧道里有相关的转发。

现在试着在 VM 里把 VPN 关了。因为 vpnserver 是守护进程,所以需要 kill 掉后台的进程。关了之后,虽然 telnet 连接没断,但是不管输入什么都不会回显了,数据传不到 HostV 了。

image-20220509224317747


好啦,基于 UDP 的 VPN 隧道实验就完了。不过没有进行加密、证书等,显然不是严格意义上的 VPN,下一篇会记录安全 VPN 的实验过程。


文章作者: SrcMiLe
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 SrcMiLe !
评论
  目录