本篇介紹如何在 Ubuntu Linux 中以 WireGuard 架設 VPN 伺服器,並將 Windows、macOS 與 Linux 系統的電腦加入 VPN 網路中。
WireGuard 是一套非常簡潔且快速的虛擬私人網路(VPN)系統,效能優於傳統的 OpenVPN 與 IPsec,採用最先進的加密演算法,初期為 Linux 核心的功能,後來拓展到各種作業系統中(包含 Windows、macOS、BSD、iOS、Android)。
安裝 WireGuard
若在 Ubuntu Linux 中,可以使用 apt
安裝 WireGuard:
# 更新套件庫 sudo apt update # 安裝 wireguard 套件 sudo apt install wireguard
建立金鑰
安裝好 WireGuard 之後,接下來要建立 WireGuard 伺服器本身的公鑰與私鑰,私鑰可以使用 wg genkey
指令來產生,我們將產生出來的私鑰直接透過 tee
指令寫入檔案中保存,同時也將私鑰顯示在螢幕上:
# 產生私鑰
wg genkey | sudo tee /etc/wireguard/private.key
oLWgnNdR7CwFo9pakypprMwroGzGT9UQqQxRLGvogUE=
這一行以 Base64 編碼的文字就是 WireGuard 的私鑰,WireGuard 伺服器的私鑰是非常重要的機密資訊,不可以外洩,所以我們將私鑰的檔案權限設定為群組或其他人都不可以存取:
# 設定私鑰檔案權限 sudo chmod go= /etc/wireguard/private.key
有了私鑰之後,我們就可以使用 wg pubkey
指令以私鑰來產生公鑰,同樣使用 tee
將公鑰儲存至檔案並輸出至螢幕上:
# 產生公鑰
sudo cat /etc/wireguard/private.key | wg pubkey | sudo tee /etc/wireguard/public.key
YK0ljSkWMJgSN7pMqGHiJuDCOaBOIl+CeYaXoYN+RE4=
WireGuard 的公鑰同樣是一行以 Base64 編碼的字串,公鑰不需要保密,在設定 VPN 連線時,每個要與 WireGuard 伺服器連線的端點設備都必須知道 WireGuard 伺服器的公鑰。
選擇 IPv4 網段
如果我們的 VPN 要使用 IPv4 的位址,就會需要選擇一個私有的 IPv4 網段來使用,根據 RFC 1918 的規範,我們可以從以下幾個保留的 IPv4 網段中選擇一部分來使用:
10.0.0.0
至10.255.255.255
(10/8 開頭)172.16.0.0
至172.31.255.255
(172.16/12 開頭)192.168.0.0
至192.168.255.255
(192.168/16 開頭)
在選擇私有的網段時,只要不要跟自己系統上既有段衝突,要選擇哪一個網段都可以,這裡我們選用 10.8.0.0/24
這個 Class C 網段來作為 VPN 內部的網段,在這個網段中可以使用的 IPv4 位址有 254 個,也就是從 10.8.0.1/24
到 10.8.0.254/24
。
選擇 IPv6 網段
若 VPN 要使用 IPv6 的位址,依據 RFC 4193 的規範,我們可以在 fd00::/8
保留網段之中隨機選擇一個網段來使用,首先以時間戳記與機器 ID 產生 SHA1 檢查碼:
# 以時間戳記與機器 ID 產生 SHA1 檢查碼 printf `date +%s%N``cat /var/lib/dbus/machine-id` | sha1sum
427038505e487387c5fbd88746373dfcc8543c2d -
接著以這行 SHA1 檢查碼的最後 40 位元作為 Global ID,產生一組 IPv6 的私有網段,以這個例子來說,SHA1 檢查碼最後 40 位元就是 fcc8543c2d
,產生的 IPv6 的私有網段就是 fdfc:c854:3c2d::/64
,而在這個私有網段中,我們可以自由配發 IPv6 的位址,最簡單的配發方式就是依序配發,例如第一個 IPv6 位址就是 fdfc:c854:3c2d::1/64
,第二個 IPv6 位址就是 fdfc:c854:3c2d::2/64
,以此類推。
如果不想依據 RFC 的建議方式產生私有網段,也可以使用 Unique Local IPv6 Generator 這類的工具自動隨機產生。
設定 WireGuard 伺服器
Linux 系統中的 WireGuard 設定檔存放目錄為 /etc/wireguard/
,每一個網路介面會對應一個設定檔,而設定檔會以網路介面名稱加上 .conf
後綴來命名,例如 wg0
這個網路介面的設定檔就會是 /etc/wireguard/wg0.conf
。
網路介面的名稱可以自行指定(只要符合 [a-zA-Z0-9_=+.-]{1,15}
這個正規表示法即可),例如 wg0
、wgvpn0
、wgmgmt‐lan0
等都是常見的網路介面名稱,甚至想要使用地名來作為網路介面名稱也可以,例如 nyc
或 paris
等。
這裡我們打算建立一個 WireGuard 的網路介面 wg0
,先建立 /etc/wireguard/wg0.conf
這個設定檔,內容如下:
[Interface] # VPN 伺服器私鑰 PrivateKey = oLWgnNdR7CwFo9pakypprMwroGzGT9UQqQxRLGvogUE= # VPN 伺服器內部 IP 位址 Address = 10.8.0.1/24, fdfc:c854:3c2d::1/64 # 關閉 WireGuard 服務時,不要自動儲存當時的設定 SaveConfig = false # WireGuard 對外傾聽埠號 ListenPort = 51820
WireGuard 設定檔的格式為 INI,所有井字號(#
)之後的文字都會被視為註解。
在 WireGuard 的設定中,會包含一個 [Interface]
與一個或多個 [Peer]
設定區塊,[Interface]
是指定自己的網路介面設定,而 [Peer]
則是指定遠端主機的連線資訊,這裡我們只先撰寫 [Interface]
的設定,待後續新增其他主機的 VPN 連線時,會再加入 [Peer]
的部分。
在 [Interface]
設定區塊中,各設定的意義如下:
PrivateKey
:網路介面wg0
對應的私鑰,要指定為剛剛上面產生的 VPN 伺服器的私鑰。Address
:網路介面wg0
的 IP 位址,可以從剛剛上面選用的私有 IPv4 與 IPv6 網段中各選一個位址給這一台伺服器使用,如果只想使用 IPv4 或 IPv6,也可以只指定一個 IP 位址。SaveConfig
:當 WireGuard 服務關閉時,是否自動儲存當時的設定。這裡我們希望統一以設定檔的內容為準,不要讓 WireGuard 自動覆蓋設定檔的內容,這樣還可以保留設定檔內的註解,方便管理。ListenPort
:WireGuard 對外傾聽埠號。
WireGuard 伺服器 IP 轉發與偽裝設定
WireGuard 在預設的狀況下只會允許私有網段內部的相互通訊,也就是說任何人連上 WireGuard 的私有網路之後,只能透過 VPN 存取私有網路中的伺服器,如果希望連上 WireGuard 伺服器之後,可以透過 WireGuard 伺服器再連到外界,就要在 WireGuard 伺服器上加入 IP 轉發(IP forwarding)的設定。
事實上 IP 轉發並不是 WireGuard 的功能,而是 Linux 系統本身的設定,我們可以在 /etc/sysctl.conf
設定檔中加入以下兩行設定,分別開啟 Linux 系統中 IPv4 與 IPv6 的 IP 轉發功能:
# 啟用 IPv4 封包轉發 net.ipv4.ip_forward=1 # 啟用 IPv6 封包轉發 net.ipv6.conf.all.forwarding=1
修改好之後,執行 sysctl
載入新的設定:
# 載入 sysctl 設定 sudo sysctl -p
net.ipv4.ip_forward = 1 net.ipv6.conf.all.forwarding = 1
設定好 IP 轉發功能之後,還要配合防火牆的 IP 偽裝(masquerading),才能夠達到 NAT 的功能。而在設定 IP 偽裝功能之前,要先確認一下目前系統上的路由設定:
# 查詢預設路由設定
ip route list default
default via 203.0.113.1 dev eth0 proto static
從這個路由設定來看,我們可以知道預設的閘道位於 eth0
這個介面上,接下來我們就要新增 wg0
到 eth0
網路介面的 IP 轉發與偽裝的防火牆設定,在 /etc/wireguard/wg0.conf
設定檔的 [Interface]
區塊中,加入以下內容:
[Interface] # 其他設定 ... # 新增防火牆允許 IP 轉發的規則 PostUp = ufw route allow in on wg0 out on eth0 # 啟用 IP 偽裝 PostUp = iptables -t nat -I POSTROUTING -o eth0 -j MASQUERADE PostUp = ip6tables -t nat -I POSTROUTING -o eth0 -j MASQUERADE # 刪除防火牆允許 IP 轉發的規則 PreDown = ufw route delete allow in on wg0 out on eth0 # 停用 IP 偽裝 PreDown = iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE PreDown = ip6tables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
這裡的 PostUp
所指定的指令會在 WireGuard 伺服器啟用 wg0
網路介面之後,以 bash
來執行,如果有多行指令則會依照指定順序依序執行。PostUp
所指定的第一行指令是以 ufw
新增一條防火牆規則,讓防火牆允許 IP 轉發的封包通過,而第二行與第三行則是設定 IPv4 與 IPv6 的 IP 偽裝,將內部的 IP 位址轉為外部的 IP 位址。
PreDown
所指定的指令則是會在 wg0
介面停用之前依序執行,而指令的內容就是將 PostUp
新增的防火牆設定刪除。
這裡我們可以依據自己的 VPN 網路設定來篩選要加入的指令,如果我們的 VPN 網路只有使用到 IPv4 的位址,那就只需要執行 iptables
的部分,而 ip6tables
的部分就可以省略。
WireGuard 伺服器對外防火牆設定
這裡我們的 WireGuard 伺服器使用的連接埠是 UDP 的 51820
(WireGuard 預設的連接埠也是這個),所以若要讓外部的 VPN client 可以連線,就要開啟 UDP 的 51820
連接埠:
# 讓防火牆開啟 UDP 的 51820 連接埠
sudo ufw allow 51820/udp
Ubuntu Linux 防火牆的啟用、停用、狀態查詢,可以使用以下指令:
# 啟用防火牆 sudo ufw enable # 停用防火牆 sudo ufw disable # 查詢防火牆狀態 sudo ufw status
通常伺服器都會開啟 SSH 服務,所以在啟用防火牆時要記得開啟對應的防火牆設定,避免自己被擋在防火牆之外:
# 讓防火牆開啟 UDP 的 51820 連接埠
sudo ufw allow OpenSSH
啟動 WireGuard 伺服器
WireGuard 可以使用 systemd
搭配內建的 wg-quick
指令工具來自動管理各 WireGuard 網路介面的啟用與停用,而 WireGuard 網路介面的服務在 systemd
中對應的服務名稱為 wg-quick@網路介面名稱
,例如 wg0
網路介面對應的服務名稱就會是 wg-quick@wg0
,其餘的操作指令都跟一般的服務相同:
# 啟用 WireGuard 的 wg0 網路介面服務 sudo systemctl enable wg-quick@wg0 # 啟動 WireGuard 的 wg0 網路介面服務 sudo systemctl start wg-quick@wg0 # 查詢 WireGuard 的 wg0 網路介面服務狀態 sudo systemctl stop wg-quick@wg0 # 停用 WireGuard 的 wg0 網路介面服務 sudo systemctl stop wg-quick@wg0
透過這樣的方式,我們就可以使用 systemd
來分開管理多個 WireGuard 網路介面。
除了以 systemd
進行自動化管理之外,我們也可以使用 wg-quick
指令來手動啟動或停止指定的網路介面:
# 啟用 WireGuard 的 wg0 網路介面 wg-quick up wg0 # 停用 WireGuard 的 wg0 網路介面 wg-quick down wg0
但是 systemd
自動化管理與手動的 wg-quick
操作是不可以混用的,所以一般來說建議統一都使用 systemd
來管理 WireGurad 網路介面會比較單純。
新增 WireGuard 端點(Peer)
在 WireGuard 伺服器建立好並啟動之後,就可以在端點的電腦上安裝與設定 WireGuard 了。
Windows 安裝與設定 WireGuard 端點
從 WireGuard 的官方網站上下載 WireGuard 的 Windows 版本,安裝之後開啟 WireGuard,點選左下角的「新增隧道精靈」。
新增一個新的隧道時,會自動產生一組公鑰與私鑰,公鑰會顯示在上方的「公鑰」欄位中,而私鑰會自動寫在 [Interface]
區塊中的 PrivateKey
欄位,至於設定檔中其他的資訊則是要自行加入:
[Interface] PrivateKey = 8PkcFvBufFT/yPx+DB2lktJfkket2yfxmOvvcISq9WA= Address = 10.8.0.2/24, fdfc:c854:3c2d::2/64 [Peer] PublicKey = YK0ljSkWMJgSN7pMqGHiJuDCOaBOIl+CeYaXoYN+RE4= AllowedIPs = 10.8.0.0/24, fdfc:c854:3c2d::/64 Endpoint = ???.???.222.17:51820
這裡我們要新增的設定欄位與說明如下:
[Interface]
區塊-
Address
:這一台 VPN 端點所要使用的內部 IP 位址,可以從 VPN 內部的網段中任意選擇一個未使用的 IP 位址來使用。
[Peer]
區塊-
PublicKey
:VPN 伺服器的公鑰。AllowedIPs
:要透過 VPN 進行連線的 IP 網段,如果只需要透過 VPN 連線到 VPN 內部的的主機,可以指定為 VPN 的內部網段。如果希望所有的網路連線都透過 VPN 再連出去,可以設定為0.0.0.0/0
(IPv4)與::/0
(IPv6)搭配 VPN 伺服器的 IP 轉發與偽裝,這樣就會讓所有的對外連線都受到 VPN 保護。Endpoint
:VPN 伺服器的公開 IP 位址與 WireGuard 傾聽的連接埠。
將這份設定檔填寫完成之後,接著要將這裡的 VPN 端點公鑰與選用的內部 IP 位址寫入 WireGuard 伺服器的設定檔中,這樣 WireGuard 才會允許這個新的 VPN 端點建立連線。
macOS 安裝與設定 WireGuard 端點
從 WireGuard 的官方網站上下載 WireGuard 的 macOS 版本,安裝之後開啟 WireGuard,點選左下角的「Add Empty Tunnel」。
新增一個新的 Tunnel 時,會自動產生一組公鑰與私鑰,公鑰會顯示在上方的「公開金鑰」中,而私鑰會自動寫在 [Interface]
區塊中的 PrivateKey
欄位,至於設定檔中其他的資訊則是要自行加入:
[Interface] PrivateKey = aM6Aqe5DmaLNGHNzm7RzR/q+pzIZ8hc2nnyuaJOuUEw= Address = 10.8.0.3/24, fdfc:c854:3c2d::3/64 [Peer] PublicKey = YK0ljSkWMJgSN7pMqGHiJuDCOaBOIl+CeYaXoYN+RE4= AllowedIPs = 10.8.0.0/24, fdfc:c854:3c2d::/64 Endpoint = ???.???.222.17:51820
在不同的作業系統中 WireGuard 的端點設定方式都相同,關於各欄位的意義請參考上面 Windows 端點的說明。
Linux 安裝與設定 WireGuard 端點
Linux 系統的 WireGuard 端點安裝與設定的方式與伺服器方常相似,同樣都是安裝 wireguard
套件,並產生工鑰與私鑰:
# 更新套件庫 sudo apt update # 安裝 wireguard 套件 sudo apt install wireguard # 產生私鑰 wg genkey | sudo tee /etc/wireguard/private.key # 設定私鑰檔案權限 sudo chmod go= /etc/wireguard/private.key # 產生公鑰 sudo cat /etc/wireguard/private.key | wg pubkey | sudo tee /etc/wireguard/public.key
接著建立 /etc/wireguard/wg0.conf
設定檔,內容如下:
[Interface] PrivateKey = UDD+THJZ+K0u+JehwhwQlp87wXO9aKrum/VlFWaX1mY= Address = 10.8.0.4/24, fdfc:c854:3c2d::4/64 [Peer] PublicKey = YK0ljSkWMJgSN7pMqGHiJuDCOaBOIl+CeYaXoYN+RE4= AllowedIPs = 10.8.0.0/24, fdfc:c854:3c2d::/64 Endpoint = ???.???.222.17:51820
在不同的作業系統中 WireGuard 的端點設定方式都相同,關於各欄位的意義請參考上面 Windows 端點的說明。
設定完成之後,我們可以選擇使用 systemd
來管理 WireGuard 網路介面:
# 啟用 WireGuard 的 wg0 網路介面服務 sudo systemctl enable wg-quick@wg0 # 啟動 WireGuard 的 wg0 網路介面服務 sudo systemctl start wg-quick@wg0 # 查詢 WireGuard 的 wg0 網路介面服務狀態 sudo systemctl stop wg-quick@wg0 # 停用 WireGuard 的 wg0 網路介面服務 sudo systemctl stop wg-quick@wg0
或是使用 wg-quick
指令來手動啟動或停止指定的網路介面:
# 啟用 WireGuard 的 wg0 網路介面 wg-quick up wg0 # 停用 WireGuard 的 wg0 網路介面 wg-quick down wg0
這部分的操作也跟 WireGuard 伺服器完全相同。
將端點資訊匯入 WireGuard 伺服器
在設定完 VPN 端點的 WireGuard 之後,要將 VPN 端點的公鑰與端點所選用的內部 IP 位址寫入 WireGuard 伺服器的設定檔中,這樣 WireGuard 才會允許這個新的 VPN 端點建立連線。
開啟 WireGuard 伺服器上的 /etc/wireguard/wg0.conf
設定檔,在設定檔的結尾處新增一個 [Peer]
設定區塊,內容如下:
[Peer] PublicKey = 5ErLhgGezDGPVfVHA2r2FT3Fzfu+jCHt3PQhSyWI9G4= AllowedIPs = 10.8.0.3/32, fdfc:c854:3c2d::3/128
其中 PublicKey
要對應到端點所產生的公開金鑰,而 AllowedIPs
則是端點所選用的內部 IP 位址。修改完之後,讓 WireGuard 重新載入設定檔:
# 讓 WireGuard 重新載入設定檔
sudo systemctl reload wg-quick@wg0
當 WireGuard 伺服器匯入了端點的 [Peer]
設定,並載入生效之後,就可以開始建立並使用 VPN 網路連線了。
另一種在伺服器上手動新增 WireGuard 端點 [Peer]
設定的方式是使用 wg
指令:
# 在 VPN 伺服器上新增 WireGuard 端點
sudo wg set wg0 peer 5ErLhgGezDGPVfVHA2r2FT3Fzfu+jCHt3PQhSyWI9G4= allowed-ips 10.8.0.3,fdfc:c854:3c2d::3
執行這行指令之後,會直接把端點的資訊套用至目前正在執行中的 WireGuard 網路介面中,但是不會將設定寫入設定檔,我們可以使用 wg
指令查看目前所套用的設定內容:
# 顯示目前 wg0 網路介面的設定 sudo wg show wg0 # 顯示目前 wg0 網路介面的設定(以設定檔格式輸出) sudo wg showconf wg0