我在家裡運行了一個wireguard 伺服器,以便能夠從外部進入我的家庭網路。 Wireguard 使用 UDP。不幸的是,我不能像以前那樣在我的fritz box 中打開IPv4 UDP 端口,因為我的提供者已將我置於CG-NAT 後面,我必須使用IPv6,但IPv6 在我的工作場所尚不可用。
因此,我在家裡使用 IPv6,在工作中使用 IPv4,但無法存取我的家庭網路。
我還有一個帶有公共 IPv4 的 VPS,我可以為 TCP 流量建立反向 ssh 隧道。不幸的是,ssh 隧道僅支援 TCP,無論如何都不應該透過 TCP 進行 VPN 連接,這就是為什麼wireguard 僅使用 UDP 的原因。
VPN-Server VPN-Client
(UDP) | | (IPv4 UDP)
| | | not possible |
-----------|--------| <-------------------
| |
NAT CG-NAT
VPN-Server VPN-Client
(UDP) (IPv4 UDP)
| |
| |
inside agent outside agent
tunnel tunnel
|| | | ||
|| punch| punch| ||
| --------->|-------->|-------------------- |
-----------|<--------|<--------------------
| |
| |
NAT CG-NAT
外部代理程式在具有公共 IPv4 的 VPS 上運行
內部代理程式將向外部代理程式的公共 IP 和連接埠發送 UDP 資料報,這將在兩個 NAT 上打孔,外部代理將接收這些資料報並從其來源位址和連接埠了解如何將資料封包傳回內部代理程式。
VPN 用戶端可以將 UDP 發送到外部代理,這些封包將轉發到內部代理,然後從那裡轉發到 VPN 伺服器,VPN 伺服器可以回覆內部代理,這些封包將轉發到外部代理,然後從那裡轉發到VPN客戶端。
外部代理將顯示為客戶端的 VPN 伺服器,內部代理將顯示為伺服器的 VPN 用戶端。
多個客戶端連接是可能的,因為它將為每個連接到外部的新客戶端使用內部代理上的隧道和新套接字,因此對於伺服器來說,它看起來就像多個客戶端正在內部代理的主機上運作一樣。
在兩台機器上克隆或解壓縮程式碼並建構它。您至少需要 make 和 gcc。進入源碼目錄並使用命令
$ make
成功建置後,您最終會在同一資料夾中看到二進位udp-tunnel
。現在您可以直接從終端啟動它(當然使用正確的選項)來進行一些快速測試,或者您可以在 makefile 的幫助下安裝它。
編譯後的二進位udp-tunnel
可以充當內部代理,也可以充當外部代理,這取決於您在命令列上傳遞的選項。
假設 VPN 伺服器正在偵聽 UDP 1234,在本機上執行(與內部代理相同),外部電腦是 Jump.example.com,我們希望它偵聽 UDP 連接埠 9999。
在內部主機上我們以
$ ./udp-tunnel -s localhost:1234 -o jump.example.com:9999
在外部主機上我們以
$ ./udp-tunnel -l 9999
makefile 包含 3 個安裝目標: install
僅安裝二進位文件, install-outside
和install-inside
也安裝 systemd 服務文件。後兩者需要傳遞給 make 的變數才能正常運作。
若要在跳轉主機上安裝外部代理程式(假設您需要連接埠 9999),請執行下列命令:
$ sudo make install-outside listen=9999
這會將二進位檔案安裝到/usr/local/bin/
並將 systemd 服務檔案安裝到/etc/systemd/system/
中,其中包含在外部代理模式下使用連接埠 9999 啟動它所需的命令。
要在內部電腦上安裝內部代理,請使用以下命令(假設您的 VPN 伺服器是 localhost:1234,跳轉主機是 Jump.example.com):
$ sudo make install-inside service=localhost:1234 outside=jump.example.com:9999
這將再次將二進位檔案安裝到/usr/local/bin/
並將 systemd 單元檔案安裝到/etc/systemd/system/
此時,您可能需要快速查看 systemd 單元文件,以了解二進位檔案的使用方式並檢查選項是否正確。這些選項應類似於上面快速測試中所述。
systemd 檔案安裝並確認正確後,尚未啟用自動啟動,您需要啟用並啟動它們。在內部機器上:
$ sudo systemctl enable udp-tunnel-inside.service
$ sudo systemctl start udp-tunnel-inside.service
在外部機器上
$ sudo systemctl enable udp-tunnel-outside.service
$ sudo systemctl start udp-tunnel-outside.service
沒有加密。資料包會按原樣轉發,假設您正在隧道傳輸的任何服務都知道如何自行保護或加密其資料。 VPN 連線通常就是這種情況。
此外,攻擊者可能想要欺騙來自內部代理的保活資料包,以迷惑外部代理並將隧道轉移到自己的計算機,從而導致服務中斷。為了防止這種非常簡單的攻擊,可以使用基於雜湊的訊息驗證碼來驗證保活資料封包。您可以在隧道兩端使用 -k 選項來使用預共享密碼來啟動此功能。
在內部主機上你會這樣使用它
$ ./udp-tunnel -s localhost:1234 -o jump.example.com:9999 -k mysecretpassword
在外部主機上,您可以使用以下命令啟動它
$ ./udp-tunnel -l 9999 -k mysecretpassword
成功完成上述安裝步驟後,您可能需要手動編輯兩端的 systemd 檔案並新增 -k 選項,然後在兩端重新載入並重新啟動。
然後,保活訊息將包含對此密碼的 SHA-256 以及嚴格遞增的隨機數,該隨機數只能使用一次,以防止簡單的重播攻擊。
兩個代理程式都維護一個連接列表,每個連接都儲存與該特定客戶端連接相關的套接字位址和套接字句柄。
啟動時,內部代理將啟動到外部代理的傳出隧道,將其標記為未使用,並透過其發送保活資料包。外部代理程式將看到這些資料包,並將該隧道及其來源位址新增至其自己的連線清單中。 keepalive 封包使用隨機數字和身份驗證程式碼進行簽名,因此它們無法被欺騙或重播。
當客戶端連接時,外部代理將沿著未使用的備用隧道發送客戶端數據,內部代理將看到這一點,將隧道標記為活動狀態,創建一個用於與服務通信的套接字並在兩個方向上轉發資料。然後,它還將立即建立另一個新的傳出備用隧道,為下一個傳入用戶端做好準備。
當新的備用隧道到達外部代理程式時,它會將其新增至自己的連線清單中,以便能夠立即為下一個連線用戶端提供服務。
此時,兩個代理程式的清單中都有 2 個隧道,一個處於活動狀態,一個處於備用狀態,等待下一個客戶端連線。
不活動逾時將確保死隧道(那些過去被標記為活動但一段時間內沒有客戶端資料的隧道)將被刪除,並且它們的套接字將關閉。內部代理將檢測到長時間缺少轉發數據,將其從自己的列表中刪除並關閉其套接字,然後一段時間後,外部代理將檢測到不再有任何保活到達此連接並將其從自己的清單中刪除也列出來。
該程式碼仍處於高度實驗階段,因此不要將價值數百萬美元的業務建立在它的基礎上,至少現在還不是。它對我來說完美地滿足了目的,但它可能會導致你的伺服器崩潰、燒毀和爆炸。你已被警告過。