在现代网络架构中,WireGuard 凭借其极简的代码实现和现代加密算法,已成为高效组建虚拟局域网(VLAN)的首选方案。但在实际部署中,无论是跨境链路的 MTU 陷阱,还是 K8s 容器环境下缺失 systemdresolvconf 的限制,都让初学者颇为头疼。

本文结合实战经验,深度分析三类核心部署脚本的设计逻辑,助你构建稳健的私有网络。

一、 服务端:自动化网关的基石

服务端的职责不仅仅是握手,更重要的是充当流量转发的中转站。

1. 核心自动化逻辑

  • 网络环境初始化:自动开启内核级别的 net.ipv4.ip_forward 转发功能,确保虚拟网卡与物理网卡之间流量互通。
  • 动态流量伪装:通过 iptables 自动配置 MASQUERADE 规则。脚本能智能识别宿主机的物理网卡名称,实现 NAT 转发,让客户端能够通过服务器访问公网。
  • 热加载节点管理:引入 wg syncconf 机制,在添加新 Peer(客户端)时,无需重启整个服务即可实时生效,保证了已有连接的稳定性。

wg_install.sh

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
#!/bin/bash

# ====================================================
# 服务端固定配置
# ====================================================
SERVER_PRIV="your private key"
SERVER_PUB="your public key"
SERVER_PORT=15321
SERVER_IP_RANGE="10.10.10.1/24"
INTERFACE="wg0"
CONF_PATH="/etc/wireguard/${INTERFACE}.conf"

show_help() {
echo "用法: sudo $0 [选项]"
echo ""
echo "选项:"
echo " gen 生成一组全新的客户端密钥对"
echo " add [名称] [公钥] [IP/32] 添加一个新的客户端 (例如: add Phone xxxx 10.10.10.5/32)"
echo " --help, -h 显示此帮助信息"
echo ""
echo "示例:"
echo " 首次安装: sudo $0"
echo " 增加设备: sudo $0 add Node_A xxxxxxxx 10.10.10.3/32"
}

# 处理指令
case "$1" in
gen)
command -v wg >/dev/null 2>&1 || sudo apt update && sudo apt install wireguard -y
_tmp_priv=$(wg genkey)
_tmp_pub=$(echo "$_tmp_priv" | wg pubkey)
echo "PRIVATE_KEY: $_tmp_priv"
echo "PUBLIC_KEY: $_tmp_pub"
exit 0
;;
add)
if [ -z "$3" ] || [ -z "$4" ]; then
echo "错误: add 指令需要 [名称] [公钥] [IP] 三个参数。"
exit 1
fi
CLIENT_NAME="$2"
CLIENT_PUB="$3"
CLIENT_IP="$4"
;;
--help|-h)
show_help
exit 0
;;
"")
# 默认模式:初始化或显示当前状态
;;
*)
echo "未知指令: $1"
show_help
exit 1
;;
esac

# 执行逻辑
command -v wg >/dev/null 2>&1 || sudo apt update && sudo apt install wireguard iptables -y
sudo sysctl -w net.ipv4.ip_forward=1
echo "net.ipv4.ip_forward=1" | sudo tee /etc/sysctl.d/99-wireguard.conf > /dev/null

if [ ! -f "$CONF_PATH" ]; then
ETH_NAME=$(ip route | grep default | awk '{print $5}' | head -n1)
cat <<EOF | sudo tee $CONF_PATH
[Interface]
PrivateKey = ${SERVER_PRIV}
Address = ${SERVER_IP_RANGE}
ListenPort = ${SERVER_PORT}
PostUp = iptables -A FORWARD -i ${INTERFACE} -j ACCEPT; iptables -t nat -A POSTROUTING -o ${ETH_NAME} -j MASQUERADE
PostDown = iptables -D FORWARD -i ${INTERFACE} -j ACCEPT; iptables -t nat -D POSTROUTING -o ${ETH_NAME} -j MASQUERADE
EOF
fi

if [ -n "$CLIENT_PUB" ]; then
if grep -q "$CLIENT_PUB" "$CONF_PATH"; then
echo "提示: 公钥 $CLIENT_PUB 已存在。"
else
echo "正在追加 Peer: $CLIENT_NAME ($CLIENT_IP)"
cat <<EOF | sudo tee -a $CONF_PATH

# $CLIENT_NAME
[Peer]
PublicKey = ${CLIENT_PUB}
AllowedIPs = ${CLIENT_IP}
EOF
fi
fi

sudo systemctl enable wg-quick@${INTERFACE}
if sudo systemctl is-active --quiet wg-quick@${INTERFACE}; then
sudo wg syncconf ${INTERFACE} <(sudo wg-quick strip ${INTERFACE})
else
sudo systemctl start wg-quick@${INTERFACE}
fi
echo "--- 当前 WireGuard 状态 ---"
sudo wg show

二、 常规客户端:交互式简化与自启动

对于标准的 Linux 服务器或虚拟机,部署的重点在于快速入网与开机持久化。

1. 部署亮点

  • 交互式配置:通过命令行交互自动获取服务端 IP 与端口,避免手动编辑配置文件的低效。
  • MTU 优化策略:针对跨运营商或跨境链路,脚本默认将 MTU 设定为 1280,有效避免了大包在复杂网络节点中被静默丢弃的问题。
  • Systemd 托管:通过 systemctl enable 实现服务级别的自启动,确保设备重启后隧道能够自动重连。

wg_install_client.sh

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
#!/bin/bash

# ====================================================
# WireGuard 客户端优化脚本
# 功能:自动安装依赖、密钥管理、配置校验、服务自启
# ====================================================

# 1. 基础配置
CLIENT_WG_IP="10.10.10.4/24"
ALLOWED_IPS="10.10.10.0/24"
SERVER_PUB="dHJLSDvhCkYlDvj/3ic3thYV9rxJfda10SphYZXEcQk="
INTERFACE="wg0"
CONF_PATH="/etc/wireguard/${INTERFACE}.conf"

# 检查 root 权限
[[ $EUID -ne 0 ]] && echo "错误: 请使用 sudo 运行" && exit 1

# 2. 自动安装 WireGuard 相关工具
install_wg() {
if ! command -v wg &> /dev/null; then
echo "正在安装 wireguard-tools..."
if command -v apt-get &> /dev/null; then
apt-get update && apt-get install -y wireguard resolvconf
elif command -v yum &> /dev/null; then
yum install -y epel-release && yum install -y wireguard-tools
else
echo "错误: 无法识别的包管理器,请手动安装 wireguard-tools"
exit 1
fi
fi
}

# 3. 交互式输入 (增加验证)
get_server_info() {
if [ -z "$SERVER_IP" ]; then
read -p "请输入海外服务器公网 IP: " SERVER_IP
while [[ ! $SERVER_IP =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; do
read -p "IP 格式错误,请重新输入: " SERVER_IP
done

read -p "请输入端口 [15321]: " SERVER_PORT
SERVER_PORT=${SERVER_PORT:-15321}
fi
}

# 4. 密钥管理逻辑 (优化提取逻辑)
manage_keys() {
mkdir -p /etc/wireguard/
chmod 700 /etc/wireguard/

if [ -f "$CONF_PATH" ]; then
CLIENT_PRIV=$(grep "PrivateKey" "$CONF_PATH" | sed 's/ //g' | cut -d'=' -f2)
fi

if [ -z "$CLIENT_PRIV" ]; then
CLIENT_PRIV=$(wg genkey)
echo "生成新私钥并已存入配置文件。"
echo "对应的公钥为: $(echo "$CLIENT_PRIV" | wg pubkey)"
fi
}

# 5. 执行安装与配置
install_wg
get_server_info
manage_keys

# 6. 写入配置
cat <<EOF > "$CONF_PATH"
[Interface]
PrivateKey = ${CLIENT_PRIV}
Address = ${CLIENT_WG_IP}
MTU = 1280

[Peer]
PublicKey = ${SERVER_PUB}
Endpoint = ${SERVER_IP}:${SERVER_PORT}
AllowedIPs = ${ALLOWED_IPS}
PersistentKeepalive = 25
EOF

chmod 600 "$CONF_PATH"

# 7. 启动与验证
echo "正在重启 WireGuard 接口: ${INTERFACE}..."
systemctl stop wg-quick@${INTERFACE} >/dev/null 2>&1

# 启用并启动服务
systemctl daemon-reload
systemctl enable wg-quick@${INTERFACE} >/dev/null 2>&1

if systemctl restart wg-quick@${INTERFACE}; then
echo "===================================================="
echo "状态: 启动成功!"
echo "模式: 安全分流 (仅处理 ${ALLOWED_IPS})"
echo "测试: ping -c 3 10.10.10.1"
echo "===================================================="
wg show "${INTERFACE}"
else
echo "启动失败,请检查日志: journalctl -xeu wg-quick@${INTERFACE}"
fi

三、 K8s/PM2 深度优化版:攻克容器环境

这是最具挑战性的场景。在 K8s 容器或精简版镜像中,由于缺乏 systemd 守护进程且 /etc/resolv.conf 受到系统严格锁定,传统脚本往往会失效。

1. 探索与突破点分析

  • 剔除 resolvconf 依赖:在容器中,/etc/resolv.conf 通常是挂载的只读文件。脚本显式移除了对 resolvconf 的依赖,解决了因文件锁定导致的安装失败问题。
  • PM2 进程托管:在没有 systemd 的环境下,利用 PM2 接管隧道。通过一个“包装脚本”执行 wg-quick up 后进入 sleep infinity 状态,欺骗 PM2 维持进程常驻。
  • 生命周期管理:利用 Shell 的 trap 机制捕获 SIGTERMSIGINT 信号。当执行 pm2 stop 时,脚本能自动触发 wg-quick down 清理网卡,防止网卡名称冲突。
  • 环境变量补全:在容器内,PM2 的运行路径可能不包含 /sbin。脚本通过显式导出 PATH,确保了 ipwg 等核心命令能被正确调用。

wg_install_pm2.sh

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
#!/bin/bash

# ====================================================
# WireGuard 客户端 (PM2 适配版 - K8s 深度优化)
# 修复:移除 resolvconf 依赖,解决 /etc/resolv.conf 被锁定问题
# ====================================================

# 1. 基础配置
CLIENT_WG_IP="10.10.10.3/24"
ALLOWED_IPS="10.10.10.0/24"
SERVER_PUB="dHJLSDvhCkYlDvj/3ic3thYV9rxJfda10SphYZXEcQk="
INTERFACE="wg0"
CONF_PATH="/etc/wireguard/${INTERFACE}.conf"
WRAPPER_PATH="/etc/wireguard/wg_pm2_run.sh"

[[ $EUID -ne 0 ]] && echo "错误: 请使用 sudo 运行" && exit 1

# 2. 强力安装依赖 (剔除 resolvconf)
install_deps() {
echo "正在清理受损的 resolvconf (如果存在)..."
apt-get purge -y resolvconf &> /dev/null

echo "正在同步并安装网络工具包 (iproute2, wireguard-tools)..."
apt-get update && apt-get install -y iproute2 wireguard-tools

if ! command -v ip &> /dev/null; then
echo "错误: ip 命令安装失败,请检查系统源!"
exit 1
fi
}

# 3. 密钥管理 (修复等号截断)
manage_keys() {
mkdir -p /etc/wireguard/
chmod 700 /etc/wireguard/
if [ -f "$CONF_PATH" ]; then
CLIENT_PRIV=$(grep "PrivateKey" "$CONF_PATH" | sed 's/ //g' | cut -d'=' -f2-)
fi
if [ -z "$CLIENT_PRIV" ] || [ ${#CLIENT_PRIV} -lt 43 ]; then
CLIENT_PRIV=$(wg genkey)
fi
CLIENT_PUB=$(echo "$CLIENT_PRIV" | wg pubkey)
echo "===================================================="
echo "客户端公钥: $CLIENT_PUB"
echo "===================================================="
}

# 4. 获取服务端信息
get_server_info() {
if [ -z "$SERVER_IP" ]; then
read -p "请输入海外服务器公网 IP: " SERVER_IP
read -p "请输入端口 [15321]: " SERVER_PORT
SERVER_PORT=${SERVER_PORT:-15321}
fi
}

install_deps
get_server_info
manage_keys

# 5. 写入配置 (显式去掉 DNS 选项防止调用 resolvconf)
cat <<EOF > "$CONF_PATH"
[Interface]
PrivateKey = ${CLIENT_PRIV}
Address = ${CLIENT_WG_IP}
MTU = 1380

[Peer]
PublicKey = ${SERVER_PUB}
Endpoint = ${SERVER_IP}:${SERVER_PORT}
AllowedIPs = ${ALLOWED_IPS}
PersistentKeepalive = 25
EOF
chmod 600 "$CONF_PATH"

# 6. PM2 包装脚本 (显式补全环境变量)
cat <<EOF > "$WRAPPER_PATH"
#!/bin/bash
# 确保 PM2 环境能找到 sbin 下的 ip 命令
export PATH=\$PATH:/sbin:/usr/sbin:/usr/local/sbin

function cleanup {
echo "正在关闭隧道..."
wg-quick down ${INTERFACE}
exit
}
trap cleanup SIGTERM SIGINT

echo "正在启动 WireGuard 隧道..."
wg-quick up ${INTERFACE}

echo "隧道运行中..."
sleep infinity
EOF
chmod +x "$WRAPPER_PATH"

# 7. 启动 PM2
if command -v pm2 &> /dev/null; then
pm2 delete "wg-client" &> /dev/null
pm2 start "$WRAPPER_PATH" --name "wg-client"
pm2 save
echo "配置完成!请检查:pm2 logs wg-client"
else
echo "未找到 PM2,请手动启动 $WRAPPER_PATH"
fi

四、 实战避坑指南:那些让你握手失败的细节

在探索过程中,我们总结了几个极易忽视的“深坑”:

1. 依赖包的精准补全

精简版镜像往往不含 iproute2。如果报错 ip: command not foundwg-quick 将彻底瘫痪。必须确保安装了此包。

2. 私钥提取的截断问题

在自动化提取旧配置中的私钥时,简单的 cut 命令可能会截断 Base64 末尾的 = 号。WireGuard 对密钥格式有严格要求,任何字符缺失都会导致静默丢包。

3. MTU 的“木桶效应”

如果隧道能 Ping 通但无法加载网页,通常是 MTU 导致的。建议在不稳定的链路中统一使用 1280


五、 使用建议与工作流

  1. 服务端先行:运行服务端脚本初始化网关,生成服务端公钥。
  2. 客户端入网:根据环境选择常规版或 PM2 优化版。运行脚本生成客户端公钥后,在服务端执行 add 操作。
  3. 持久化验证:重启机器或 PM2 任务,验证隧道是否能自动拉起并恢复握手。

结语:WireGuard 的强大在于其简洁,而部署的难点在于环境的多样性。通过理解 systemdPM2 的托管差异,以及容器对网络配置的特殊限制,我们才能构建出真正“全场景通用”的自动化组网方案。