通过 WireGuard 将云服务器公网IP映射到内网服务器
内网穿透 家宽附加公网IP
1. 场景说明
- 云服务器(VPS):有公网 IP,作为流量入口
- 内网服务器:没有公网 IP,但能连上网
- 目标:外网客户端通过云服务器访问内网服务,保留真实客户端 IP
2. 流程图
3. 部署步骤
3.1 云服务器安装必需软件
apt update && apt install -y wireguard iproute2 iptables
- wireguard:VPN 模块和工具(
wg
,wg-quick
) - iproute2:提供
ip
、ip rule
、ip route
等路由工具 - iptables:做 DNAT 转发和防火墙规则
3.2 生成 WireGuard 密钥
云服务器(服务端)
# 生成私钥,保存到文件
wg genkey | tee /etc/wireguard/server_private.key \
# 同时生成公钥
| wg pubkey > /etc/wireguard/server_public.key
# 设置私钥权限,避免被非 root 读取
chmod 600 /etc/wireguard/server_private.key
内网服务器(客户端)
wg genkey | tee /etc/wireguard/client_private.key \
| wg pubkey > /etc/wireguard/client_public.key
chmod 600 /etc/wireguard/client_private.key
为什么要两边都生成?
因为 WireGuard 是点对点加密,双方都要有自己的密钥对,并保存对方的公钥。
3.3 云服务器配置 /etc/wireguard/wg0.conf
cat > /etc/wireguard/wg0.conf <<'EOF'
[Interface]
# WireGuard 虚拟网卡 IP(服务端)
Address = 10.0.5.1/24
# 监听 UDP 端口(在云厂商安全组放行)
ListenPort = 60000
# 云服务器私钥(替换变量)
PrivateKey = <SERVER_PRIVATE_KEY>
# 启动时执行的命令:
# 1. 开启 IPv4 转发(允许转发数据包)
PostUp = sysctl -w net.ipv4.ip_forward=1
# 2. 放宽 rp_filter,防止非对称路由丢包
PostUp = sysctl -w net.ipv4.conf.all.rp_filter=2
# 保留 SSH & WireGuard 端口,不做 DNAT 转发(防止自己被转发走)
PostUp = iptables -t nat -I PREROUTING -i eth0 -p tcp -d <CLOUD_PRIV_IP> --dport 22 -j RETURN
PostUp = iptables -t nat -I PREROUTING -i eth0 -p udp -d <CLOUD_PRIV_IP> --dport 22 -j RETURN
PostUp = iptables -t nat -I PREROUTING -i eth0 -p tcp -d <CLOUD_PRIV_IP> --dport 60000 -j RETURN
PostUp = iptables -t nat -I PREROUTING -i eth0 -p udp -d <CLOUD_PRIV_IP> --dport 60000 -j RETURN
# 将其他流量 DNAT 到内网机 wg0 IP
PostUp = iptables -t nat -A PREROUTING -d <CLOUD_PRIV_IP> -j DNAT --to-destination <CLIENT_WG_IP>
# 放行公网→wg0 转发
PostUp = iptables -A FORWARD -i eth0 -o wg0 -d <CLIENT_WG_IP> -m state --state NEW,ESTABLISHED,RELATED -j ACCEPT
# 放行 wg0→公网 转发
PostUp = iptables -A FORWARD -i wg0 -o eth0 -m state --state ESTABLISHED,RELATED -j ACCEPT
# 停止时撤销规则(与 PostUp 一一对应)
PostDown = iptables -t nat -D PREROUTING -i eth0 -p tcp -d <CLOUD_PRIV_IP> --dport 22 -j RETURN
PostDown = iptables -t nat -D PREROUTING -i eth0 -p udp -d <CLOUD_PRIV_IP> --dport 22 -j RETURN
PostDown = iptables -t nat -D PREROUTING -i eth0 -p tcp -d <CLOUD_PRIV_IP> --dport 60000 -j RETURN
PostDown = iptables -t nat -D PREROUTING -i eth0 -p udp -d <CLOUD_PRIV_IP> --dport 60000 -j RETURN
PostDown = iptables -t nat -D PREROUTING -d <CLOUD_PRIV_IP> -j DNAT --to-destination <CLIENT_WG_IP>
PostDown = iptables -D FORWARD -i eth0 -o wg0 -d <CLIENT_WG_IP> -m state --state NEW,ESTABLISHED,RELATED -j ACCEPT
PostDown = iptables -D FORWARD -i wg0 -o eth0 -m state --state ESTABLISHED,RELATED -j ACCEPT
[Peer]
# 客户端公钥
PublicKey = <CLIENT_PUBLIC_KEY>
# 允许客户端的 wg IP
AllowedIPs = <CLIENT_WG_IP>/32
EOF
3.4 内网服务器配置 /etc/wireguard/wg0.conf
cat > /etc/wireguard/wg0.conf <<'EOF'
[Interface]
# 客户端 wg IP
Address = 10.0.5.2/24
PrivateKey = <CLIENT_PRIVATE_KEY>
# 不修改系统默认路由
Table = off
# 进入 wg0 的连接打标记,用于策略路由
PostUp = iptables -t mangle -A PREROUTING -i wg0 -j CONNMARK --set-mark 0x1
# 回程包也打相同标记
PostUp = iptables -t mangle -A OUTPUT -m connmark --mark 0x1 -j MARK --set-mark 0x1
# 策略路由:标记包走 table 100
PostUp = ip rule add fwmark 0x1 lookup 100
# table 100 的默认路由走 wg0
PostUp = ip route add default dev wg0 table 100
# 停止时撤销规则
PostDown = iptables -t mangle -D PREROUTING -i wg0 -j CONNMARK --set-mark 0x1
PostDown = iptables -t mangle -D OUTPUT -m connmark --mark 0x1 -j MARK --set-mark 0x1
PostDown = ip rule del fwmark 0x1 lookup 100
PostDown = ip route del default dev wg0 table 100
[Peer]
# 云服务器公钥
PublicKey = <SERVER_PUBLIC_KEY>
# 云服务器公网 IP + 端口
Endpoint = <CLOUD_PUBLIC_IP_OR_FQDN>:60000
# 只允许与云服务器 wg IP / 公网 IP 通信
AllowedIPs = 10.0.5.1/32, <CLOUD_PUBLIC_IP>/32
# 保活防止 NAT 断开
PersistentKeepalive = 25
EOF
3.5 启动与开机自启
云服务器和本地服务器分别执行:
# 设置开机启动
systemctl enable wg-quick@wg0
# 立即启动
systemctl start wg-quick@wg0
# 查看状态
wg
wg
输出中 latest handshake
不为空,说明隧道已连通。
3.6 测试访问
在内网机:
python3 -m http.server 8080
在外网机:
curl -I http://<云服务器公网IP>:8080
如果能访问且内网机日志中显示外网客户端真实 IP,则成功。