——并且可以看见客户端的真实IP!
已知问题:
- 无法访问docker映射出的端口,因为docker会再次进行NAT操作,导致回包源IP变为docker容器内部ip而非10.0.0.2,不能被策略路由匹配
- 由于wireguard接收的流量会进入forward链而不是input链,ufw/fail2ban在默认情况下无法封锁来自某ip的流量,可手动添加iptables规则
0.要求
- 一台家庭服务器
- 一台拥有公网IP的云服务器,用于流量转发
1.相比常见的其他内网穿透方案的优势
使用商业方案:
- 又贵又慢,不如自建一根
其他自建方案:
- Wireguard性能明显更强(包括frp、ngrok等端口映射工具,openvpn等其他l2 vpn工具)
- 支持范围端口转发,同时转发上万个端口或全端口
- 本文中实现的:为所有tcp/udp服务传递真实IP,无需proxy protocol,几乎还原公网体验
- 完美支持ipv6(本文中未实现但和ipv4配置方法完全一样)(真的有人用ipv6吗)
2.通过wireguard实现内网互通:
教程参考https://varkai.com/posts/operation/use-wireguard-networking-to-achieve-intranet-penetration/ 的教程
配置云服务器内网ip为10.0.0.1,家里云ip为10.0.0.2,可互相ping通、访问服务后进行下一步
3.在云服务器上配置DNAT/SNAT转发规则
教程参考https://superuser.com/questions/1777082/using-vps-to-give-public-ip-to-on-home-network/1777106
很多转发教程会使用masquerade,这样当然非常简单,但是会导致运行在家里云上的服务无法获取访问者的真实ip,全部显示为10.0.0.1
- ssh/rdp/web日志全部来自10.0.0.1,无法封禁爆破ip
- 游戏服务器中所有玩家的数据包都会来自10.0.0.1,触发速率限制导致玩家掉线
使用DNAT/SNAT即可解决,规则如下:
iptables -I FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
iptables -I FORWARD -d 10.0.0.2 -m conntrack --ctstate DNAT -j ACCEPT
iptables -t nat -I PREROUTING -p tcp -d [VPS public IP] -i ens192 --dport 80 -j DNAT --to-destination 10.0.0.2:80
iptables -t nat -A POSTROUTING -o ens192 -j SNAT --to-source [VPS public IP]
其中ens192换为vps的网卡,VPS public IP不一定是服务器的公网IP,用ifconfig查看网卡IP,比如阿里云eth0的绑定IP就是172.开头的
4.在家里云上配置路由规则
ip -4 route add default dev wg0 table 4242
ip -4 rule add pref 500 from 10.0.0.2 lookup 4242
这可以确保所有来自云服务器的流量,都通过云服务器的IP来响应;不配置该规则的话,家里云将会直接使用家庭IP回包,导致数据包被客户端的防火墙丢弃
对于使用udp的服务(常见于各种游戏服务端,比如Mincraft BE、CS:GO..),绑定IP必须设置为家里云的wireguard IP(10.0.0.2),否则无法正常使用!(原因如上)
5.完整Wireguard配置,通过PostUp/PostDown实现自动化
根据实际情况修改
云服务器端:
wg0.conf
[Interface]
Address = 10.0.0.1/24
SaveConfig = true
PostUp = /etc/wireguard/post-up.sh
PostDown = /etc/wireguard/post-down.sh
ListenPort = 61820
PrivateKey = [VPS Wireguard Private Key]
[Peer]
...
post-up.sh
#!/bin/bash
# VPS WireGuard PostUp Script - NAT 端口转发配置
# 转发 1000-60000 端口的 TCP/UDP 流量到 Homelab
# 配置变量
PUBLIC_IP="172.x.x.x" # VPS 公网 IP
PUBLIC_IFACE="eth0" # VPS 公网接口
HOMELAB_IP="10.0.0.2" # Homelab 内网 IP
PORT_RANGE="1000:60000" # 转发端口范围
echo "[$(date)] Starting VPS NAT rules setup..."
# 1. 启用 IP 转发
sysctl -w net.ipv4.ip_forward=1
echo "net.ipv4.ip_forward=1" > /etc/sysctl.d/99-wireguard.conf
# 2. FORWARD 链规则 - 允许转发
iptables -I FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
iptables -I FORWARD -d ${HOMELAB_IP} -m conntrack --ctstate DNAT -j ACCEPT
# 3. PREROUTING NAT 规则 - DNAT 端口转发
# TCP
iptables -t nat -A PREROUTING -p tcp -d ${PUBLIC_IP} \
-i ${PUBLIC_IFACE} --dport 222 \
-j DNAT --to-destination ${HOMELAB_IP}:22
iptables -t nat -A PREROUTING -p tcp -d ${PUBLIC_IP} \
-i ${PUBLIC_IFACE} --dport ${PORT_RANGE} \
-j DNAT --to-destination ${HOMELAB_IP}
# UDP
iptables -t nat -A PREROUTING -p udp -d ${PUBLIC_IP} \
-i ${PUBLIC_IFACE} --dport ${PORT_RANGE} \
-j DNAT --to-destination ${HOMELAB_IP}
# 4. POSTROUTING NAT 规则 - SNAT 出站流量
iptables -t nat -A POSTROUTING -o ${PUBLIC_IFACE} \
-j SNAT --to-source ${PUBLIC_IP}
echo "[$(date)] VPS NAT rules setup completed!"
postdown.sh
#!/bin/bash
# VPS WireGuard PostDown Script - 清理 NAT 规则
# 配置变量(与 PostUp 保持一致)
PUBLIC_IP="172.x.x.x"
PUBLIC_IFACE="eth0"
HOMELAB_IP="10.0.0.2"
PORT_RANGE="1000:60000"
echo "[$(date)] Cleaning up VPS NAT rules..."
# 清理 FORWARD 规则
iptables -D FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT 2>/dev/null
iptables -D FORWARD -d ${HOMELAB_IP} -m conntrack --ctstate DNAT -j ACCEPT 2>/dev/null
# 清理 PREROUTING 规则
iptables -t nat -D PREROUTING -p tcp -d ${PUBLIC_IP} \
-i ${PUBLIC_IFACE} --dport 222 \
-j DNAT --to-destination ${HOMELAB_IP}:22
iptables -t nat -D PREROUTING -p tcp -d ${PUBLIC_IP} \
-i ${PUBLIC_IFACE} --dport ${PORT_RANGE} \
-j DNAT --to-destination ${HOMELAB_IP} 2>/dev/null
iptables -t nat -D PREROUTING -p udp -d ${PUBLIC_IP} \
-i ${PUBLIC_IFACE} --dport ${PORT_RANGE} \
-j DNAT --to-destination ${HOMELAB_IP} 2>/dev/null
# 清理 POSTROUTING 规则
iptables -t nat -D POSTROUTING -o ${PUBLIC_IFACE} \
-j SNAT --to-source ${PUBLIC_IP} 2>/dev/null
echo "[$(date)] VPS NAT rules cleaned up!"
家里云端
wg0.conf
[Interface]
PrivateKey = [ Homelab Wireguard Private Key]
Address = 10.0.0.2/24
Table = off
PostUp = /etc/wireguard/post-up.sh
PostDown = /etc/wireguard/post-down.sh
[Peer]
PublicKey = [ VPS Wireguard Public Key ]
Endpoint = [ VPS Reachable Public IP ] :61820
AllowedIPs = 0.0.0.0/0, ::/0
PersistentKeepalive = 25
post-up.sh
#!/bin/bash
# Homelab WireGuard PostUp Script - 策略路由配置
# 配置变量
HOMELAB_IP="10.0.0.2"
WG_IFACE="wg0"
ROUTING_TABLE="4242"
echo "[$(date)] Setting up Homelab policy routing..."
# 1. 创建自定义路由表
ip route add default dev ${WG_IFACE} table ${ROUTING_TABLE}
# 2. 添加策略路由规则 - 从 HOMELAB_IP 发出的包走 WireGuard
ip rule add from ${HOMELAB_IP} lookup ${ROUTING_TABLE} pref 100
# 3. 刷新路由缓存
ip route flush cache
# 4. 验证配置
echo "Policy routing rules:"
ip rule show | grep ${ROUTING_TABLE}
echo "Routing table ${ROUTING_TABLE}:"
ip route show table ${ROUTING_TABLE}
echo "[$(date)] Homelab policy routing setup completed!"
post-down.sh
#!/bin/bash
# Homelab WireGuard PostDown Script - 清理策略路由
# 配置变量
HOMELAB_IP="10.0.0.2"
ROUTING_TABLE="4242"
echo "[$(date)] Cleaning up Homelab policy routing..."
# 1. 删除策略路由规则
ip rule del from ${HOMELAB_IP} lookup ${ROUTING_TABLE} 2>/dev/null
# 2. 清空路由表
ip route flush table ${ROUTING_TABLE} 2>/dev/null
# 3. 刷新路由缓存
ip route flush cache
echo "[$(date)] Homelab policy routing cleaned up!"
最后
配置完成且测试通过后,可以执行systemctl enable –now wg-quick@wg0来让wireguard服务开机自启动
wg-quick up/down wg0可以控制开关
6.故障排除
在折腾的时候用到的一些故障排除/测试手段:
路由测试:ip route get 8.8.8.8 from 10.0.0.2,可以检查家里云路由是否走wireguard
检查udp conntrack情况:watch -n 1 ‘sudo conntrack -L -p udp | grep 23333’
调整conntrack超时时间:sysctl -w net.netfilter.nf_conntrack_udp_timeout=30 && sysctl -w net.netfilter.nf_conntrack_udp_timeout_stream=180
另一个方案
参考https://blog.feld.me/posts/2025/03/static-ips-from-the-cloud/
这个也是利用wireguard,但是使用了更底层的arp proxy,可以将公网ip直接绑定到家里云的虚拟网卡上,实际上和公网无异了
该方案的缺点是要求vps有两个IP,并且不能是VPC网络(单IP其实也可以用VNC访问,但是不方便管理)
Edit:目前几乎所有云服务商的VPS都在使用VPC网络,此方案可行性较低