VPN是如何工作的

VPN

想象一下这两种场景:

  1. 你下班家,公司线上一台机器出事故了,这个时候你需要远程到这台机器上来排查并处理问题。
  2. 你在其他城市实习,这个时候学校需要选下学期的课了,但是教务网站只对校园网开放。

在这两种场景中,都离不开一种关键的技术:VPN(Virtual Private Network:虚拟局域网)。

VPN提供了一种在公网上安全的加入局域网的方式,可以我们这里就以开源的 OpenConnect VPN 为例,探讨一下 VPN 这种技术是如何安全的将局域网在互联网中横向扩展的。

需要说明的一点是,通常大家会将能实现访问 Google 等网站的技术都叫做 VPN,但比如常见的 ShadowSocks,v2ray(vmess)等其实只是一种加密部分常用流量的代理技术,并不能真正构建一个局域网,严格意义上来说他们并不是VPN。

常见的 VPN 技术按照主要使用的传输协议可以分为 L2TP/IPSec VPN、PPTP VPN、TLS VPN等,我们这次则主要分析 openconnect 这个 TLS VPN 产品来概览 VPN 技术的实现全流程。

OpenConnect

架设服务端

要在公网中接入到某个特定的局域网,首先我们需要在局域网中找一台服务器,这个服务器需要一张网卡在局域网一张网卡在公网,然后我们在上面需要一个 VPN 服务。OpenConnect 开源的 ocserv 就是一个不错的选择,接着我们就可以通过客户端在公网上和服务端交互建立隧道来实现远程局域网访问,openconnect 组织开源了一个同名的 openconnect 客户端用于和 ocserv 配套使用。

image-20201222234058831

ocserv服务端模块图:

ocserv服务端模块图

身份认证

并不是所有的连接请求 server 都要接受,不然这会是一个非常大的安全隐患,这就需要 server 能提供一套身份认证机制,识别出来哪些想要蒙混过关的恶意攻击者。

ocserv 支持密码认证、GSSAPI、公钥+证书等身份认证机制,通常安全级别较高的企业还会对认证进行定制化的开发配合动态令牌进行使用。

给客户端配置网络

客户端要接入一个网络的话,首先肯定需要一张身份证:IP地址,以及对应的子网掩码。通常企业或者学校会在内网中架设私有的DNS服务器,那么这个 DNS 服务器的地址也是需要告知客户端的,需要它来帮助解析一些内网域名。

我们正常加入一个网络的时候,DHCP服务器会下发给我们这些信息。而当我们通过 VPN 这种非常规的方式加入时,VPN server 就需要来承担这一部分工作。

在 oscerv 中,它会读取一个指定格式的配置文件,配置文件中的 ipv4-network ipv4-netmask dns

这三个选项指定了提供给客户端的 IP 地址池和 DNS 地址。

ipv4-network = 192.168.1.0 # 每次会随机从 192.168.1 这个子网IP池中分配一个给客户端使用
ipv4-netmask = 255.255.255.0
dns = 192.168.1.2

VPN 身份认证完毕之后,openconnect 就会在计算机中创建一张 tun 虚拟网卡,之后 openconnect 就可以读取到发送到这张网卡的原始 IP 报文,处理封装后再经由真实的物理网卡发送出去,然后再由那边的 oscerv 服务端接收处理,再转发到真实的目标地址。

在创建虚拟网卡之后,我们需要将网络流量导到这张网卡上来,默认 openconnect 会在系统中添加一条路由规则,将虚拟网卡设置为默认出口,相当于命令:route add 0.0.0.0 tun4,不过有可能并不是所有的流量都需要流经 VPN,可以在 oscerv 服务端通过 route 配置项下发路由表规则指定哪些流量走 VPN。

网络传输

客户端设好了 IP 地址虚拟网卡等配置后,客户端就可以正常发送数据包到服务端了,但是另一个问题来了,如果只是简单地把整个原始局域网 IP 包放在一个公网IP包中,那么这个风险是非常大的,任何一个中间的设备都可以轻松的窥探其中传输的数据。

所以数据包最好能经过加密之后再进行传输,目前 VPN 领域内比较主流的加密传输协议有应用层的 TLS 和网络层的 IPSec。

openconnect 的传输是基于 TLS 的,客户端和服务端之间会维持一条基于 TCP 的 TLS 用于传输控制配置等信息,而数据则通过另一条基于 UDP 的 DTLS 连接(UDP 版 TLS)传输。

不直接用 TLS 传输主要是考虑到通常网络流量中 TCP 流量占大多数,而用 TCP 传输 TCP 会有一些性能问题,考虑一个场景,如果一个 TCP 包丢了,那么VPN和业务端对应的两条TCP连接都会需要重传这一个包,但却是为了同一份数据,这就会造成大量不必要的重传。

所以 openconnect 主要使用 udp 传输数据,基于 DTLS 实现的可靠传输来保证数据的完整性,在 UDP 不可用的时候才会降级到 TCP 的 TLS 通道。

通道建立好之后,就可以传输数据了,由于我们要传输的虚拟 IP 数据包在物理传输链路上直到到达 VPN 服务端之前都是不会有人关心的,所以这就有了压缩的空间,我传输的时候把 VPN 网络层的报文压缩了,VPN 服务端再解开,对于网络协议这种具有明显规律的数据,压缩的收益还是比较高的,这样就能在一定程度上提高传输速度。

具体到 openconnect,它支持目前压缩效率比较高 LZ4 和 LZS 两种压缩算法。

image-20201222233502643

这样,一个安全、可靠、高效的VPN通道就建立完成了~