精品軟體與實用教程
1. 快速開始
工欲善其事,必先利其器!配置 WireGuard 的大致流程如下:
準備工作
查看當前核心版本
uname -r #4.18.0-348.2.1.el8_5.x86_64 uname -a #Linux mir4 4.18.0-348.2.1.el8_5.x86_64 #1 SMP Tue Nov 16 164:205 UTC x86_64 x86_64 GNU/Linux cat /etc/redhat-release #CentOS Linux release 8.5.2111
升級系統內核
dnf -y update

在CentOS 8.x 上安裝EPEL 儲存庫
EPEL 代表“Extra Packages for Enterprise Linux”,它是一個自由開源的附加軟體包倉庫,可用於CentOS 和RHEL 伺服器。顧名思義,EPEL 倉庫提供了額外的軟體包,這些軟體在CentOS 8 和RHEL 8 的預設軟體包倉庫中不可用。
epel官方網站:https://fedoraproject.org/wiki/EPEL/zh-cn
dnf search epel #epel-next-release.noarch : Extra Packages for Enterprise Linux Next repository configuration #epel-release.noarch : Extra Packages for Enterprise Linux repository configuration #檢視系統軟體庫中的系統版本碼隨身碟版本碼epel-release #透過URL從網站安裝epel來源dnf install https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm #啟用PowerTools儲存庫,因為EPEL套件可能依賴這些儲存庫中的套件編號#安裝成功後,測試儲存庫中的軟體包數量sudo dnf --disablerepo="*" --enablerepo="epel" list available | wc -l
在CentOS 8.x 上安裝ELRepo 儲存庫
ELRepo 倉庫是基於社群的企業級Linux 倉庫,提供對RedHat Enterprise (RHEL) 和其他基於RHEL的Linux 發行版(CentOS、Scientific、Fedora 等)的支援。
ELRepo 聚焦於和硬體相關的軟體包,包括檔案系統驅動、顯示卡驅動、網路驅動、音效卡驅動和相機驅動等。
#導入ELRepo倉庫的公共金鑰rpm --import https://www.elrepo.org/RPM-GPG-KEY-elrepo.org #安裝ELRepo倉庫的yum來源dnf install https://www.elrepo.org/elrepo-release-8.el8.elrepo.nodmro. --enablerepo="elrepo-kernel" list available
伺服器上需要安裝eple和ELRepo 儲存庫,之後才能進行wireguard的安裝。
#軟體來源安裝完畢後,清除快取dnf clean all #重新建立儲存庫的快取yum makecache reboot
安裝
官方安裝參考:https://www.wireguard.com/install/
以下提供幾種作業系統的安裝方式,包括Centos8、Centos7、Ubuntu、MacOS。
#CentOS8 dnf install elrepo-release epel-release dnf config-manager --set-enabled powertools dnf copr enable jdoss/wireguard dnf install wireguard-dkms wireguard-tools 1TP5 installyrelum installalease https://www.elrepo.org/elrepo-release-7.el7.elrepo.noarch.rpm yum install yum-plugin-elrepo yum install kmod-wireguard wireguard-tools #如果你使用的是非標準內核,需要安裝DinstaKMS包yum llll https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm curl -o /etc/yum.repos.d/jdoss-wireguard-epel-7.repo https://copr.fedorainfracloud.org/coprs install wireguard-dkms wireguard-tools #Ubuntu≥18.04 apt install wireguard #Ubuntu≤16.04 add-apt-repository ppa:wireguard/wireguard apt-get update instagu-get llTire

Windows 用戶端下載位址:
- https://download.wireguard.com/windows-client/wireguard-amd64-0.1.1.msi
- https://download.wireguard.com/windows-client/wireguard-installer.exe

Android 和iOS 可透過應用程式商店下載相關用戶端:
https://www.wireguard.com/install/#installation


在中繼伺服器上開啟IP 位址轉送:
echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.conf
echo "net.ipv4.conf.all.proxy_arp = 1" >> /etc/sysctl.conf
sysctl -p /etc/sysctl.conf
新增iptables 規則,允許本機的NAT 轉換:
iptables -A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT iptables -A FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT iptables -A FORWARD -i wg0 -o wg0 -m conntINGpstate -m connt –POtable -i wg0 -o wg0 -m conntINGm 192.0.2.0/24 -o eth0 -j MASQUERADE
需要把 eth0 改成你實際使用的網路卡介面名稱。
編寫設定檔
設定檔可以放在任何路徑下,但必須透過絕對路徑引用。預設路徑是 /etc/wireguard/wg0.conf。
#建立一個設定檔vi /etc/wireguard/wg0.conf
產生金鑰
產生私鑰
wg genkey > example.key
產生公鑰
wg pubkey < example.key > example.key.pub
啟動與停止
$ wg-quick up /full/path/to/wg0.conf
$ wg-quick down /full/path/to/wg0.conf
# 啟動/停止 VPN 網路介面
$ ip link set wg0 up
$ ip link set wg0 down
# 註冊/註銷 VPN 網路介面
$ ip link add dev wg0 type wireguard
$ ip link delete dev wg0
# 註冊/登出 本地 VPN 位址
$ ip address add dev wg0 192.0.2.3/32
$ ip address delete dev wg0 192.0.2.3/32
# 新增/刪除 VPN 路由
$ ip route add 192.0.2.3/32 dev wg0
$ ip route delete 192.0.2.3/32 dev wg0
查看資訊
接口:
# 查看系統 VPN 介面資訊
$ ip link show wg0
# 查看 VPN 介面詳細信息
$ wg show all
$ wg show wg0
地址:
# 查看 VPN 介面位址
$ ip address show wg0
路由
# 檢視系統路由表
$ ip route show table main
$ ip route show table local
# 取得到特定 IP 的路由
$ ip route get 192.0.2.3
一鍵安裝
一鍵安裝請參考這個項目:WireGuard installer[3]
2. 配置詳解
WireGuard 使用 INI[4] 語法作為其配置文件格式。設定檔可以放在任何路徑下,但必須透過絕對路徑引用。預設路徑是 /etc/wireguard/wg0.conf。
設定檔的命名形式必須為 ${WireGuard 介面的名稱}.conf。通常情況下WireGuard 介面名稱以 wg 為前綴,並從 0 開始編號,但你也可以使用其他名稱,只要符合正規表示式 ^[a-zA-Z0-9_=+.-]{1,15}$ 就行。
你可以選擇使用 wg 指令來手動設定VPN,但一般建議使用 wg-quick,它提供了更強大且使用者友好的設定體驗,可以透過設定檔來管理設定。
下面是一個設定檔範例:
[Interface]
# Name = node1.example.tld
Address = 192.0.2.3/32
ListenPort = 51820
PrivateKey = localPrivateKeyAbcAbcAbc=
DNS = 1.1.1.1,8.8.8.8
Table = 12345
MTU = 1500
PreUp = /bin/example arg1 arg2 %i
PostUp = /bin/example arg1 arg2 %i
PreDown = /bin/example arg1 arg2 %i
PostDown = /bin/example arg1 arg2 %i
[Peer]
# Name = node2-node.example.tld
AllowedIPs = 192.0.2.1/24
Endpoint = node1.example.tld:51820
PublicKey = remotePublicKeyAbcAbcAbc=
PersistentKeepalive = 25
[Interface]
這一節定義本地VPN 配置。例如:
本地節點是客戶端,只路由自身的流量,只暴露一個IP。
[Interface]
# Name = phone.example-vpn.dev
Address = 192.0.2.5/32
PrivateKey =本地節點是中繼伺服器,它可以將流量轉送到其他對等節點(peer),並公開整個VPN 子網路的路由。
[Interface]
# Name = public-server1.example-vpn.tld
Address = 192.0.2.1/24
ListenPort = 51820
PrivateKey =
DNS = 1.1.1.1
① # Name
這是 INI 語法中的標準註釋,用於展示該配置部分屬於哪個節點。這部分設定會被WireGuard 完全忽略,對VPN 的行為沒有任何影響。
② Address
定義本地節點應該對哪個位址範圍進行路由。如果是常規的客戶端,則將其設定為節點本身的單一IP(使用CIDR 指定,例如192.0.2.3/32);如果是中繼伺服器,則將其設定為可路由的子網路範圍。
例如:
常規客戶端,只路由自身的流量:Address = 192.0.2.3/32 中繼伺服器,可以將流量轉送到其他對等節點(peer):Address = 192.0.2.1/24 也可以指定多個子網路或IPv6 子網路:Address = 192.0.2.1/24,2001:DB8::/64
③ ListenPort
當本地節點是中繼伺服器時,需要透過此參數指定連接埠來監聽傳入VPN 連接,預設連接埠號碼為 51820。常規客戶端不需要此選項。
④ PrivateKey
本機節點的私鑰,所有節點(包括中繼伺服器)都必須設定。不可與其他伺服器共用。
私鑰可透過指令 wg genkey > example.key 來產生。
⑤ DNS
透過DHCP 向客戶端宣告DNS 伺服器。用戶端將會使用這裡指定的DNS 伺服器來處理VPN 子網路中的DNS 請求,但也可以在系統中覆寫此選項。例如:
如果不配置則使用系統預設DNS 可以指定單一DNS:DNS = 1.1.1.1 也可以指定多個DNS:DNS = 1.1.1.1,8.8.8.8
⑥ Table
定義VPN 子網路使用的路由表,預設不需要設定。此參數有兩個特殊的值要注意:
Table = off : 禁止建立路由 Table = auto(預設值) : 將路由新增至系統預設的table 中,並啟用對預設路由的特殊處理。
例如:Table = 1234
⑦ MTU
定義連接到對等節點(peer)的 MTU(Maximum Transmission Unit,最大傳輸單元),預設不需要設置,一般由系統自動決定。
⑧ PreUp
啟動VPN 介面之前執行的命令。這個選項可以指定多次,依序執行。
例如:
新增路由:PreUp = ip rule add ipproto tcp dport 22 table 1234
⑨ PostUp
啟動VPN 介面之後執行的命令。這個選項可以指定多次,依序執行。
例如:
從檔案或某個指令的輸出讀取設定值:
PostUp = wg set %i private-key /etc/wireguard/wg0.key <(some command here)新增一行日誌到文件中:
PostUp = echo "$(date +%s) WireGuard Started" >> /var/log/wireguard.log呼叫WebHook:
PostUp = curl https://events.example.dev/wireguard/started/?key=abcdefg新增路由:
PostUp = ip rule add ipproto tcp dport 22 table 1234新增iptables 規則,啟用封包轉送:
PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -A FORWARD -o %i -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE強制WireGuard 重新解析對端網域的IP 位址:
PostUp = resolvectl domain %i "~."; resolvectl dns %i 192.0.2.1; resolvectl dnssec %i yes
⑩ PreDown
停止VPN 介面之前執行的命令。這個選項可以指定多次,依序執行。
例如:
新增一行日誌到文件中:
PreDown = echo "$(date +%s) WireGuard Going Down" >> /var/log/wireguard.log呼叫WebHook:
PreDown = curl https://events.example.dev/wireguard/stopping/?key=abcdefg
⑪ PostDown
停止VPN 介面之後執行的命令。這個選項可以指定多次,依序執行。
例如:
新增一行日誌到文件中:
PostDown = echo "$(date +%s) WireGuard Going Down" >> /var/log/wireguard.log呼叫WebHook:
PostDown = curl https://events.example.dev/wireguard/stopping/?key=abcdefg刪除iptables 規則,關閉封包轉送:
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -D FORWARD -o %i -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
[Peer]
定義能夠為一個或多個位址路由流量的對等節點(peer)的VPN 設定。對等節點(peer)可以是將流量轉送到其他對等節點(peer)的中繼伺服器,也可以是透過公網或內部網路直連的客戶端。
中繼伺服器必須將所有的客戶端定義為對等節點(peer),除了中繼伺服器之外,其他客戶端都不能將位於NAT 後面的節點定義為對等節點(peer),因為路由不可達。對於那些只為自己路由流量的客戶端,只需將中繼伺服器作為對等節點(peer),以及其他需要直接存取的節點。
舉個例子,在下面的設定中,public-server1 作為中繼伺服器,其他的客戶端有的是直連,有的位於NAT 後面:
public-server1(中繼伺服器)[peer] : public-server2, home-server, laptop, phone public-server2(直連客戶端)[peer] : public-server1 home-server(客戶端位於NAT 後面)[peer] : public-server1, public-server2 laptop(客戶端位於NAT 後面)[peer] : public-server1, public-server2 phone(客戶端位於NAT 後面)[peer] : public-server1, public-server2
設定範例:
對等節點(peer)是路由可達的客戶端,只為自己路由流量
[Peer]
# Name = public-server2.example-vpn.dev
Endpoint = public-server2.example-vpn.dev:51820
PublicKey =
AllowedIPs = 192.0.2.2/32對等節點(peer)是位於NAT 後方的客戶端,只為自己路由流量
[Peer]
# Name = home-server.example-vpn.dev
Endpoint = home-server.example-vpn.dev:51820
PublicKey =
AllowedIPs = 192.0.2.3/32對等節點(peer)是中繼伺服器,用來將流量轉送到其他對等節點(peer)
[Peer]
# Name = public-server1.example-vpn.tld
Endpoint = public-server1.example-vpn.tld:51820
PublicKey =
# 路由整個VPN 子網路的流量
AllowedIPs = 192.0.2.1/24
PersistentKeepalive = 25
① Endpoint
指定遠端對等節點(peer)的公網位址。如果對等節點(peer)位於NAT 後面或沒有穩定的公網存取位址,就忽略這個欄位。通常只需要指定中繼伺服器的 Endpoint,當然有穩定公網IP 的節點也可以指定。例如:
透過IP 指定:
Endpoint = 123.124.125.126:51820透過網域指定:
Endpoint = public-server1.example-vpn.tld:51820
② AllowedIPs
允許該對等節點(peer)傳送過來的VPN 流量中的來源位址範圍。同時這個欄位也會作為本機路由表中wg0 綁定的IP 位址範圍。如果對等節點(peer)是常規的客戶端,則將其設定為節點本身的單一IP;如果對等節點(peer)是中繼伺服器,則將其設定為可路由的子網路範圍。可以使用 , 來指定多個IP 或子網路範圍。該字段也可以指定多次。
當決定如何對一個資料包進行路由時,系統首先會選擇最具體的路由,如果不匹配再選擇更廣泛的路由。例如,對於一個發送至 192.0.2.3 的資料包,系統首先會尋找位址為 192.0.2.3/32 的對等節點(peer),如果沒有再尋找位址為 192.0.2.3/32 的對等節點(peer),以此類推。
例如:
對等節點(peer)是常規客戶端,只路由自身的流量:
AllowedIPs = 192.0.2.3/32對等節點(peer)是中繼伺服器,可以將流量轉送到其他對等節點(peer):
AllowedIPs = 192.0.2.1/24對等節點(peer)是中繼伺服器,可以轉送所有的流量,包括外網流量和VPN 流量,可以用來幹嘛你懂得:
AllowedIPs = 0.0.0.0/0,::/0對等節點(peer)是中繼伺服器,可以路由其自身和其他對等節點(peer)的流量:
AllowedIPs = 192.0.2.3/32,192.0.2.4/32對等節點(peer)是中繼伺服器,可以路由其自身的流量和它所在的內部網路的流量:
AllowedIPs = 192.0.2.3/32,192.168.1.1/24
③ PublicKey
對等節點(peer)的公鑰,所有節點(包括中繼伺服器)都必須設定。可與其他對等節點(peer)共用同一個公鑰。
公鑰可透過指令 wg pubkey < example.key > example.key.pub 來生成,其中 example.key 是上面生成的私鑰。
例如:PublicKey = somePublicKeyAbcdAbcdAbcdAbcd=
④ PersistentKeepalive
如果連接是從一個位於NAT 後面的對等節點(peer)到一個公網可達的對等節點(peer),那麼NAT 後面的對等節點(peer)必須定期發送一個出站ping 包來檢查連通性,如果IP 有變化,就會自動更新Endpoint。
例如:
本機節點與對等節點(peer)可直連:此欄位不需要指定,因為不需要連線檢查。 對等節點(peer)位於NAT 後面:此欄位不需要指定,因為維持連線是客戶端(連線的發起方)的責任。 本機節點位於NAT 後面,對等節點(peer)公網可達:需指定該欄位 PersistentKeepalive = 25,表示每隔 25 秒發送一次ping 檢查連線。
3. 高階特性
IPv6
前面的範例主要使用 IPv4,WireGuard 也支援 IPv6。例如:
[Interface]
AllowedIps = 192.0.2.3/24, 2001:DB8::/64
[Peer]
...
AllowedIPs = 0.0.0.0/0, ::/0
轉送所有流量
如果你想透過VPN 轉送所有的流量,包括VPN 子網路和公網流量,需要在 [Peer] 的 AllowedIPs 中加入 0.0.0.0/0, ::/0。
即便只轉送 IPv4 流量,也要指定一個 IPv6 網段,以避免將 IPv6 封包外洩到VPN 之外。詳情請見:reddit.com/r/WireGuard/comments/b0m5g2/ipv6_leaks_psa_for_anyone_here_using_wireguard_to[5]
例如:
[Interface]
# Name = phone.example-vpn.dev
Address = 192.0.2.3/32
PrivateKey =
[Peer]
# Name = public-server1.example-vpn.dev
PublicKey =
Endpoint = public-server1.example-vpn.dev:51820
AllowedIPs = 0.0.0.0/0, ::/0
一般只有把VPN 當做武當縱雲梯來用時,才會需要轉送所有流量,不多說,點到為止。
NAT-to-NAT 連接
如果兩個對等節點(peer)都位於NAT 後面,想不透過中繼伺服器直接連接,需要確保至少有一個對等節點(peer)具有穩定的公網出口,使用靜態公網IP 或透過 DDNS 動態更新 FQDN 都可以。
WebRTC 協定可以動態配置兩個NAT 之間的連接,它可以透過訊號伺服器來偵測每個主機的 IP:Port 組合。而WireGuard 沒有這個功能,它沒有沒有信令伺服器來動態搜尋其他主機,只能硬編碼 Endpoint+ListenPort,並透過 PersistentKeepalive 來維持連線。
總結一下NAT-to-NAT 連接的前提條件:
至少有一個對等節點(peer)有固定的公網IP,如果都沒有固定的公網IP,也可以使用 DDNS 來維護一個穩定的網域。 至少有一個對等節點(peer)指定UDP ListenPort,而且它的NAT 路由器不能做UDP 來源連接埠隨機化,否則傳回的封包將被傳送到先前指定的 ListenPort,並被路由器丟棄,不會傳送到新分配的隨機連接埠。 所有的對等節點(peer)必須在 [Peer] 配置中啟用其他對等節點(peer)的 PersistentKeepalive,這樣就可以維持連接的持久性。
對於通信雙方來說,只要服務端所在的NAT 路由器沒有指定到NAT 後面的對等節點(peer)的轉送規則,就需要進行UDP 打洞。
UDP 打洞的原理:
Peer1 向 Peer2 發送一個UDP 封包,不過 Peer2 的NAT 路由器不知道該將這個包發給誰,直接丟棄了,不過沒關係,這一步的目的是讓 Peer1 的NAT 路由器能夠接收UDP 回應並轉發到後面的 Peer1。 Peer2 向 Peer1 發送UDP 封包,由於上一步驟的作用,Peer1 的NAT 路由器已經建立臨時轉送規則,可以接收UDP 回應,所以可以接收到該封包,並轉送到 Peer1。 Peer1 向 Peer2 發送UDP 回應,由於上一個步驟的作用,由於上一個步驟的作用,Peer2 的NAT 路由器已經可以接收UDP 回應,所以可以接收到該封包,並轉送到 Peer2。
這種發送初始的資料包被拒絕,然後利用路由器已建立的轉送規則來接收回應的過程稱為『UDP 打洞』。
當你發送一個UDP 封包出去時,路由器通常會創建一個臨時規則來映射來源位址/端口和目的地址/端口,反之亦然。從目的位址和連接埠返回的UDP 封包會被轉送到原來的來源位址和端口,這就是大多數UDP 應用在NAT 後面的運作方式(如BitTorrent、Skype 等)。這個臨時規則會在一段時間後失效,所以NAT 後面的客戶端必須透過 PersistentKeepalive 定期發送封包來維持連線的持久性。
當兩個對等節點(peer)都位於NAT 後面時,要想讓UDP 打洞生效,需要兩個節點在差不多的時間向對方發送資料包,這意味著雙方需要提前知道對方的公網位址和連接埠號,可以在 wg0.conf 中指定。
UDP 打洞的局限性
從2019 年開始,許多以前用過的老式打洞方法都不再有效了。以前很著名的就是 pwnat[6] 開創的一種新的打洞方法,它能夠在不需要代理、第三方伺服器、upnp、DMZ、sproofing、dns 轉換的情況下實現NAT 中的P2P 通訊。它的原理也很簡單:
透過讓客戶端假裝成為網路上任意的 ICMP 跳躍點( a random hop on the Internet)來解決這個問題,從而讓服務端能夠取得到客戶端的IP 位址。 traceroute 指令也是使用這項技術來偵測Internet 上的跳躍點。
具體來說,當伺服器啟動時,它開始向固定位址 3.3.3.3 發送固定的 ICMP 回應請求包(ICMP echo request packets)。顯然,我們無法從 3.3.3.3 收到返回的 ICMP 回應封包(ICMP echo packets)。然而,3.3.3.3 並不是我們可以存取的主機,我們也不是想偽裝成它來發送ICMP 回應封包。相反,pwnat 技術的實作原理在於,當我們的客戶端想要連接服務端時,客戶端(知道伺服器IP位址)就會傳送到服務端 ICMP 逾時資料包(ICMP Time Exceeded packet)。這個ICMP 封包裡麵包含了服務端發送到 3.3.3.3 的原始固定 ICMP 回應請求包。
為什麼要這樣做呢?好吧,我們假裝是互聯網上的一個ICMP 跳越點,禮貌地告訴服務器它原來的 ICMP 回應請求包無法傳遞到 3.3.3.3。而你的NAT 是個聰明的設備,它會注意到 ICMP 逾時資料包內的資料包與伺服器發出 ICMP 回應請求包相匹配。然後它將 ICMP 逾時資料包轉回NAT 後面的伺服器,包括來自客戶端的完整IP 封包頭,從而讓服務端知道客戶端IP 位址是什麼!
現在這種類似的UDP 打洞方法受到了很多的限制,詳情可以參考上篇文章,這裡不過多闡述。除了UDP 打洞之外,我們仍然可以使用硬編碼的方式指定兩個對等節點(peer)的公網位址和連接埠號,這個方法對大多數NAT 網路都有效。
來源埠隨機化
如果所有的對等節點(peer)都在具有嚴格的UDP 來源連接埠隨機化的NAT 後面(例如大多數蜂窩網路),那麼無法實現 NAT-to-NAT 連接。因為雙方都無法協商出一個 ListenPort,並保證自己的NAT 在發出ping 套件後能夠接收發送到該連接埠的流量,所以就無法初始化打洞,導致連線失敗。因此,一般在 LTE/3G 網路中無法進行p2p 通訊。
使用信令伺服器
上節提到了,如果所有的對等節點(peer)都在具有嚴格的UDP 來源連接埠隨機化的NAT 後面,就無法直接實現 NAT-to-NAT 連接,但透過第三方的信令伺服器是可以實現的。信令伺服器相當於一個中轉站,它會告訴通訊雙方關於對方的 IP:Port 資訊。這裡有幾個項目可以參考:
takutakahashi/wg-connect[7] git.zx2c4.com/wireguard-tools/tree/contrib/nat-hole-punching[8]
動態IP 位址
WireGuard 只會在啟動時解析域名,如果你使用 DDNS 來動態更新域名解析,那麼每當IP 發生變化時,就需要重新啟動WireGuard。目前建議的解決方案是使用 PostUp 鉤子每隔幾分鐘或幾小時重新啟動WireGuard 來強制解析網域名稱。
總的來說,NAT-to-NAT 連線極為不穩定,而且還有一堆其他的限制,所以還是建議透過中繼伺服器來通訊。
NAT-to-NAT 設定範例:
Peer1:
[Interface]
...
ListenPort = 12000
[Peer]
...
Endpoint = peer2.example-vpn.dev:12000
PersistentKeepalive = 25
Peer2:
[Interface]
...
ListenPort = 12000
[Peer]
...
Endpoint = peer1.example-vpn.dev:12000
PersistentKeepalive = 25
更多資料:
samyk/pwnat[9] en.wikipedia.org/wiki/UDP_hole_punching[10] stackoverflow.com/questions/8892142/udp-hole-punching-algorithm[11] stackoverflow.com/questions/12359502/udp-hole-punching-not-going-through-on-3g[12] stackoverflow.com/questions/11819349/udp-hole-punching-not-possible-with-mobile-provider[13] WireGuard/WireGuard@ master
/contrib/examples/nat-hole-punching[14]staaldraad.github.io/2017/04/17/nat-to-nat-with-wireguard[15] golb.hplar.ch/2019/01/expose-server-vpn.html[16]
動態分配子網路IP
這裡指的是對等節點(peer)的VPN 子網路IP 的動態分配,類似DHCP,不是指 Endpoint。
WireGuard 官方已經在開發動態分配子網IP 的功能,具體的實作可以看這裡:WireGuard/wg-dynamic[17]
當然,你也可以使用 PostUp 在執行時期從檔案中讀取IP 值來實作一個動態分配IP 的系統,類似Kubernetes 的CNI 外掛程式。例如:
[Interface]
...
PostUp = wg set %i allowed-ips /etc/wireguard/wg0.key <(some command)
實用技巧
共享一個peers.conf 文件
介紹一個秘密功能,可以簡化WireGuard 的設定工作。如果某個 peer 的公鑰與本機介面的私鑰能夠配對,那麼WireGuard 就會忽略該 peer。利用這個特性,我們可以在所有節點上共用同一個peer 列表,每個節點只需要單獨定義一個 [Interface] 就行了,即使列表中有本節點,也會被忽略。具體方式如下:
每個對等節點(peer)都有一個單獨的 /etc/wireguard/wg0.conf 文件,只包含 [Interface] 部分的配置。 每個對等節點(peer)共用同一個 /etc/wireguard/peers.conf 文件,其中包含了所有的peer。 Wg0.conf 檔案中需要設定一個PostUp 鉤子,內容為 PostUp = wg addconf /etc/wireguard/peers.conf。
關於 peers.conf 的共享方式有很多種,你可以透過 ansible 這樣的工具來分發,可以使用 Dropbox 之類的網盤來同步,當然也可以使用 ceph 這種分散式檔案系統來將其掛載到不同的節點上。
從檔案或命令輸出讀取配置
WireGuard 也可以從任意指令的輸出或檔案讀取內容來修改配置的值,利用這個特性可以方便管理金鑰,例如可以在執行時間從 Kubernetes Secrets 或 AWS KMS 等第三方服務讀取金鑰。
容器化
WireGuard 也可以跑在容器中,最簡單的方式是使用 --privileged 和 --cap-add=all 參數,讓容器可以載入核心模組。
你可以讓WireGuard 跑在容器中,向宿主機暴露一個網路介面;也可以讓WireGuard 運作在宿主機中,向特定的容器暴露一個介面。
下面給出一個具體的範例,本範例中的 vpn_test 容器透過WireGuard 中繼伺服器來路由所有流量。本範例中給出的容器配置是 docker-compose 的設定檔格式。
中繼伺服器容器配置:
version: '3'
services:
wireguard:
image: linuxserver/wireguard
ports:
- 51820:51820/udp
cap_add:
- NET_ADMIN
- SYS_MODULE
volumes:
- /lib/modules:/lib/modules
- ./wg0.conf:/config/wg0.conf:ro
中繼伺服器WireGuard 設定 wg0.conf:
[Interface]
# Name = relay1.wg.example.com
Address = 192.0.2.1/24
ListenPort = 51820
PrivateKey = oJpRt2Oq27vIB5/UVb7BRqCwad2YMReQgH5tlxz8YmI=
DNS = 1.1.1.1,8.8.8.8
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE; ip6tables -A FORWARD -i wg0 -j ACCEPT; ip6tables -t nat -A RADPOOUT -i wg0 -j ACCEPT; ip6tables -t nat -A RADPOSTRINGo ethethE
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE; ip6tables -D FORWARD -i wg0 -j ACCEPT; ip6tables -t nat -D FORPOOUT -i wg0 -j ACCEPT; ip6tables -t nat -D RAD0OUT -o wg0 -j ACCEPT; ip6tables -t nat -D.
[Peer]
# Name = peer1.wg.example.com
PublicKey = I+hXRAJOG/UE2IQvIHsou2zTgkUyPve2pzvHTnd/2Gg=
AllowedIPs = 192.0.2.2/32
客戶端容器配置:
version: '3'
services:
wireguard:
image: linuxserver/wireguard
cap_add:
- NET_ADMIN
- SYS_MODULE
volumes:
- /lib/modules:/lib/modules
- ./wg0.conf:/config/wg0.conf:ro
vpn_test:
image: curlimages/curl
entrypoint: curl -s http://whatismyip.akamai.com/
network_mode: 'service:wireguard'
客戶端WireGuard 設定 wg0.conf:
[Interface]
# Name = peer1.wg.example.com
Address = 192.0.2.2/32
PrivateKey = YCW76edD4W7nZrPbWZxPZhcs32CsBLIi1sEhsV/sgk8=
DNS = 1.1.1.1,8.8.8.8
[Peer]
# Name = relay1.wg.example.com
Endpoint = relay1.wg.example.com:51820
PublicKey = zJNKewtL3gcHdG62V3GaBkErFtapJWsAx+2um0c0B1s=
AllowedIPs = 192.0.2.1/24,0.0.0.0/0
PersistentKeepalive = 21