作者:0x7F@知道創宇404實驗室
時間:2021年7月21日

0x00 前言

最近在工作中遇到 VPN 的相關問題,之前一直對 VPN 的原理存在一些疑惑,借此機會學習一下 VPN 的原理以及進行實現驗證。

由于 VPN 在不同系統下的實現方式不同,為了便于學習和理解,這里我們選擇 Linux 環境,我本地測試環境使用的是 Ubuntu 18.04 x64

本文從 TUN/TAP 出發,逐步理解 VPN 中的技術細節;并結合 simpletun 源碼,進行 VPN 的原理驗證。

0x01 VPN是什么

VPN 全稱為虛擬私人網絡(Virtual Private Network),常用于連接中、大型企業或團體間私人網絡的通訊方法,利用隧道協議(Tunneling Protocol)來達到發送端認證、消息保密與準確性等功能。

比如多地辦公的公司,可以使用 VPN 將不同地區連接在同一內網下;或者在家辦公的時候也可以通過 VPN 接入公司內網中。

VPN 以 CS 架構運行,工作流程如下:

1.VPN工作流程

在外網的用戶可以使用 vpn client 連接組織搭建的 vpn server 以建立通信隧道,隨后便建立了虛擬的私人網絡,處于外網的 worker 和內網中的 server 可以相互通信。

那么我們可以簡單理解 VPN,由 VPN client 捕獲用戶發出的報文,封裝報文后通過物理網絡通信鏈路將報文發給 VPN serverVPN server 接收到報文后進行解包,再將其轉發給實際的目標,反之同理; VPN 在邏輯層面構建了虛擬網絡。

0x02 TUN/TAP

那么在代碼層面 VPN 是如何實現的呢?我們可以先來看看 TUN/TAP。

TUN/TAP 是操作系統內核中的虛擬網絡設備,由軟件進行實現,向操作系統和應用程序提供與硬件網絡設備完全相同的功能。其中 TAP 是以太網設備(二層設備),操作和封裝以太網數據幀,TUN 則是網絡層設備(三層設備),操作和封裝網絡層數據幀。

當應用程序發出報文后,報文將通過操作系統協議棧處理,到達網絡設備,硬件網絡設備將收到的報文轉化為電信號發出,而虛擬網絡設備(TUN/TAP)不具備實際的物理功能,報文需要上層應用進行處理,如下:

2.硬件/虛擬網絡設備

我們直接使用命令創建 TUN/TAP 設備,并進行測試:

# ip tuntap 創建名為 tun0 的 tun 設備
sudo ip tuntap add dev tun0 mod tun
# 為 tun0 配置 ip
sudo ifconfig tun0 192.168.0.10 netmask 255.255.255.0
# 查看 tun0 網卡
ifconfig tun0

如下:

3.通過命令創建TUN設備

在 VPN 中我們可以借助 TUN/TAP 來捕獲用戶發出的報文。

0x03 虛擬通信鏈路

按照 TUN/TAP 的工作特性,我們可以編寫程序直接讀寫虛擬網卡(也就是物理網卡實際收發報文的過程),來實現捕獲用戶數據以及傳遞用戶數據。(TUN 和 TAP 有不同的應用場景,下文我們將以更簡單的 TUN 作為例子)

隨后,位于不同主機上的程序通過 socket 進行通信,將從虛擬網卡的接收的數據通過 socket 發送給對端,這就是一個 VPN 的雛形了,如下:

4.虛擬通信鏈路工作流程

simpletun 是這種方案的最小實現(源碼僅 300+ 行,感興趣的小伙伴可以自行學習),在源碼中實現了創建虛擬網絡設備以及 socket 通信,借助 simpletun 可以幫助我們快速進行驗證。

需要注意一點,simpletun 啟動后需要我們手動配置虛擬網卡的 ip 地址,當 ip 地址未配置時,兩端相互發送數據(部分操作系統會自動發送)會造成程序異常退出,所以在代碼中添加一個 sleep(30) 便于我們配置 ip 地址:

5.在simpletun中添加sleep

在兩臺 Ubuntu 測試環境下配置并進行驗證:

# A主機
# 編譯 simpletun
gcc simpletun.c -Wall -o vpn
# 作為 vpn server 啟動,并開啟 debug,默認監聽 55555
sudo ./vpn -i tun0 -s -d
# 配置 tun 網卡地址
sudo ifconfig tun0 192.168.0.10 netmask 255.255.255.0

# B主機
# 編譯 simpletun
gcc simpletun.c -Wall -o vpn
# 作為 vpn client 啟動,連接 server,并開啟 debug
sudo ./vpn -i tun0 -c 10.11.33.50 -d
# 配置 tun 網卡地址
sudo ifconfig tun0 192.168.0.11 netmask 255.255.255.0

此時兩臺主機位于 192.168.0.0/24 虛擬網絡網段下,可以相互通信,如下:

6.虛擬通信鏈路兩端通信

0x04 訪問內網網段

在上文的驗證中,我們可以實現兩端的虛擬網絡搭建和通信,但實際 VPN 的使用場景是需要通過 VPN 訪問整個內網網段,在這種使用場景下,VPN server 至少配置有兩張物理網卡,其中一張接入內網網段,另一張則連接到互聯網。

按照 0x03 虛擬通信鏈路 的鏈路,VPN client 發送報文到內網主機,VPN server 接收到該報文后,將其寫入到虛擬網卡中,隨后報文進入 TCP/IP 協議棧,但是由于 IP 地址不是 VPN server 自己,該報文會被丟棄,無法正常進行通信;這里我們需要借助「報文轉發」,將內網報文從虛擬網卡轉發到內網網卡上。其新的工作流程如下:

7.VPN訪問內網網段

VPN server 一般會作為內網網關,內網主機無需任何額外配置就可以在虛擬網段下正常工作。

我們按照該流程配置測試環境,復用 0x03 虛擬通信鏈路 中的環境,在 VPN server 上我們使用 docker 模擬內網網段和主機,其環境搭建如下:

8.VPN測試環境搭建

然后按照 0x03 虛擬通信鏈路 中的方式,啟動 simpletun 并使用 ifconfig 配置 ip 地址,創建虛擬通信鏈路;使用如下命令開啟報文轉發:

# 臨時開啟報文轉發
echo "1" > /proc/sys/net/ipv4/ip_forward

實際上在該測試環境下,docker 會自動開啟報文轉發

再通過 iptables 配置轉發策略,如下:

# 將入口網卡、來源ip為 192.168.0.0/24 轉發至 docker0
sudo iptables -A FORWARD -i tun0 -s 192.168.0.0/24 -o docker0 -j ACCEPT
# 將入口網卡、目的ip為 192.168.0.0/24 轉發至 tun0
sudo iptables -A FORWARD -i docker0 -d 192.168.0.0/24 -o tun0 -j ACCEPT

實際上在該測試環境下,第二條可以不用配置,因為 docker 會自動配置轉發策略,會覆蓋這條策略

除此之外,為了在 VPN client 可以訪問到內網主機,需要手動添加路由:

# VPN client 添加內網網段路由,設置為虛擬網絡設備 tun0
sudo route add -net 172.17.0.0/24 tun0

此時 VPN 配置完成,內網主機和 VPN client 相互連通:

9.內網主機訪問以及驗證

0x05 拓展

上文中我們使用最小實現驗證了 VPN 的工作原理,但是實際場景卻比這個復雜很多,這里我們簡單拋出一些問題作為拓展學習。

1.VPN作為網關?
VPN server 一般作為網關進行配置,內網主機不用進行額外配置,也可以把報文發送給 VPN server。

2.UDP通信鏈路?
simpletun 中 VPN server 和 client 之間使用 TCP 進行通信,但是在實際場景一般使用 UDP 進行通信。

當使用 TCP 作為通信隧道時,并且上層應用也使用 TCP,也就是 tcp in tcp,當出現丟包時,上層應用的 TCP 和 VPN 通信隧道的 TCP 都會進行重傳,從而通信中出現大量的重傳報文,降低通信效率;如果在這種情況下,以 UDP 作為通信隧道,tcp in udp,丟包后將只由上層應用的 TCP 進行重傳。

3.etc

0x06 總結

最后感謝 rook1e@知道創宇404實驗室 小伙伴同我一起學習和研究,解決了諸多問題。

本文從 VPN 原理出發,介紹了關鍵作用的 TUN/TAP 虛擬網絡設備,并結合 simpletun 創建了兩端的虛擬通信鏈路,最后配合報文轉發,實現并驗證了 VPN 的通信工作原理。

VPN 的實現較為簡單,但涉及到各種細枝末節的網絡知識;這里的最小驗證,可以為我們實現更為復雜的 VPN 或基于 VPN 技術的其他項目提供參考。



References:
https://zh.wikipedia.org/wiki/%E8%99%9B%E6%93%AC%E7%A7%81%E4%BA%BA%E7%B6%B2%E8%B7%AF
https://zhaohuabing.com/post/2020-02-24-linux-taptun/
https://www.cnblogs.com/sparkdev/p/9262825.html
https://serverfault.com/questions/39307/linux-ip-forwarding-for-openvpn-correct-firewall-setup
https://liuyehcf.github.io/2019/08/25/OpenVPN-%E8%BD%AC%E8%BD%BD/
https://yunfwe.cn/2018/05/24/2018/%E4%B8%80%E8%B5%B7%E5%8A%A8%E6%89%8B%E5%86%99%E4%B8%80%E4%B8%AAVPN/
https://github.com/gregnietsky/simpletun


Paper 本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.bjnorthway.com/1648/