VPN 原理以及實現

作者: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) 不具備實際的物理功能,報文需要上層應用進行處理,如下:

  1. 硬件 / 虛擬網絡設備

我們直接使用命令創建 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

如下:

  1. 通過命令創建 TUN 設備

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

**  0x03 虛擬通信鏈路  **

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

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

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

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

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

  1. 在 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 虛擬網絡網段下,可以相互通信,如下:

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

**  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 相互連通:

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

**  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

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/uM2U4RAJCGdxYvtsrHPbYg