香港服务器的 UDP 全被封,VPN 就只能走 TCP 了
香港服务器的 UDP 全被封,VPN 就只能走 TCP 了
在同一台跑着博客和游戏站的香港服务器上架了个 VPN 给自己用。体检全部合格,唯独 UDP 入站被上游 NAT 封得死死的——于是 WireGuard 出局,OpenVPN 走 TCP 8443。
先说结论
- 这台服务器做 VPN 完全可行:KVM 全虚拟化、内核自带 WireGuard、TUN 可用,但 UDP 入站被服务商 1:1 NAT 封死,原生 WireGuard 方案直接出局
- 最终方案:OpenVPN over TCP 8443,独立端口不碰 443,实测全隧道出口正常、ping 8.8.8.8 延迟 2.4ms、Google 返回 200
- 最大的坑不在 OpenVPN,在 firewalld:它自己的转发链末尾是一条 reject,执行优先级还比 iptables 高,iptables 里加多少条 ACCEPT 都没用
第一步:先搞清楚「能不能做」
光看配置单看不出来,直接登上去体检:
| 检查项 | 结果 |
|---|---|
| 虚拟化 | KVM 全虚拟化(不是 OpenVZ,不会被内核锁死) |
| 系统 | CentOS Stream 9,内核 5.14,自带 wireguard 模块 |
| 权限 | root 全量 capabilities |
| TUN/TAP | /dev/net/tun 存在,ip tuntap add 实测成功 |
| 防火墙 | 宝塔 iptables 链 + firewalld 双层 |
| 资源 | 7.6G 内存(可用 2.7G)/ 50G 盘剩 22G / 下行约 110Mbps |
| TCP 入站 | 任意端口可达,实测临时开 51822 秒连上 |
| UDP 入站 | 全部超时 |
最后一行就是整篇文章的转折点。
第二步:UDP 到底是不是真被封了
「UDP 不通」有很多种可能:我自己被封?服务器防火墙?还是服务商?三步排除:
- 本机发 UDP 到服务器 51820 —— 超时
- 服务器自己给自己公网 IP 发(hairpin)—— 超时
- 本机 UDP 出口正常吗?DNS(53)、NTP(123)都通 —— 排除自己这边
都排除之后,请外援:用 check-host 从全球 60 个节点同时探测 UDP 51820,服务器上同时开 tcpdump 抓 eth0。结果:60 个节点全部 timeout,服务器上一个包都没抓到。
# 服务器上只看到 silence
tcpdump -n -i eth0 udp and port 51820
listening on eth0 ... (0 packets captured)
包根本没进机房。再看一眼网卡就明白了:eth0 是 10.0.34.2 的私有地址,机器在服务商 1:1 NAT 后面,出口 SNAT 成公网 IP,而服务商只做了 TCP 端口映射,UDP 没映射。
这一步的教训:下结论之前先让结论可被证伪。「防火墙没放行」和「上游根本不转发」是两个世界的问题。
第三步:装 OpenVPN(这步反而是最快的)
EPEL 里直接装 openvpn 和 easy-rsa,证书用 ECDSA(prime256v1),服务端配置几个关键点:
port 8443
proto tcp-server # ← UDP 被封,必须 TCP
dev tun
topology subnet
server 10.8.0.0 255.255.255.0
push "redirect-gateway def1 bypass-dhcp"
tls-crypt ta.key # 控制信道加密,顺便防探测
data-ciphers AES-256-GCM:AES-128-GCM
mssfix 1360 # TCP 隧道里防分片
max-clients 8
小坑一个:EPEL 包的服务单元叫 openvpn-server@server,网上教程里清一色的 openvpn@server 会直接报 Unit not found。

第四步:转发被拒(本次最大的坑)
VPN 起来了、握手成功,但隧道里的流量出不去。iptables 这边明明是:
-P FORWARD ACCEPT
-A FORWARD -i tun0 -o eth0 -j ACCEPT ← 计数器 0 packets
规则在、策略全 ACCEPT,计数器却永远是 0——说明包压根没走到 iptables 的 filter 表。真正的凶手在这里:
# nft list chain inet firewalld filter_FORWARD
type filter hook forward priority filter + 10; policy accept;
...
reject with icmpx admin-prohibited ← 末尾这条
firewalld 自己那张 nft 表的转发链末尾是 reject,而且它的 hook 优先级(filter+10)比 iptables filter 表先执行。也就是说:包先过 firewalld 的链,匹配不到任何放行规则就被拒了,iptables 那些规则形同虚设。
解法顺理成章:把 tun0 加进 public 区域(和 eth0 同区,同区内转发放行),NAT 用 firewalld 的 masquerade 做主、iptables 规则做备。改完重启 firewalld、重启 OpenVPN,复测全通过——这套配置现在重启机器也活着。
第五步:怎么测才算真的测过
一开始图省事,在同一台服务器上再起一个 openvpn 客户端连自己。结果 ping 10.8.0.1 不通、隧道流量也出不去,折腾半天发现是测试方法本身错了:服务端和客户端在同一个内核里,10.8.0.1 和 10.8.0.3 都是"本地地址",回程包直接被本地投递,根本不进隧道。
正确的姿势是用网络命名空间造一个真正的"外部客户端":
ip netns add vtest
ip link add veth-h type veth peer name veth-c
ip link set veth-c netns vtest
ip addr add 172.31.9.1/30 dev veth-h
ip netns exec vtest ip addr add 172.31.9.2/30 dev veth-c
ip netns exec vtest ip route add <服务器内网IP>/32 via 172.31.9.1
ip netns exec vtest openvpn --config client1.ovpn &
| 验证项 | 结果 |
|---|---|
| 客户端握手 | 通过,拿到 10.8.0.3 |
| 隧道内出口 IP | 154.219.108.21(服务器公网 IP) |
| ping 8.8.8.8 | 0% 丢包,2.4ms |
| https://www.google.com | HTTP 200 |
| 重启 firewalld + 重启 OpenVPN 后复测 | 全部通过 |
客户端分发
Mac 上用 Tunnelblick,.ovpn 一个文件打包了 CA、证书、私钥和 tls-crypt 密钥,双击导入。
两个顺带的坑:
- tunnelblick.net 在大陆打不开。让香港服务器把最新版 dmg 下载下来再传回来,本地校验 SHA-256、
codesign开发者签名(Jonathan Bullard)和苹果公证票据,全套过了才装。 - iOS 中国区 App Store 没有 OpenVPN Connect(2017 年整体下架过一波),得用香港区的 Apple ID 下载,付款方式选"无"就行。iOS 原生那套 IKEv2/L2TP 全是 UDP 500/4500,在这台机器上同样没戏。
运维备忘
| 项 | 位置 |
|---|---|
| 服务端配置 | /etc/openvpn/server/server.conf |
| 运行日志 | /var/log/openvpn/openvpn.log |
| 在线状态 | /var/log/openvpn/status.log |
| NAT | openvpn-nat.service + firewalld masquerade 双保险 |
| 吊销证书 | easy-rsa revoke + gen-crl,crl-verify 已配好 |
最要紧的一条:别把 proto tcp-server 改成 UDP,改了立刻全断,而且这种断法连报错都不会有。
评论 (0)
还没有评论,来说两句吧~
发表评论