香港服务器如何利用CN2 GIA线路搭建安全VPN,保证跨境员工访问内部系统的稳定性?

周一 9 点,北京、杭州、成都的同事陆续上线,Slack 红点狂闪:“Git 拉不动”“ERP 打不开”。我从 Prometheus 面板看跨境 RTT 抖成锯齿,晚高峰更夸张。问题不是内网,是“出海”。领导一句话:“把香港的那台机器拉起来,走好一点的线,今天就要稳住。”
我盯着机房里那台 1U 小钢炮,给自己定了四件事:
- 线路:必须是 CN2 GIA(AS4809) 入口,必要时双上联备份
- 协议:桌面/服务器走 WireGuard,移动端走 IKEv2/IPsec(strongSwan),统一认证
- 系统:CentOS 7 基线加固 + 升级到能开 BBR 的 LTS 内核
- 路由:策略分流——中国大陆前缀强走 CN2,其他流量走普通 BGP,上线健康检查与自动切换
当天傍晚,我完成了首轮上线,第二天早高峰延迟曲线从“锯齿”变成了“直尺”。下面是完整过程。
1. 目标与验收标准(SLO)
| 指标 | 目标 | 说明 |
|---|---|---|
| 北京/上海/广州 → HK VPN RTT | ≤ 45~65 ms | 走 CN2 GIA 的稳定区间 |
| 抖动(Jitter, p95) | ≤ 8 ms | 早晚高峰不明显抬升 |
| 丢包(1 小时窗口) | < 0.2% | 连续观测 |
| Git Clone 1GB 仓库 | ≤ 3 分钟 | 员工端到 HK,经 HK 再到代码托管 |
| VPN 并发 | ≥ 300 端 | 典型 200 人同时在线,留余量 |
| 故障切换 | ≤ 15 秒 | CN2 健康探测降级到普通 BGP |
2. 硬件与主机参数
| 项 | 选型/参数 | 备注 |
|---|---|---|
| 机房 | 香港葵涌/荃湾机房 | 电力与空调稳定 |
| 线路 | CN2 GIA 100 Mbps 保障 + 普通 BGP 200 Mbps 备 | 两上联,独立网口 |
| 服务器 | 1U 双电 | 冗余电源 |
| CPU | Intel Xeon Silver 4210R(10C/20T) | AES-NI 加速 |
| 内存 | 64 GB ECC | 足够多并发 |
| 系统盘 | 480 GB 企业 SATA / NVMe | 系统+日志分区 |
| 网口 | Intel X520 10GbE(eth1 接 CN2, eth0 接 BGP) | SR-IOV 可选 |
| OS | CentOS 7.9(Enforcing SELinux) | 内核后续换 5.4 LTS |
经验:WireGuard 更吃 CPU 单核 AES 与内核网络栈优化,10GbE 不是必须,但优质 Intel NIC 的中断与 offload 表现更稳。
3. 拓扑与寻址
[China Users] <--CN2 GIA--> [HK VPN GW]
│ │
WireGuard/IKEv2 ├─ 内网系统 (私网 10.20.0.0/16)
└─ 外部资源(Git, SaaS)
- VPN 虚拟网段:10.8.0.0/24(WG),10.9.0.0/24(IKEv2)
- 管理面只允许跳板机段访问(ACL)
- 路由策略:目的地在中国大陆前缀 → 走 CN2;其他 → 走普通 BGP
4. 系统基线:加固与最小化
# 基础源与工具
yum install -y epel-release
yum update -y
# 账户与SSH
useradd -m ops && passwd -l root
mkdir -p /home/ops/.ssh && chmod 700 /home/ops/.ssh
# 上传你的公钥到 /home/ops/.ssh/authorized_keys
chown -R ops:ops /home/ops/.ssh && chmod 600 /home/ops/.ssh/authorized_keys
# SSH 加固
sed -i 's/^#\?PermitRootLogin.*/PermitRootLogin no/' /etc/ssh/sshd_config
sed -i 's/^#\?PasswordAuthentication.*/PasswordAuthentication no/' /etc/ssh/sshd_config
systemctl restart sshd
# SELinux 保持 Enforcing
sestatus
# Firewalld 基线
yum install -y firewalld
systemctl enable --now firewalld
firewall-cmd --set-default-zone=public
5. 内核升级(开启 BBR、提升网络栈)
CentOS 7 默认 3.10 内核不带 BBR,升级到 ELRepo kernel-lt(5.4 LTS):
rpm --import https://www.elrepo.org/RPM-GPG-KEY-elrepo.org
yum install -y https://www.elrepo.org/elrepo-release-7.el7.elrepo.noarch.rpm
yum --enablerepo=elrepo-kernel install -y kernel-lt kernel-lt-devel
grub2-set-default 0
grub2-mkconfig -o /boot/grub2/grub.cfg
reboot
# 重启后验证
uname -r
# 开 BBR + 基本内核网络参数
cat >/etc/sysctl.d/99-vpn.conf <<'EOF'
net.ipv4.ip_forward = 1
net.ipv4.conf.all.rp_filter = 0
net.core.default_qdisc = fq
net.ipv4.tcp_congestion_control = bbr
net.ipv4.tcp_mtu_probing = 1
net.core.rmem_max = 2500000
net.core.wmem_max = 2500000
net.ipv4.tcp_keepalive_time = 600
net.ipv4.tcp_fin_timeout = 15
EOF
sysctl --system
坑 1:升级内核后,某些旧版 out-of-tree 驱动会跪,Intel X520 属于内置驱动 ixgbe,基本没坑。
6. 安装 WireGuard(桌面/服务器主力)
CentOS 7 + kernel 5.4 下可用 ELRepo extras:
yum --enablerepo=elrepo-extras install -y kmod-wireguard wireguard-tools
# 生成密钥
umask 077
wg genkey | tee /etc/wireguard/server.key | wg pubkey > /etc/wireguard/server.pub
/etc/wireguard/wg0.conf:
[Interface]
Address = 10.8.0.1/24
ListenPort = 443 # 走 443/udp,更像 QUIC,穿透友好
PrivateKey = <把 /etc/wireguard/server.key 内容贴这里>
MTU = 1380 # 减少跨境分段导致的丢包
SaveConfig = true
# NAT 与防火墙
PostUp = firewall-cmd --zone=public --add-port=443/udp; \
firewall-cmd --zone=public --add-masquerade; \
iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -o eth1 -j MASQUERADE
PostDown = firewall-cmd --zone=public --remove-port=443/udp; \
firewall-cmd --zone=public --remove-masquerade; \
iptables -t nat -D POSTROUTING -s 10.8.0.0/24 -o eth1 -j MASQUERADE
启用:
systemctl enable --now wg-quick@wg0
wg show
示例 客户端(开发同学的 Mac/Linux):
[Interface]
PrivateKey = <client.private>
Address = 10.8.0.2/32
DNS = 10.20.0.53 # 如果你在内网有权威DNS
[Peer]
PublicKey = <server.pub>
Endpoint = vpn.example.com:443
AllowedIPs = 10.20.0.0/16, 172.16.0.0/12, 192.168.0.0/16 # 企业私网分流
PersistentKeepalive = 25
坑 2:MTU 不合适会出现“能连上但访问慢/断流”。跨境我一般从 1380 起试,必要时配合 iptables -t mangle --clamp-mss-to-pmtu。
7. IKEv2(strongSwan)给手机与内置客户端
yum install -y strongswan strongswan-libcharon
# 用 acme.sh 签证书(示例采用 standalone)
curl https://get.acme.sh | sh
~/.acme.sh/acme.sh --issue -d vpn.example.com --standalone --keylength ec-256
~/.acme.sh/acme.sh --install-cert -d vpn.example.com --ecc \
--key-file /etc/strongswan/ipsec.d/private/server.key \
--fullchain-file /etc/strongswan/ipsec.d/certs/server.crt
chown root:root /etc/strongswan/ipsec.d/private/server.key
chmod 600 /etc/strongswan/ipsec.d/private/server.key
restorecon -Rv /etc/strongswan/ipsec.d
/etc/strongswan/ipsec.conf(EAP 用户名/密码,分流指向企业网段):
config setup
uniqueids=never
conn ikev2-eap
keyexchange=ikev2
ike=aes256-sha256-modp2048!
esp=aes256-sha256!
dpdaction=clear
dpddelay=30s
rekey=no
left=%defaultroute
leftid=vpn.example.com
leftcert=server.crt
leftauth=pubkey
leftsubnet=10.20.0.0/16,172.16.0.0/12,192.168.0.0/16
right=%any
rightauth=eap-mschapv2
rightsendcert=never
eap_identity=%any
rightsourceip=10.9.0.0/24
auto=add
/etc/strongswan/ipsec.secrets:
: RSA "server.key"
alice : EAP "S3cureP@ss"
bob : EAP "An0therP@ss"
推送 DNS(/etc/strongswan/strongswan.d/charon/attr.conf):
charon {
attr {
dns = 10.20.0.53
nbns = 0.0.0.0
}
}
防火墙:
firewall-cmd --permanent --add-port=500/udp
firewall-cmd --permanent --add-port=4500/udp
firewall-cmd --permanent --add-masquerade
firewall-cmd --reload
systemctl enable --now strongswan
坑 3:iOS 连接不上但日志显示握手正常,多半是 证书链/leftid 问题。确保 leftid 与证书 CN/SAN 匹配域名,且安装完整链。
8. 利用 CN2 GIA 做跨境稳定分流(策略路由)
机房给了两条链路:
- eth1 = CN2 GIA(AS4809),网关 GW_CN2
- eth0 = 普通 BGP,网关 GW_BGP
思路:把目的地在 中国大陆前缀 的流量标记后,走 路由表 100(CN2);其余保持默认(表 main → BGP)。此外做 健康检查,CN2 抖动/丢包超阈值就回切 BGP。
8.1 路由表与规则
# /etc/iproute2/rt_tables 末尾追加
echo "100 cn2" >> /etc/iproute2/rt_tables
# 配置静态默认路由到 CN2 表
ip route add default via GW_CN2 dev eth1 table cn2
# 把来自 VPN 网段、且目的地命中“大陆前缀”的流量打标
yum install -y ipset
# 定义中国大陆前缀集合(示例)
cat >/etc/sysconfig/ipset <<'EOF'
create cn cidr hash:net family inet hashsize 1024 maxelem 65536
# 你可以用 chnroutes 生成并导入,以下仅示意几条
add cn 1.0.1.0/24
add cn 1.0.2.0/23
# ...(建议每日定时更新)
EOF
ipset restore < /etc/sysconfig/ipset
# mangle 表标记 + fwmark 路由
iptables -t mangle -N CN2_MARK
iptables -t mangle -A PREROUTING -s 10.8.0.0/24 -m set --match-set cn dst -j CN2_MARK
iptables -t mangle -A PREROUTING -s 10.9.0.0/24 -m set --match-set cn dst -j CN2_MARK
iptables -t mangle -A CN2_MARK -j MARK --set-mark 10
ip rule add fwmark 10 table cn2
说明:我们只标记 来自 VPN 虚拟网段 的流量,避免影响管理面与机房出网。
8.2 健康检查与自动切换
/usr/local/sbin/route-healthcheck.sh(简化示例):
#!/usr/bin/env bash
# 选取三个可靠的大陆探测 IP(可用自家北上广前置机、或权威DNS)
targets=("101.226.4.6" "180.163.57.45" "120.196.165.24")
loss_threshold=3 # 丢包超过 3% 判定不健康
rtt_threshold=80 # 平均 RTT 超过 80ms 判定不健康
count=20
check_cn2() {
local loss_sum=0
local rtt_sum=0
for t in "${targets[@]}"; do
# 走 cn2 表发包
res=$(ping -I eth1 -c $count -n -q $t 2>/dev/null)
loss=$(echo "$res" | awk -F',' '/packet/{print $3}' | tr -dc '0-9')
rtt=$(echo "$res" | awk -F'/' 'END{print $5+0}')
loss_sum=$((loss_sum + loss))
rtt_sum=$(echo "$rtt_sum + $rtt" | bc)
done
avg_loss=$(echo "scale=2; $loss_sum / ${#targets[@]}" | bc)
avg_rtt=$(echo "scale=2; $rtt_sum / ${#targets[@]}" | bc)
if (( $(echo "$avg_loss > $loss_threshold" | bc -l) )) || \
(( $(echo "$avg_rtt > $rtt_threshold" | bc -l) )); then
return 1
else
return 0
fi
}
if check_cn2; then
ip rule | grep -q "fwmark 0xa lookup cn2" || ip rule add fwmark 10 table cn2
else
ip rule del fwmark 10 table cn2 2>/dev/null
fi
Systemd 定时:
cat >/etc/systemd/system/route-healthcheck.service <<'EOF'
[Unit]
Description=CN2 Healthcheck
[Service]
Type=oneshot
ExecStart=/usr/local/sbin/route-healthcheck.sh
EOF
cat >/etc/systemd/system/route-healthcheck.timer <<'EOF'
[Unit]
Description=CN2 Healthcheck Timer
[Timer]
OnBootSec=30s
OnUnitActiveSec=15s
[Install]
WantedBy=timers.target
EOF
systemctl daemon-reload
systemctl enable --now route-healthcheck.timer
坑 4:不要用 curl 去测应用延迟,有时被 WAF/限速干扰。ICMP + 你自己的前置探针更可靠。
9. 安全与可观测性
- fail2ban:针对 443/udp 无意义但高频的探测,配合 wg show 日志与 journalctl -u strongswan 做智能拉黑
- SELinux:保持 Enforcing,若 strongSwan 证书目录访问被拒,restorecon -Rv 基本解决
- 审计:auditd + rsyslog,登录、配置变更审计到专用文件
- 监控:node_exporter、VPN 在线数、自研 RTT/丢包 exporter,Grafana 设阈值告警
- 备份:/etc/wireguard、/etc/strongswan、/etc/sysconfig/ipset、/usr/local/sbin/*.sh 拉快照到安全仓
10. DNS 与分流策略(避免全流量出海)
建议:只把企业私网与必要的 SaaS 目的网段走 VPN,其他本地直出
WireGuard 客户端通过 AllowedIPs 控制(见第 6 节)
IKEv2 通过 leftsubnet= 向客户端下发路由前缀
内网域名用 dnsmasq/unbound 做权威/转发,避免泄露与跨境解析抖动
/etc/dnsmasq.d/corp.conf 示例:
server=/corp.example.com/10.20.0.53
address=/git.corp.example.com/10.20.10.23
11. 压测与观测数据(上线前后对比)
端到端 Ping(员工端→HK VPN)
| 城市 | 协议 | 升级前 RTT(均值/抖动) | 升级后(CN2 GIA) |
|---|---|---|---|
| 北京 | ICMP | 74 ms / 18 ms | 46 ms / 5 ms |
| 上海 | ICMP | 68 ms / 15 ms | 42 ms / 4 ms |
| 广州 | ICMP | 58 ms / 12 ms | 39 ms / 3 ms |
Git Clone 1GB 仓库(均 3 次)
| 城市 | 升级前 | 升级后 |
|---|---|---|
| 北京 | 7m42s | 2m58s |
| 上海 | 6m31s | 2m34s |
| 广州 | 5m54s | 2m20s |
丢包(1 小时窗口)
| 时间 | 升级前 | 升级后 |
|---|---|---|
| 09:00-10:00 | 1.8% | 0.12% |
| 20:00-21:00 | 2.3% | 0.18% |
12. 现场踩过的坑与修复过程
WireGuard 帧分片导致“时好时坏”
现象:wg show 正常,网页间歇超时。
排查:MTR 显示跨境某跳丢包,怀疑 MTU 触发分片。
处理:WG MTU 调 1380,并在出口加 --clamp-mss-to-pmtu。问题消失。
iOS IKEv2 连接失败(证书/ID 不匹配)
现象:charon 握手后掉线。
处理:确认证书 CN/SAN 含 vpn.example.com,并将 leftid 设为域名,重签完整链。OK。
SELinux 拒绝 strongSwan 读私钥
现象:type=AVC 拒绝。
处理:restorecon -Rv /etc/strongswan/ipsec.d;确保权限 600,所有者 root。
CN2 夜间抖动
现象:20:00-21:30 RTT 飙至 90ms+。
处理:健康检查脚本将策略临时切到 BGP,平稳后切回;Grafana 告警留痕。
ethtool offload 导致奇怪的乱序
现象:iperf3 单流带宽忽高忽低。
处理:ethtool -K ethX gso off gro off tso off,并设置中断亲和 irqbalance --oneshot 后手工绑核,波动明显减小。
13. 运维手册(常用操作清单)
# 新增 WireGuard 用户(示例)
umask 077
wg genkey | tee alice.key | wg pubkey > alice.pub
cat >> /etc/wireguard/wg0.conf <<EOF
[Peer]
# Alice
PublicKey = <alice.pub 内容>
AllowedIPs = 10.8.0.10/32
EOF
systemctl reload wg-quick@wg0
# strongSwan 新增用户
echo 'carol : EAP "Str0ng!Pass"' >> /etc/strongswan/ipsec.secrets
systemctl reload strongswan
# 查看在线与统计
wg show
swanctl --list-sas # 若使用 swanctl;starter 体系用 'ipsec statusall'
# 临时切换策略路由(故障演练)
ip rule del fwmark 10 table cn2
ip rule add fwmark 10 table cn2
# 更新大陆前缀(每日计划任务)
# 生成 cn.txt 后:
ipset flush cn && ipset restore < /etc/sysconfig/ipset
14. 成本核算(粗略)
| 项 | 规格 | 单价(参考) | 备注 |
|---|---|---|---|
| CN2 GIA | 100M 保障 | $$$ | 可突发 200M |
| 普通 BGP | 200M | $$ | 备链路 |
| 服务器租用 | 1U 托管/独立 | $$ | 含电力/机位 |
| 人工 | 1 人/周 | $ | 首次部署与调优 |
经验:CN2 GIA 的“贵”是换来稳定的早晚高峰曲线和少数工单;并发多时,踏实比便宜更重要。
15. 把“锯齿”磨平
第二天早高峰,我习惯性地盯着 Grafana。RTT 线像拉紧的琴弦,平直。Slack 里有人说:“今天怎么这么顺?”我笑了笑,给香港那台小钢炮的机箱门拍了拍。它并不耀眼,但把跨境那段最脆的链路接住了。到了晚上,CN2 有一次短暂抖动,健康检查自动把策略切回 BGP,15 秒后稳定——没人察觉。
这就是我想要的 VPN:不是炫技,而是稳定、可回滚、可观察。
留给后来者的 3 个要点:
- 选好线路 > 堆参数;CN2 GIA 能把“锯齿”磨平
- 双协议双路径:WireGuard 性能,IKEv2 兼容
- 策略分流 + 健康检查:让“正确的流量走正确的路”,并能自动自救
如果你也准备把香港服务器撑成跨境的“缓冲带”,希望这篇现场笔记能让你少踩几个坑,多睡几个安稳觉。