如何把无公网的求生之路 2 服务器借助 VPS 转发注册到 steam master 列表中
先说在前,这个方法解决的是:一台无公网的游戏服务器,单靠 frp 等软件内网穿透不能进入匹配列表和路人联机的问题,因为 l4d2server 的注册方式是向 steam master server 发送注册请求和心跳包,上报服务器 IP 和端口。下面的方法需要至少具备以下条件:
- 有一台 gameserver 和一台公网 VPS
- 操作系统均为 Linux 发行版
- 了解 wireguard 的基本使用
可能只对很少人感兴趣,因为目前大部分服务器都会加上 -nomaster 隐藏来防止扫段 DDOS,只留给部分亲友联机,或者确实有人有这方面需求但是有自己的方法(反正我没搜到相关帖子)
背景
Master Server Query Protocol - Valve Developer Community 详细写了关于 gameserver 的注册流程和请求响应报文格式,开始我的想法就是反向代理然后手动构建注册请求,结果在 Discussion 末尾有人提到 16 年之后游戏注册和查询使用了内部加密协议,之后的查询只能用 steam webAPI。如果尝试在 gameserver 启动后的控制台输入 heartbeat 同时抓包, 会出现几个使用 TLS 加密的 packet。经过一番搜索,最终只得出来两个可能的方法:1.逆向报文/server 端 2.让公网 VPS IP 作为默认网关。
第一种方法显然对我来说不可能(有这技术我还折腾这吗)随后知道了 Linux network namespace 机制的存在,它可以让 srcds_run 在虚拟的网络空间运行,并且把 VPS 当出口网关,同时不影响我服务器上其他正常的业务。
最后方案很简单:先用 wireguard 组网实现内网穿透,配置 iptables 转发 27015 流量进入 wg 组网内路由到内网机器上,然后让 gameserver 程序跑在 network namespace 里把公网 VPS 对应的 wg IP 设置成默认出口路由,这样注册请求就能从公网 IP 发出去了。
实现
之前很少手动配过路由和 iptables,实际折腾了不少时间
以下的操作我会分成 gameserver 端(即没公网的一端)和 VPS 端(有公网 IP 一端)分别讲:
gameserver 端:
#!/usr/bin/env bash
set -e
NS="ns1"
VETH_HOST="veth-host"
VETH_NS="veth-ns"
HOST_IP="10.200.1.1/24"
NS_IP="10.200.1.2/24"
VPS_IP="你的公网IP" #自行替换
WG_CONF="/etc/wireguard/wg0.conf"
echo "=== Create namespace ==="
if ! ip netns list | grep -q "$NS"; then
sudo ip netns add "$NS"
fi
echo "=== Create veth pair ==="
if ! ip link show "$VETH_HOST" &>/dev/null; then
sudo ip link add "$VETH_HOST" type veth peer name "$VETH_NS"
fi
echo "=== Move veth-ns to namespace ==="
sudo ip link set "$VETH_NS" netns "$NS" 2>/dev/null || true
echo "=== Configure host interface ==="
sudo ip addr add "$HOST_IP" dev "$VETH_HOST" 2>/dev/null || true
sudo ip link set "$VETH_HOST" up
echo "=== Configure namespace interface ==="
sudo ip netns exec "$NS" ip addr add "$NS_IP" dev "$VETH_NS" 2>/dev/null || true
sudo ip netns exec "$NS" ip link set "$VETH_NS" up
sudo ip netns exec "$NS" ip link set lo up
echo "=== Setup DNS for namespace ==="
sudo mkdir -p "/etc/netns/$NS"
echo -e "nameserver 1.1.1.1\nnameserver 8.8.8.8" | sudo tee "/etc/netns/$NS/resolv.conf" >/dev/null
echo "=== Start WireGuard ==="
sudo ip netns exec "$NS" wg-quick up "$WG_CONF" || true
echo "=== Add routes ==="
sudo ip netns exec "$NS" ip route add "$VPS_IP" via 10.200.1.1 dev "$VETH_NS" 2>/dev/null || true
sudo ip netns exec "$NS" ip route add default dev wg0 2>/dev/null || true
if ! iptables -t nat -C POSTROUTING -s 10.200.1.0/24 -o eth0 -j MASQUERADE 2>/dev/null; then
iptables -t nat -A POSTROUTING -s 10.200.1.0/24 -o eth0 -j MASQUERADE
fi
sudo sysctl -w net.ipv4.ip_forward=1
echo "=== Done ==="
VPS 侧:
#!/usr/bin/env bash
set -e
#wg网段自行替换
WG_NET="10.0.8.0/24"
SERVER_IP="10.0.8.5"
echo "=== Enable IP forwarding ==="
sudo sysctl -w net.ipv4.ip_forward=1
echo "=== Setup NAT for WireGuard network ==="
sudo iptables -t nat -C POSTROUTING -s $WG_NET -o eth0 -j MASQUERADE 2>/dev/null || \
sudo iptables -t nat -A POSTROUTING -s $WG_NET -o eth0 -j MASQUERADE
echo "=== Setup DNAT for game ports ==="
sudo iptables -t nat -C PREROUTING -p udp --dport 27015 -j DNAT --to-destination $SERVER_IP:27015 2>/dev/null || \
sudo iptables -t nat -A PREROUTING -p udp --dport 27015 -j DNAT --to-destination $SERVER_IP:27015
sudo iptables -t nat -C PREROUTING -p udp --dport 27005 -j DNAT --to-destination $SERVER_IP:27005 2>/dev/null || \
sudo iptables -t nat -A PREROUTING -p udp --dport 27005 -j DNAT --to-destination $SERVER_IP:27005
echo "=== Allow forwarding ==="
sudo iptables -C FORWARD -p udp -d $SERVER_IP --dport 27015 -j ACCEPT 2>/dev/null || \
sudo iptables -A FORWARD -p udp -d $SERVER_IP --dport 27015 -j ACCEPT
sudo iptables -C FORWARD -p udp -d $SERVER_IP --dport 27005 -j ACCEPT 2>/dev/null || \
sudo iptables -A FORWARD -p udp -d $SERVER_IP --dport 27005 -j ACCEPT
echo "=== Done ==="
此时大功告成了,进入 l4d2server 根目录执行:
sudo ip netns exec ns1 ./srcds_run -game left4dead2 -strictportbind -ip 0.0.0.0 -port 27015 +clientport 27005 +map c5m1_waterfront +servercfgfile l4d2server.cfg -maxplayers 8 -tickrate 100 -insecure
(直接使用 sudo 提权执行会出现服务端警告)
连接 VPS_IP: 27015 就能联机,查看 https://ismygameserver.online/valve/输入你的 VPS_IP: 27015, 可以看到 Steam Master Server: LISTED,表示服务器已经成功注册到 steam master server 上
Ping 延迟问题
使用这种方式内网穿透的服务器受地理位置影响,RTT 往往显著增加,从而导致较高的 Ping 值,导致服务器在匹配列表中处于优先级靠后的位置,有一种方法是在 VPS 端手动构建 A2S Query 返回包(也就是做假 Ping),A2S 是 UDP 协议而且是明文,大厅的 ping 值测试来源于 A2S_CHALLENGE 报文
这种方式可能可以解决大厅匹配优先级靠后的问题(未经验证),但游戏内的 Ping 值检测无法修改,并且一定程度上损害游戏匹配公平性,所以不提供实现