安装
命令
apt install wireguard nftablesWireGuard 配置与维护命令。
apt install wireguard nftablescd /etc/wireguardumask 077wg genkey | tee server_private.key | wg pubkey > server_public.keynano wg0.conf# ENDPOINT 10.0.0.2
[Interface]
# 服务器私钥(请使用实际生成的私钥)
PrivateKey = uB4YJTk0LtWuWYcYJmKkLdIWyhLEMLUYtuLtGUv0VF8=
# 服务器监听端口(与 nftables 配置中 wireguard set 对应的端口一致)
ListenPort = 57575
# 服务器在隧道内的地址(可多写几行地址:IPv4 + IPv6)
Address = 10.100.0.1/24
Address = fddd:5555::1/64
# 若要访问外网,需要开启 IP 转发
# 可以直接在这里使用 PostUp/PostDown 配合 nftables,也可以单独写入 /etc/sysctl.conf
# 下面仅做演示
#PostUp = sysctl -w net.ipv4.ip_forward=1; sysctl -w net.ipv6.conf.all.forwarding=1
#PostDown = sysctl -w net.ipv4.ip_forward=0; sysctl -w net.ipv6.conf.all.forwarding=0
# 如果需要让客户端访问服务器所在的局域网,可以在这里追加路由相关设置,或者由 nftables 完成 NAT。
# 例如服务端所在局域网是 192.168.1.0/24,可以加:
# PostUp = ip route add 192.168.1.0/24 dev wg0sudo wg-quick up wg0sudo systemctl enable wg-quick@wg0.servicesudo sed -i 's/#net.ipv4.ip_forward=1/net.ipv4.ip_forward=1/' /etc/sysctl.confsudo sed -i 's/#net.ipv6.conf.all.forwarding=1/net.ipv6.conf.all.forwarding=1/' /etc/sysctl.confsudo sysctl -papt install qrencodenano wg-client-manage.sh#!/usr/bin/env bash
#
# WireGuard - Add/Remove clients, automatically parse Address prefix from wg0.conf
#
WG_CONF="/etc/wireguard/wg0.conf"
# 1) 必须使用 bash
if readlink /proc/$$/exe | grep -q "dash"; then
echo "请使用 bash 而不是 sh/dash 运行此脚本。"
exit 1
fi
# 2) 必须用 root
if [[ $EUID -ne 0 ]]; then
echo "请使用 root 权限运行此脚本。"
exit 1
fi
# 3) 检查配置文件
if [[ ! -f "$WG_CONF" ]]; then
echo "未找到 $WG_CONF,请确认已正确安装并配置 WireGuard。"
exit 1
fi
############################################
# 函数: 解析服务器信息 (PrivateKey等) #
############################################
parse_server_info() {
# 服务器 PrivateKey
server_privkey=$(sed -n 's/^[[:space:]]*PrivateKey[[:space:]]*=[[:space:]]*\(.*\)/\1/p' "$WG_CONF" | head -1)
if [[ -z "$server_privkey" ]]; then
echo "无法在 $WG_CONF 中解析到服务器 PrivateKey。"
exit 1
fi
server_pubkey=$(echo "$server_privkey" | wg pubkey)
# 服务器端口
server_port=$(sed -n 's/^[[:space:]]*ListenPort[[:space:]]*=[[:space:]]*\(.*\)/\1/p' "$WG_CONF" | head -1)
[[ -z "$server_port" ]] && server_port="51820"
# 服务器 Endpoint
server_endpoint=$(sed -n 's/^[[:space:]]*# ENDPOINT[[:space:]]*\(.*\)/\1/p' "$WG_CONF" | head -1)
[[ -z "$server_endpoint" ]] && server_endpoint="your_server_ip_or_domain"
# 解析服务器 IPv4 = x.x.x.x/24
# 取 "x.x.x.x" 部分
local v4_addr
v4_addr=$(sed -n 's/^[[:space:]]*Address[[:space:]]*=[[:space:]]*\([0-9]\+\(\.[0-9]\+\)\{3\}\)\/24/\1/p' "$WG_CONF" | head -1)
if [[ -n "$v4_addr" ]]; then
# 去掉最后一段 -> x.x.x
IPV4_PREFIX="${v4_addr%.*}"
HAS_IPV4=1
else
HAS_IPV4=0
fi
# 解析服务器 IPv6 = fddd:xxxx::1/64
# 假设服务器地址以 "::1" 结尾
local v6_addr
v6_addr=$(sed -n 's/^[[:space:]]*Address[[:space:]]*=[[:space:]]*\([0-9A-Fa-f:]\+\)\/64/\1/p' "$WG_CONF" | head -1)
if [[ -n "$v6_addr" ]]; then
# 用 Bash 正则匹配出 "前缀::" 与 "尾部数字"
# 例如 "fd42:42::1" -> group1="fd42:42::" group2="1"
if [[ "$v6_addr" =~ ^(.*::)([0-9A-Fa-f]+)$ ]]; then
IPV6_PREFIX="${BASH_REMATCH[1]}"
HAS_IPV6=1
else
# 如果你的地址不是 ::1 形式,请自行改进解析
IPV6_PREFIX=""
HAS_IPV6=0
fi
else
HAS_IPV6=0
fi
}
############################################
# 函数: 让用户选择 DNS #
############################################
choose_dns() {
echo "选择客户端使用的 DNS 服务器:"
echo " 1) 系统默认 (/etc/resolv.conf)"
echo " 2) Google (8.8.8.8, 8.8.4.4)"
echo " 3) Cloudflare (1.1.1.1, 1.0.0.1)"
echo " 4) OpenDNS (208.67.222.222, 208.67.220.220)"
echo " 5) Quad9 (9.9.9.9, 149.112.112.112)"
echo " 6) AdGuard (94.140.14.14, 94.140.15.15)"
read -p "请选择 [1]: " dns_opt
until [[ -z "$dns_opt" || "$dns_opt" =~ ^[1-6]$ ]]; do
echo "$dns_opt: 无效输入。"
read -p "请选择 [1]: " dns_opt
done
case "$dns_opt" in
1|"")
if grep -q '^nameserver' /etc/resolv.conf | grep -qv '127.0.0.53'; then
resolv_conf="/etc/resolv.conf"
else
resolv_conf="/run/systemd/resolve/resolv.conf"
fi
DNS=$(grep -v '^#\|^;' "$resolv_conf" \
| grep '^nameserver' \
| grep -v '127.0.0.53' \
| grep -oE '[0-9]{1,3}(\.[0-9]{1,3}){3}' \
| xargs \
| sed 's/ /, /g')
;;
2) DNS="8.8.8.8, 8.8.4.4" ;;
3) DNS="1.1.1.1, 1.0.0.1" ;;
4) DNS="208.67.222.222, 208.67.220.220" ;;
5) DNS="9.9.9.9, 149.112.112.112" ;;
6) DNS="94.140.14.14, 94.140.15.15" ;;
esac
# 如果最终没检测到任何 DNS,就默认给个 Google
if [[ -z "$DNS" ]]; then
DNS="8.8.8.8, 8.8.4.4"
fi
}
############################################
# 函数: 添加新客户端 #
############################################
add_client() {
# 解析服务器信息
parse_server_info
if [[ $HAS_IPV4 -eq 0 && $HAS_IPV6 -eq 0 ]]; then
echo "服务器未配置 IPv4 或 IPv6 地址 (Address = ...),脚本无法继续。"
exit 1
fi
# 输入客户端名称
echo "请输入新客户端名称 (仅允许英数字、下划线、减号,不超过15字符):"
read -p "Name: " input_name
local client_name
client_name=$(sed 's/[^0-9A-Za-z_-]/_/g' <<< "$input_name" | cut -c1-15)
while [[ -z "$client_name" ]] || grep -q "^# BEGIN_PEER $client_name\$" "$WG_CONF"; do
echo "$client_name: 无效或已存在,请重新输入。"
read -p "Name: " input_name
client_name=$(sed 's/[^0-9A-Za-z_-]/_/g' <<< "$input_name" | cut -c1-15)
done
# 选择 DNS
echo
choose_dns
# 生成客户端密钥
local client_privkey client_pubkey client_psk
client_privkey=$(wg genkey)
client_pubkey=$(echo "$client_privkey" | wg pubkey)
client_psk=$(wg genpsk)
# 找下一个可用尾段
local octet=2
while true; do
local used4=0 used6=0
if [[ $HAS_IPV4 -eq 1 ]]; then
# 若 AllowedIPs 中已出现 x.x.x.octet/32,则说明占用
if grep -q "AllowedIPs = .*${IPV4_PREFIX}\.${octet}/32" "$WG_CONF"; then
used4=1
fi
fi
if [[ $HAS_IPV6 -eq 1 ]]; then
# 若 AllowedIPs 中已出现 prefix::octet/128,则说明占用
if grep -q "AllowedIPs = .*${IPV6_PREFIX}${octet}/128" "$WG_CONF"; then
used6=1
fi
fi
if [[ $used4 -eq 0 && $used6 -eq 0 ]]; then
break
else
(( octet++ ))
if [[ $octet -ge 65535 ]]; then
echo "已无可用地址可分配。"
exit 1
fi
fi
done
# 拼出客户端 IPv4/IPv6
local client_v4 client_v6
if [[ $HAS_IPV4 -eq 1 ]]; then
client_v4="${IPV4_PREFIX}.${octet}"
fi
if [[ $HAS_IPV6 -eq 1 ]]; then
client_v6="${IPV6_PREFIX}${octet}"
fi
# 将 Peer 写入服务器配置 wg0.conf
{
echo "# BEGIN_PEER $client_name"
echo "[Peer]"
echo "PublicKey = $client_pubkey"
echo "PresharedKey = $client_psk"
if [[ $HAS_IPV4 -eq 1 && $HAS_IPV6 -eq 1 ]]; then
echo "AllowedIPs = $client_v4/32, $client_v6/128"
elif [[ $HAS_IPV4 -eq 1 ]]; then
echo "AllowedIPs = $client_v4/32"
else
echo "AllowedIPs = $client_v6/128"
fi
echo "# END_PEER $client_name"
} >> "$WG_CONF"
# 生成客户端配置文件
local client_conf=~/"$client_name.conf"
{
echo "[Interface]"
if [[ $HAS_IPV4 -eq 1 && $HAS_IPV6 -eq 1 ]]; then
echo "Address = $client_v4/24, $client_v6/64"
elif [[ $HAS_IPV4 -eq 1 ]]; then
echo "Address = $client_v4/24"
else
echo "Address = $client_v6/64"
fi
echo "DNS = $DNS"
echo "PrivateKey = $client_privkey"
echo
echo "[Peer]"
echo "PublicKey = $server_pubkey"
echo "PresharedKey = $client_psk"
echo "AllowedIPs = 0.0.0.0/0, ::/0"
echo "Endpoint = $server_endpoint:$server_port"
echo "PersistentKeepalive = 25"
} > "$client_conf"
# 加载到当前运行中的 wg0
wg addconf wg0 <(sed -n "/^# BEGIN_PEER $client_name\$/,/^# END_PEER $client_name\$/p" "$WG_CONF")
echo
echo "=========================================="
echo "已添加新客户端:$client_name"
if [[ -n "$client_v4" ]]; then
echo " IPv4: $client_v4"
fi
if [[ -n "$client_v6" ]]; then
echo " IPv6: $client_v6"
fi
echo "客户端配置文件:$client_conf"
# 若安装了 qrencode 则输出二维码
if command -v qrencode &>/dev/null; then
echo
echo "客户端配置二维码:"
qrencode -t ANSI256UTF8 < "$client_conf"
echo
fi
}
############################################
# 函数: 删除已有客户端 #
############################################
remove_client() {
local total
total=$(grep -c '^# BEGIN_PEER' "$WG_CONF")
if [[ $total -eq 0 ]]; then
echo
echo "当前没有可删除的客户端配置。"
return
fi
echo
echo "现有客户端列表:"
grep '^# BEGIN_PEER' "$WG_CONF" | cut -d ' ' -f 3 | nl -s ') '
read -p "请输入要删除的客户端编号: " idx
if ! [[ "$idx" =~ ^[0-9]+$ ]] || (( idx < 1 || idx > total )); then
echo "编号不合法。"
return
fi
local client_name
client_name=$(grep '^# BEGIN_PEER' "$WG_CONF" | cut -d ' ' -f 3 | sed -n "${idx}p")
echo
read -p "确认删除客户端 [$client_name]? [y/N]: " confirm
if [[ "$confirm" =~ ^[yY]$ ]]; then
# 从运行中的 wg0 中移除
local pubk
pubk=$(sed -n "/^# BEGIN_PEER $client_name\$/,/^# END_PEER $client_name\$/p" "$WG_CONF" \
| grep '^PublicKey' | awk '{print $3}')
if [[ -n "$pubk" ]]; then
wg set wg0 peer "$pubk" remove
fi
# 从配置文件删除
sed -i "/^# BEGIN_PEER $client_name\$/,/^# END_PEER $client_name\$/d" "$WG_CONF"
echo "已删除客户端:$client_name"
else
echo "操作取消。"
fi
}
############################################
# 主菜单 #
############################################
clear
echo "============================================"
echo " WireGuard 客户端管理 (自动解析地址前缀)"
echo "============================================"
echo
echo "1) 添加新客户端"
echo "2) 删除已有客户端"
echo "3) 退出"
echo
read -p "请选择操作 [1-3]: " choice
case "$choice" in
1) add_client ;;
2) remove_client ;;
3) echo "退出。"; exit 0 ;;
*) echo "无效选项,退出。"; exit 1 ;;
esac