SKILL.md
唯讀
名稱
homelab-wireguard-vpn
描述
WireGuard VPN 伺服器建置、對等點(Peer)設定、金鑰生成、分流通道(Split Tunneling)與全通道(Full Tunnel)路由設定,以及從手機與筆記型電腦等用戶端遠端存取家庭網路。
Homelab WireGuard VPN
WireGuard 是快速且現代化的 VPN 協定。它是遠端存取家庭網路的最佳選擇——比 OpenVPN 更容易設定,且速度比大多數替代方案更快。
所有設定範例均展示了常見的配置方式。在將命令套用到您的系統之前,請仔細檢查每條命令——特別是 iptables 轉發規則和金鑰檔案權限——並建議在維護時間區段內進行變更。
使用時機
- 在 Raspberry Pi、Linux 主機、pfSense 或路由器上建置 WireGuard 伺服器
- 生成 WireGuard 金鑰對並撰寫對等點(Peer)設定檔
- 設定手機或筆記型電腦遠端存取家庭網路
- 解釋分流通道(Split Tunneling,僅路由家庭網路流量)與全通道(Full Tunnel,路由所有流量)的差異
- 排除無法連線的 WireGuard 連線故障
- 自動化多個用戶端的對等點設定檔生成
WireGuard 運作原理
您的手機(WireGuard 用戶端)
│
│ 加密 UDP 通道(連接埠 51820)
│
您的家用路由器(WireGuard 伺服器 — 需要實體公用 IP 或 DDNS)
│
您的家庭網路(192.168.1.0/24、NAS、Pi 等)
每台設備都有一組金鑰對(公鑰 + 私鑰)。
伺服器知道每個用戶端的公鑰。
用戶端 know 伺服器的公鑰與端點(IP:Port)。
流量進行端到端加密,無需中央伺服器或憑證簽發機構(CA)。
伺服器建置 (Linux)
# 安裝 WireGuard
sudo apt update && sudo apt install wireguard -y
# 生成伺服器金鑰對 — 從一開始就建立具有私密權限的檔案
sudo mkdir -p /etc/wireguard
sudo sh -c 'umask 077; wg genkey > /etc/wireguard/server_private.key'
sudo sh -c 'wg pubkey < /etc/wireguard/server_private.key > /etc/wireguard/server_public.key'
# 撰寫伺服器設定檔 — 替換為實際的私鑰數值
# 請勿將私鑰儲存在版本控制中或進行分享
sudo tee /etc/wireguard/wg0.conf << 'EOF'
[Interface]
Address = 10.8.0.1/24 # VPN 子網路 — 伺服器取得 .1
ListenPort = 51820
PrivateKey = <paste_server_private_key_here>
# 嚴格限定範圍的轉發規則:僅允許 VPN 流量進出,而非通用的 FORWARD ACCEPT
PostUp = iptables -A FORWARD -i wg0 -o eth0 -j ACCEPT
PostUp = iptables -A FORWARD -i eth0 -o wg0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
PostUp = iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i wg0 -o eth0 -j ACCEPT
PostDown = iptables -D FORWARD -i eth0 -o wg0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
PostDown = iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
[Peer]
# 手機 — 替換為實際的手機公鑰
PublicKey = <phone_public_key>
AllowedIPs = 10.8.0.2/32
[Peer]
# 筆記型電腦 — 替換為實際的筆記型電腦公鑰
PublicKey = <laptop_public_key>
AllowedIPs = 10.8.0.3/32
EOF
sudo chmod 600 /etc/wireguard/wg0.conf
# 將 eth0 替換為您實際的外網介面名稱
# 可使用此命令查詢:ip route show default
# 啟用 IP 轉發(透過伺服器路由流量所需)
echo "net.ipv4.ip_forward=1" | sudo tee /etc/sysctl.d/99-wireguard.conf
sudo sysctl --system
# 啟動 WireGuard 並設定開機自動啟動
sudo wg-quick up wg0
sudo systemctl enable wg-quick@wg0
用戶端設定
# 為每個用戶端設備生成唯一的金鑰對
# 在用戶端執行,或在伺服器端執行後安全傳輸私鑰 — 切勿以明文傳輸
umask 077
wg genkey | tee phone_private.key | wg pubkey > phone_public.key
# 用戶端設定檔 (phone_wg0.conf):
[Interface]
PrivateKey = <phone_private_key>
Address = 10.8.0.2/32
DNS = 192.168.1.2 # 可選:經由通道使用 Pi-hole 進行 DNS 解析
[Peer]
PublicKey = <server_public_key>
Endpoint = your-home-ip.ddns.net:51820 # 您的公用 IP 或 DDNS 主機名稱
AllowedIPs = 192.168.1.0/24 # 分流通道:僅路由家庭網路流量
# AllowedIPs = 0.0.0.0/0, ::/0 # 全通道:所有流量均通過 VPN
PersistentKeepalive = 25 # 維持 NAT 連線管道(行動用戶端必需)
分流通道 vs 全通道
# 分流通道:AllowedIPs = 192.168.1.0/24
只有發往家庭網路的流量會通過 VPN。
一般網際網路流量(YouTube、Spotify)則直接連線 — 在行動裝置上效能較佳。
最適合:「我只想從任何地方連回家裡的 NAS 和 Pi。」
# 全通道:AllowedIPs = 0.0.0.0/0, ::/0
所有流量都會通過您家中的網路連線。
適用於:借用家中的 DNS/Pi-hole 廣告攔截。
缺點:家中的上傳速度會成為您在任何地方的效能瓶頸。
# 多子網路分流通道(最常見的 Homelab 使用情境):
AllowedIPs = 192.168.10.0/24, 192.168.20.0/24, 192.168.30.0/24, 10.8.0.0/24
將所有 VLAN 流量經由通道路由;一般網際網路連線仍保持直連。
金鑰生成與對等點管理
import subprocess
def generate_keypair() -> tuple[str, str]:
"""生成 WireGuard 金鑰對。傳回 (private_key, public_key)。"""
private = subprocess.check_output(["wg", "genkey"]).decode().strip()
public = subprocess.run(
["wg", "pubkey"], input=private.encode(), capture_output=True
).stdout.decode().strip()
return private, public
def generate_preshared_key() -> str:
return subprocess.check_output(["wg", "genpsk"]).decode().strip()
def build_client_config(
client_private_key: str,
client_vpn_ip: str, # 例如 "10.8.0.3"
server_public_key: str,
server_endpoint: str, # 例如 "home.example.com:51820"
allowed_ips: str = "192.168.1.0/24",
dns: str = "",
) -> str:
dns_line = f"DNS = {dns}\n" if dns else ""
return f"""[Interface]
PrivateKey = {client_private_key}
Address = {client_vpn_ip}/32
{dns_line}
[Peer]
PublicKey = {server_public_key}
Endpoint = {server_endpoint}
AllowedIPs = {allowed_ips}
PersistentKeepalive = 25
"""
def build_server_peer_block(
client_public_key: str,
client_vpn_ip: str,
comment: str = "",
) -> str:
comment_line = f"# {comment}\n" if comment else ""
return f"""
{comment_line}[Peer]
PublicKey = {client_public_key}
AllowedIPs = {client_vpn_ip}/32
"""
請切勿將私鑰納入版本控制。如果您使用此指令碼,請將金鑰寫入權限設為 600 的檔案中,絕不記錄或印出金鑰。
pfSense / OPNsense WireGuard
# pfSense: VPN → WireGuard → Add Tunnel
Interface Keys: Generate(自動生成金鑰對)
Listen Port: 51820
Interface Address: 10.8.0.1/24
# 新增對等點 Add Peer(每個用戶端一個):
Public Key: <client public key>
Allowed IPs: 10.8.0.2/32
# 指派 WireGuard 介面:
Interfaces → Assignments → Add(選擇 wg0)
啟用介面,無需設定 IP(已在 Tunnel 設定中指定)
# 防火牆規則:
WAN → 允許 UDP port 51820 入站(讓用戶端可以連線至伺服器)
WireGuard 介面 → 允許流量通往您希望可存取的 LAN 網路
家庭伺服器的 DDNS (Dynamic DNS)
大多數家庭網際網路連線都使用動態 IP。使用 DDNS 可確保您的 VPN 端點在 IP 變更後仍可正常連線。
# 選項 1:Cloudflare DDNS — 將憑證儲存在 secrets 檔案中,而非內嵌在設定檔
# 使用 env 檔案的 docker-compose 設定項目:
ddns-updater:
image: qmcgaw/ddns-updater
env_file: ./ddns.env # 在此處儲存 zone_id 和 token,而非放在 compose 中
restart: unless-stopped
# ddns.env(chmod 600,請勿提交至 git):
# SETTINGS_CLOUDFLARE_ZONE_ID=your_zone_id
# SETTINGS_CLOUDFLARE_TOKEN=your_api_token
# 選項 2:DuckDNS(免費且簡單)
在 duckdns.org 註冊 → 取得 token 和子網域(myhome.duckdns.org)
將 token 儲存在 /etc/ddns.env(權限 600),然後使用由 root 擁有的輕量指令碼:
# /usr/local/bin/update-duckdns
#!/bin/sh
set -eu
. /etc/ddns.env
curl --fail --silent --show-error --max-time 10 \
--get "https://www.duckdns.org/update" \
--data-urlencode "domains=myhome" \
--data-urlencode "token=${DUCKDNS_TOKEN}" \
--data-urlencode "ip="
# Cron 排程作業:
*/5 * * * * /usr/local/bin/update-duckdns >/dev/null 2>&1
疑難排解
# 檢查 WireGuard 狀態與上次握手(Handshake)時間
sudo wg show
# 若 "latest handshake" 顯示為 never 或時間很久以前,代表通道未連線。
# 請檢查:
# 1. 路由器/防火牆上的 UDP port 51820 是否已開啟?
sudo ufw status # 或檢查 pfSense/UniFi 防火牆規則
# 2. 用戶端設定檔中的伺服器公鑰是否正確?
sudo wg show wg0 public-key # 與用戶端設定檔中的內容進行比對
# 3. 伺服器上的 IP 轉發功能是否已啟用?
cat /proc/sys/net/ipv4/ip_forward # 數值應為 1
# 4. 用戶端的 AllowedIPs 是否涵蓋您嘗試存取的 IP?
# 若 AllowedIPs = 192.168.1.0/24,而您嘗試存取 192.168.3.5,則無法順利路由。
# 檢查核心紀錄(kernel log)中的 WireGuard 錯誤訊息
dmesg | grep wireguard
# 重啟 WireGuard
sudo wg-quick down wg0 && sudo wg-quick up wg0
反模式
# 錯誤做法:將私鑰儲存在版本控制中或進行分享
# 私鑰等同於密碼 — 切勿提交至 git
# 錯誤做法:在行動裝置上使用 AllowedIPs = 0.0.0.0/0 卻未考量影響
# 全通道會將行動裝置的所有流量導向家中的上傳頻寬 — 通常速度較慢
# 錯誤做法:未在行動用戶端上設定 PersistentKeepalive
# 位於 NAT 後方的行動用戶端若未設定此項,閒置的通道會中斷連線
# 錯誤做法:在防火牆開啟 Port 51820,卻忘記在伺服器上啟用 IP 轉發
# 通道看似已連線但無法路由流量 — 除錯時容易令人困惑
# 錯誤做法:在多個用戶端設備之間共用同一組金鑰對
# 每台設備都必須擁有唯一的金鑰對 — 共用金鑰會破壞安全模型
# 錯誤做法:使用過於寬鬆的 "FORWARD ACCEPT" iptables 規則
# 應將轉發規則嚴格限定在 wg0 介面與指定方向
最佳實踐
- 為每個用戶端設備生成唯一的金鑰對 — 切勿重複使用金鑰
- 在行動裝置上使用分流通道(
AllowedIPs = <家庭網路子網路>) - 在所有行動用戶端上設定
PersistentKeepalive = 25 - 若 ISP 分配動態 IP,請使用 DDNS;將憑證儲存在環境變數檔案中,而非內嵌於設定檔
- 使用限定範圍的 iptables 轉發規則(僅限 wg0 入站),而非全域的 FORWARD ACCEPT
- 在用戶端設定檔中添加 Pi-hole IP 作為
DNS =,以透過 VPN 享有廣告攔截功能 - 定期輪替伺服器金鑰對並更新所有用戶端設定檔
相關 Skills
- homelab-network-setup
- homelab-vlan-segmentation
- homelab-pihole-dns




