SKILL.md
只读
名称
homelab-wireguard-vpn
描述
WireGuard VPN 服务端搭建、Peer 节点配置、密钥对生成、分流(Split Tunneling)与全局(Full Tunnel)路由策略,以及通过手机和电脑客户端远程访问 Home Lab / 家庭局域网的完整指南。
Home Lab WireGuard VPN 搭建指南
WireGuard 是一种高速、现代的 VPN 协议。相比传统的 OpenVPN 配置更简单,性能也优于绝大多数替代方案,是远程访问家庭局域网的绝佳选择。
本文中的配置示例均基于常见场景。在将命令应用到生产系统前,请务必仔细检查每一条命令(特别是 iptables 转发规则与密钥文件的访问权限),建议在维护窗口期内进行操作。
使用场景
- 在树莓派、Linux 主机、pfSense 或路由器上搭建 WireGuard 服务端
- 生成 WireGuard 密钥对并编写 Peer 节点配置文件
- 配置手机或电脑等客户端远程安全访问家庭局域网
- 理解分流模式(仅路由家庭网段流量)与全局模式(路由所有网络流量)的差异
- 排查 WireGuard 连接失败或无法通包的问题
- 批量自动化生成多客户端 Peer 配置文件
WireGuard 架构与工作原理
你的手机(WireGuard 客户端)
│
│ 加密 UDP 隧道(默认端口 51820)
│
家庭路由器 / 主机(WireGuard 服务端 — 需要公网 IP 或 DDNS)
│
家庭局域网(192.168.1.0/24,NAS、树莓派等设备)
每个设备都拥有各自的密钥对(公钥 + 私钥)。
服务端已知各个客户端的公钥。
客户端已知服务端的公钥 + Endpoint 接入点(IP:端口)。
流量实现端到端加密,无需中央服务器或 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 = <在这里粘贴服务端私钥>
# 精细化转发规则:仅允许 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 = <手机端公钥>
AllowedIPs = 10.8.0.2/32
[Peer]
# 笔记本电脑节点 — 替换为笔记本端实际生成的公钥
PublicKey = <笔记本端公钥>
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 = <手机端私钥>
Address = 10.8.0.2/32
DNS = 192.168.1.2 # 可选:配置通过隧道使用 Pi-hole 等自建 DNS
[Peer]
PublicKey = <服务端公钥>
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 全局模式
# 分流模式(Split Tunnel):AllowedIPs = 192.168.1.0/24
只有发往家庭局域网的流量才走 VPN 隧道。
普通上网流量(哔哩哔哩、YouTube 等)直连出去 — 移动设备体验更好、速度更快。
适用场景:"我只想随时随地访问家里的 NAS 和树莓派。"
# 全局模式(Full Tunnel):AllowedIPs = 0.0.0.0/0, ::/0
所有网络流量全部走家里的公网出口进行路由。
适用场景:借用家里的 DNS 进行广告过滤(如 Pi-hole)或在外防公用 WiFi 抓包。
缺点:家庭宽带的上行带宽会成为你在外上网的瓶颈。
# 多子网分流模式(Home Lab 最常见组合):
AllowedIPs = 192.168.10.0/24, 192.168.20.0/24, 192.168.30.0/24, 10.8.0.0/24
将所有 VLAN 子网流量打通走隧道,公网上网流量依然保持本地直连。
密钥生成与 Peer 节点自动化管理
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:
"""生成预共享密钥(PSK)。"""
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
# 添加 Peer 节点(每个客户端添加一个):
Public Key: <客户端公钥>
Allowed IPs: 10.8.0.2/32
# 绑定 WireGuard 接口:
Interfaces → Assignments → Add(选择 wg0)
启用接口,无需配置 IP(隧道配置中已定义)
# 防火墙规则(Firewall Rules):
WAN 接口 → 允许 UDP 端口 51820 入站(确保客户端能连上服务端)
WireGuard 接口 → 放行访问目标 LAN 局域网网段的流量
家庭服务器 DDNS(动态域名解析)
大多数家庭宽带分配的都是动态公网 IP。使用 DDNS 可以保证在 IP 变更后,VPN 依然能稳定连通。
# 方案 1:Cloudflare DDNS — 凭据应保存在环境变量文件内,切勿硬编码
# docker-compose.yml 示例(引入 env 文件):
ddns-updater:
image: qmcgaw/ddns-updater
env_file: ./ddns.env # zone_id 和 token 保存在这里,不要放进 compose
restart: unless-stopped
# ddns.env (设置权限 chmod 600,且添加至 .gitignore):
# 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 状态及最后一次握手时间
sudo wg show
# 若 "latest handshake" 显示从未握手或时间很早,说明隧道未成功建立。
# 依次检查:
# 1. 路由器 / 防火墙是否放行了 UDP 51820 端口?
sudo ufw status # 或检查 pfSense / UniFi 等防火墙规则
# 2. 客户端配置里的服务端公钥(server public key)填对了吗?
sudo wg show wg0 public-key # 打印出来的公钥比对客户端配置文件
# 3. 服务端是否开启了 IP 转发(IP forwarding)?
cat /proc/sys/net/ipv4/ip_forward # 输出必须为 1
# 4. 客户端的 AllowedIPs 是否覆盖了你试图访问的 IP 段?
# 例如:AllowedIPs = 192.168.1.0/24,但你却在尝试 Ping 192.168.3.5,流量就不会走隧道。
# 查看内核日志获取 WireGuard 报错
dmesg | grep wireguard
# 重启 WireGuard 服务
sudo wg-quick down wg0 && sudo wg-quick up wg0
避坑指南 / 反模式 (Anti-Patterns)
# 错误操作:把私钥放进代码仓库或直接发给他人
# 私钥就等同于密码 — 绝对不能提交到 Git 仓库
# 错误操作:在手机端无脑开 AllowedIPs = 0.0.0.0/0
# 全局模式会把你手机的所有流量都强行绕回家里的宽带出口,往往会导致上网极慢
# 错误操作:手机端不配置 PersistentKeepalive
# 在 NAT 后的移动设备在空闲时会掉线断连
# 错误操作:防火墙放行了 51820 端口,但服务端忘记开 IP 转发
# 结果就是隧道能连上但打不通任何内网 IP — 非常困扰的典型故障
# 错误操作:多个客户端设备共用同一个密钥对
# 每个设备必须有自己独立的密钥对 — 复用密钥会破坏安全模型导致路由冲突
# 错误操作:使用粗暴的 "FORWARD ACCEPT" 全局 iptables 规则
# 转发规则应严格限定在 wg0 接口及具体的流量方向上
最佳实践 (Best Practices)
- 坚持一机一密钥:为每个客户端生成唯一的密钥对,绝不复用
- 手机端首选分流模式 (
AllowedIPs = <家庭子网网段>),省电且速度快 - 所有移动客户端均配置
PersistentKeepalive = 25保持心跳连接 - 动态公网 IP 必须结合 DDNS 使用;凭据抽离到环境变量或安全配置文件中,绝不内联明文硬编码
- 采用收紧作用域的 iptables 转发规则(仅限 wg0 入站),严禁粗暴开启全局 FORWARD ACCEPT
- 在客户端配置的
DNS =中填写家庭 Pi-hole 地址,在户外也能享受全网去广告 - 定期轮换服务端密钥对并同步更新客户端配置
相关技能 (Related Skills)
- homelab-network-setup
- homelab-vlan-segmentation
- homelab-pihole-dns




