Linux

Ubuntu Linux 以 WireGuard 架設 VPN 伺服器教學與範例

本篇介紹如何在 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.010.255.255.255(10/8 開頭)
  • 172.16.0.0172.31.255.255(172.16/12 開頭)
  • 192.168.0.0192.168.255.255(192.168/16 開頭)

在選擇私有的網段時,只要不要跟自己系統上既有段衝突,要選擇哪一個網段都可以,這裡我們選用 10.8.0.0/24 這個 Class C 網段來作為 VPN 內部的網段,在這個網段中可以使用的 IPv4 位址有 254 個,也就是從 10.8.0.1/2410.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} 這個正規表示法即可),例如 wg0wgvpn0wgmgmt‐lan0 等都是常見的網路介面名稱,甚至想要使用地名來作為網路介面名稱也可以,例如 nycparis 等。

這裡我們打算建立一個 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 這個介面上,接下來我們就要新增 wg0eth0 網路介面的 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,點選左下角的「新增隧道精靈」。

新增 WireGuard 隧道

新增一個新的隧道時,會自動產生一組公鑰與私鑰,公鑰會顯示在上方的「公鑰」欄位中,而私鑰會自動寫在 [Interface] 區塊中的 PrivateKey 欄位,至於設定檔中其他的資訊則是要自行加入:

WireGuard 端點設定
[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」。

新增 WireGuard Tunnel

新增一個新的 Tunnel 時,會自動產生一組公鑰與私鑰,公鑰會顯示在上方的「公開金鑰」中,而私鑰會自動寫在 [Interface] 區塊中的 PrivateKey 欄位,至於設定檔中其他的資訊則是要自行加入:

WireGuard 端點設定
[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

參考資料

Share
Published by
Office Guide
Tags: 資訊安全

Recent Posts

Python 使用 PyAutoGUI 自動操作滑鼠與鍵盤

本篇介紹如何在 Python ...

1 年 ago

Linux 網路設定 ip 指令用法教學與範例

本篇介紹如何在 Linux 系...

1 年 ago

Linux 以 Cryptsetup、LUKS 加密 USB 隨身碟教學與範例

介紹如何在 Linux 系統中...

1 年 ago

Linux 以 Cryptsetup 與 LUKS 加密磁碟教學與範例

介紹如何使用 Cryptset...

1 年 ago