香港服务器如何通过Ubuntu系统结合OpenVPN与CN2专线,解决跨境企业办公安全问题?

我坐在香港机房的 42U 机柜前,手里捧着一杯凉掉的美式,等着 IPMI 上那条熟悉的“Installation complete”。客户是做跨境研发协作的,中方团队在深圳与成都,外方在新加坡和旧金山。白天他们被跨境网络抖动折腾得够呛:代码同步龟速、远程桌面掉线、Git 大文件卡死。老板一句话:“要稳定、安全、快,明早给我结果。”
我选了香港的机房节点,拉了 CN2 GIA 专线,自己搭一套 OpenVPN,把办公流量按策略走 CN2,公共流量走国际带宽。下面这篇,就是那一夜到天亮的完整落地过程与所有坑点。
目标与架构
目标
- 为跨境办公提供稳定低时延的通道(CN2 专线承载关键办公流量)。
- 打出安全边界(OpenVPN + 最小权限 + 审计)。
- 可观测、可回滚(完善监控、变更与备份策略)。
- 新手可复现、老手看得过瘾。
逻辑拓扑(简化)
[CN Branches/Offices]
| (OpenVPN Client, Split Tunnel)
|-->[公网/运营商]--\
\ (Policy Route / fwmark)
\--[HK Server: Ubuntu + OpenVPN]--[CN2 GIA]-->[CN Office subnets]
\ \
\--[International Internet]-----------\-->[GitHub/Cloud]
- 办公侧关键网段(如 10.20.0.0/16)通过 VPN 且强制走 CN2
- 其他互联网访问可走香港普通国际带宽(节省专线资源)
选型与参数
硬件配置
| 组件 | 参数 |
|---|---|
| 机型 | 1U 双电热插拔(Supermicro 代工款) |
| CPU | Intel Xeon Silver 4314(16C32T) |
| 内存 | 64GB DDR4 ECC |
| 系统盘 | 2× 480GB SATA SSD(RAID1) |
| 数据盘 | 2× 1.92TB NVMe(RAID1,保存日志/备份/缓存) |
| 网卡 | 2× 10GbE(eno1/eno2),VLAN 打标;其一接普通 BGP 出口,其一接 CN2 GIA 线路 |
| 带外 | IPMI 2.0(独立口) |
备注:OpenVPN 更吃单核性能与内核包处理能力,单核频率和网卡中断分布比“核数”更重要;NVMe 主要为日志/备份与后续可扩展的代理缓存准备。
线路配置
| 线路 | 类型 | 速率 | 备注 |
|---|---|---|---|
| 国外/国际出口 | 标准 BGP | 1 Gbps 共享 | 公网服务、非关键办公流量 |
| CN2 专线 | CN2 GIA | 200 Mbps 保障 | 关键办公网段、低丢包、低时延 |
IP 规划(示例)
| 角色 | 网卡/子接口 | IP/VLAN | 用途 |
|---|---|---|---|
| 管理 | ipmi0 | 10.99.99.10/24 | 带外管理 |
| 国际出口 | eno1 | 公网 IP(BGP) | 通用上网与软件仓库 |
| CN2 出口 | eno2.301 | 172.16.30.2/30(VLAN 301) | 对接运营商 CN2 PE |
| VPN 池 | tun0 | 10.8.0.0/24 | OpenVPN 客户端地址池 |
| 办公网段 | - | 10.20.0.0/16 | 中国内网,走 CN2 |
系统安装与基础加固
1)装 Ubuntu(建议 22.04/24.04 LTS)
- BIOS: 打开 VT-x/VT-d、关 C-States 深度省电(防止抖动),NVMe 设为非 RAID 模式。
- 存储:系统盘做 RAID1(mdadm),ext4;数据盘 NVMe 做 RAID1,xfs。
- 分区:/ 120G、/var/log 独立到 NVMe(降低日志写入对系统盘磨损),/home 适量,预留 VG 空间做快照。
2)Netplan 配置(ENO1 国际 + ENO2 CN2 VLAN)
/etc/netplan/01-prod.yaml:
network:
version: 2
ethernets:
eno1:
dhcp4: false
addresses: [203.0.113.10/24]
gateway4: 203.0.113.1
nameservers:
addresses: [1.1.1.1, 8.8.8.8]
eno2:
dhcp4: false
vlans:
eno2.301:
id: 301
link: eno2
addresses: [172.16.30.2/30]
routes:
- to: 0.0.0.0/0
via: 172.16.30.1
metric: 50 # 仅用作专线路由表的下一跳,主表不使用(见策略路由)
注:主路由表不直接走 CN2,防止所有流量都吃专线;我们通过策略路由把来自 VPN 的包导向 CN2。
3)系统加固(SSH、内核、时钟、日志)
# 只用密钥,禁密码
sed -i 's/^#\?PasswordAuthentication.*/PasswordAuthentication no/' /etc/ssh/sshd_config
systemctl reload ssh
# NTP(chrony)
apt update && apt install -y chrony
timedatectl set-timezone Asia/Hong_Kong
# UFW + 基本策略
apt install -y ufw
ufw default deny incoming
ufw default allow outgoing
ufw allow 1194/udp # OpenVPN
ufw allow from <office-ip-range>/24 to any port 22 proto tcp
ufw enable
# Fail2ban
apt install -y fail2ban
systemctl enable --now fail2ban
# journald 持久化
mkdir -p /var/log/journal && systemctl restart systemd-journald
/etc/sysctl.d/99-vpn.conf:
net.ipv4.ip_forward=1
net.ipv4.conf.all.rp_filter=0
net.ipv4.conf.default.rp_filter=0
net.core.rmem_max=134217728
net.core.wmem_max=134217728
net.ipv4.tcp_rmem=4096 87380 67108864
net.ipv4.tcp_wmem=4096 65536 67108864
net.core.netdev_max_backlog=250000
net.core.somaxconn=1024
sysctl --system
经验:反向路径过滤要关,不然后面的策略路由会被内核丢包;内核缓冲做大,对高时延链路吞吐更友好。
安装 OpenVPN 与 PKI
apt install -y openvpn easy-rsa iperf3
make-cadir /etc/openvpn/pki
cd /etc/openvpn/pki
./easyrsa init-pki
./easyrsa build-ca nopass
./easyrsa gen-dh
./easyrsa build-server-full server nopass
./easyrsa gen-crl
openvpn --genkey --secret ta.key # tls-crypt/ta.key
为每个客户端签发证书(建议人/设备一证一用,便于吊销):
cd /etc/openvpn/pki
./easyrsa build-client-full alice nopass
./easyrsa build-client-full bob-laptop nopass
OpenVPN 服务器配置(重点:安全 + 性能)
/etc/openvpn/server.conf:
port 1194
proto udp
dev tun
topology subnet
server 10.8.0.0 255.255.255.0
ifconfig-pool-persist /var/lib/openvpn/ipp.txt
client-config-dir /etc/openvpn/ccd
# 加密与认证(OpenVPN 2.5+)
data-ciphers AES-256-GCM:AES-128-GCM:CHACHA20-POLY1305
data-ciphers-fallback AES-256-GCM
ncp-ciphers AES-256-GCM
tls-version-min 1.2
tls-crypt /etc/openvpn/pki/ta.key
dh /etc/openvpn/pki/dh.pem
ca /etc/openvpn/pki/pki/ca.crt
cert /etc/openvpn/pki/pki/issued/server.crt
key /etc/openvpn/pki/pki/private/server.key
crl-verify /etc/openvpn/pki/pki/crl.pem
remote-cert-tls client
# 性能相关
sndbuf 0
rcvbuf 0
push "sndbuf 0"
push "rcvbuf 0"
txqueuelen 4000
fast-io
tun-mtu 1500
mssfix 1400
# 保活
keepalive 10 60
ping-timer-rem
explicit-exit-notify 1
# 路由:仅推送需要的办公网段(Split Tunnel)
push "route 10.20.0.0 255.255.0.0"
# DNS(可选:只推企业内网域)
# push "dhcp-option DOMAIN corp.local"
# push "dhcp-option DNS 10.20.1.53"
# 权限与日志
user nobody
group nogroup
persist-key
persist-tun
status /var/log/openvpn-status.log
log-append /var/log/openvpn.log
verb 3
mute 20
为特定客户端分配固定 IP 与定制路由:
/etc/openvpn/ccd/alice:
ifconfig-push 10.8.0.10 255.255.255.0
iroute 10.30.10.0 255.255.255.0
经验:OpenVPN 的 sndbuf/rcvbuf 设为 0 表示采用内核自调优;mssfix 设为 1400 大多数场景稳妥,避免 MTU 黑洞。
启用与自启:
systemctl enable --now openvpn@server
策略路由:让 VPN 流量走 CN2
核心思路:把来自 tun0(10.8.0.0/24)到中国办公网段(10.20.0.0/16)的回程与外发标记并导向 CN2 的路由表(table 100)。
1)建立路由表与规则
# 表 100:CN2 出口
cat >/etc/iproute2/rt_tables <<'EOF'
#
# reserved values
#
255 local
254 main
253 default
0 unspec
100 cn2
EOF
# 添加默认路由到 CN2(只在表 100)
ip route add default via 172.16.30.1 dev eno2.301 table cn2
# 来自 VPN 池的包,打上 0x1 标记,走 cn2 表
iptables -t mangle -A PREROUTING -i tun0 -d 10.20.0.0/16 -j MARK --set-mark 0x1
ip rule add fwmark 0x1 lookup cn2
注:如果你的专线是路由模式并给了企业网段的回程静态路由,可不做 NAT。若是对端仅收专线端口的地址,需对来自 10.8.0.0/24 的流量做 SNAT(MASQUERADE)。
2)NAT(如需)
# 仅当 CN2 端要求源地址为 172.16.30.2 或专线网段时
iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -d 10.20.0.0/16 -o eno2.301 -j MASQUERADE
3)持久化 & systemd 钩子
/etc/systemd/system/cn2-routing.service:
[Unit]
Description=Policy routing for CN2
After=network-online.target
Wants=network-online.target
[Service]
Type=oneshot
ExecStart=/usr/local/sbin/cn2-route-setup.sh
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
/usr/local/sbin/cn2-route-setup.sh:
#!/usr/bin/env bash
set -e
ip route flush table cn2 || true
ip route add default via 172.16.30.1 dev eno2.301 table cn2
ip rule | grep -q "fwmark 0x1" || ip rule add fwmark 0x1 lookup cn2
iptables -t mangle -C PREROUTING -i tun0 -d 10.20.0.0/16 -j MARK --set-mark 0x1 2>/dev/null \
|| iptables -t mangle -A PREROUTING -i tun0 -d 10.20.0.0/16 -j MARK --set-mark 0x1
chmod +x /usr/local/sbin/cn2-route-setup.sh
systemctl enable --now cn2-routing.service
4)健康检查与自动切换(可选)
当 CN2 端不可达时,把路由临时切回国际出口(保证可用性)。
/usr/local/sbin/cn2-healthcheck.sh:
#!/usr/bin/env bash
TARGETS=("10.20.0.1" "10.20.0.2") # 对端核心网设备
OK=0
for t in "${TARGETS[@]}"; do
if ping -c2 -W1 -I eno2.301 "$t" >/dev/null; then
OK=1; break
fi
done
if [ $OK -eq 1 ]; then
# 确保表 100 走 CN2
ip route replace default via 172.16.30.1 dev eno2.301 table cn2
else
# 暂时把 cn2 表的默认路由指向国际出口(降级可用)
ip route replace default via 203.0.113.1 dev eno1 table cn2
fi
定时任务:
echo '*/1 * * * * root /usr/local/sbin/cn2-healthcheck.sh' >/etc/cron.d/cn2-health
客户端发放(.ovpn 示例)
alice.ovpn:
client
dev tun
proto udp
remote hk-vpn.example.com 1194
resolv-retry infinite
nobind
persist-key
persist-tun
# 加密参数与服务器一致
remote-cert-tls server
tls-crypt ta.key
cipher AES-256-GCM
auth SHA256
verb 3
<ca>
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
</ca>
<cert>
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
</cert>
<key>
-----BEGIN PRIVATE KEY-----
...
-----END PRIVATE KEY-----
</key>
经验:企业发放建议配合 MDM(如 Intune/Jamf)下发,移动端使用官方 OpenVPN Connect,Windows/macOS 建议开机自启动但不默认全隧道,通过服务器端 push route 实现只走办公网段。
性能验证与优化
1)MTU 探测(避免碎片/黑洞)
# 客户端到 VPN Server 的路径 MTU(示例)
tracepath hk-vpn.example.com
# 或逐步探测(Linux)
ping -M do -s 1472 hk-vpn.example.com
# 无碎片成功,再增大,最终 -s + 28(IP/ICMP)≈ 1500 即满
常用稳妥组合:tun-mtu 1500 + mssfix 1400。若跨运营商/云厂商路径更小(如 1460),把 mssfix 降到 1360~1380。
2)中断与网卡队列
apt install -y irqbalance
systemctl enable --now irqbalance
# 观察中断分布,避免所有中断压在一个核上
cat /proc/interrupts | grep -E 'eno|irq'
3)OpenVPN 管理与并发观察
开启管理端口(可选):
server.conf 增加 management 127.0.0.1 7505,然后:
echo -e "status\nload-stats\n" | nc 127.0.0.1 7505
4)实测数据(某日凌晨)
| 指标 | 优化前(无专线,公网) | 优化后(CN2 GIA + 策略路由) |
|---|---|---|
| 深圳 ↔ 香港 ping | 45–80 ms 抖动大 | 18–23 ms 稳定 |
| 深圳 → GitHub 克隆 | 120–300 KB/s | 2.5–6 MB/s(Split Tunnel 下 GitHub 未走 CN2,但基础网络更稳,DNS 正常) |
| 深圳 ↔ 香港 iperf3 | 20–60 Mbps | 180–200 Mbps(接近专线上限) |
| RDP/VSCode Remote | 频繁卡顿 | 无明显卡顿 |
可观测与审计
Prometheus Node Exporter + Grafana:CPU 单核负载、网卡 PPS、丢包、软中断。
OpenVPN 日志/状态:/var/log/openvpn.log、openvpn-status.log,按小时切割 logrotate。
审计:auditd 记录关键配置变更;VPN 连接/断开事件抄送到 Slack/飞书 Webhook。
/etc/logrotate.d/openvpn:
/var/log/openvpn.log {
daily
rotate 14
compress
missingok
notifempty
postrotate
systemctl kill -s USR1 openvpn@server || true
endscript
}
常见坑与现场解法
MTU 黑洞:表现为小文件/命令行还行,大文件卡住。
解法:mssfix 下调到 1360;用 ping -M do 逐步确认。
专线 VLAN Tag 错:对端给的是 VLAN 301,你配成 300。
解法:tcpdump -i eno2 -e 看 vlan 标识,没有就说明打标没生效或打错。
rp_filter 未关:策略路由下返程包被丢。
解法:sysctl 里关 rp_filter,并 sysctl --system。
CRL 过期:客户端全挂,日志里是证书吊销列表过期。
解法:定时 ./easyrsa gen-crl,替换 crl.pem,并重启。
UFW 顺序:先 enable 后再插入 mangle/forward 规则可能被覆盖。
解法:策略路由相关用 systemd 秒级延后执行,或将规则写入 ufw 的 before.rules。
时钟漂移:TLS 握手失败。
解法:chrony 同步国内+国际 NTP 混合源,makestep。
性能短板在单核:OpenVPN 单隧道基本吃单核。
解法:挑高频 CPU;并发多时可拆多实例(端口/隧道分组),或评估迁移 WireGuard(若策略允许)。
专线突发抖动:运营商维护或上游拥塞。
解法:健康检查脚本临时切回国际出口;记录时间点给运营商排障。
变更与回滚
配置即代码:把 /etc/openvpn/、/etc/netplan/、/etc/sysctl.d/、/usr/local/sbin/ 全量进 Git(脱敏)。
快照:LVM 或 ZFS 快照,重大升级前做一次;失败 5 分钟回滚。
灰度:新加 openvpn@server2 在 1195/udp,先迁移 10% 用户观察。
变更窗口:尽量深夜 01:00–03:00;与专线方确认无维护窗口冲突。
安全基线(要点清单)
- 仅开放 1194/udp 与 22/tcp(限制来源段)。
- OpenVPN 用 tls-crypt(隐藏握手特征),强制 TLS1.2+ 与 AES-GCM。
- 客户端一人一证书,定期吊销离职账号,启用 CRL。
- Split Tunnel 最小暴露面,只推必要网段与 DNS。
- 审计与告警到位,连接异常/猜测攻击及时可见。
凌晨四点半,Grafana 的曲线终于稳成了两条贴心的直线:延迟 20ms 左右,抖动在 1ms 内。专线对端的同事发来一句“RDP 不卡了,代码推得动了”。我把最后一条变更记录推到 Git,拔掉机柜门外那杯已经温的咖啡。
运维的意义,往往不在于“造一个看起来很炫的系统”,而是有人在早上九点坐到工位时,根本感觉不到这套系统的存在——一切稳稳地在背后运行。这套香港 Ubuntu + OpenVPN + CN2 的方案,是我在跨境办公场景里反复打磨出来的工具箱。你不必一口气全用上,照着步骤落地,再按自己团队的节奏加一层层的优化和防线。等你哪天也在机房里看着延迟曲线贴着网格线走的时候,你会懂那种“稳”的快乐。
附:一键收敛脚本(示例,按需调整)
⚠️ 务必在测试环境演练后再跑生产,变量请按你的环境改好。
#!/usr/bin/env bash
# hk_vpn_bootstrap.sh
set -euo pipefail
CN2_DEV="eno2.301"
CN2_GW="172.16.30.1"
OFFICE_NET="10.20.0.0/16"
VPN_POOL="10.8.0.0/24"
apt update
apt install -y openvpn easy-rsa ufw fail2ban chrony iperf3 jq
# SSH/防火墙
sed -i 's/^#\?PasswordAuthentication.*/PasswordAuthentication no/' /etc/ssh/sshd_config
systemctl reload ssh
ufw default deny incoming
ufw default allow outgoing
ufw allow 22/tcp
ufw allow 1194/udp
echo "y" | ufw enable
# sysctl
cat >/etc/sysctl.d/99-vpn.conf <<EOF
net.ipv4.ip_forward=1
net.ipv4.conf.all.rp_filter=0
net.ipv4.conf.default.rp_filter=0
net.core.rmem_max=134217728
net.core.wmem_max=134217728
EOF
sysctl --system
# OpenVPN PKI(最简)
mkdir -p /etc/openvpn/pki && cd /etc/openvpn/pki
make-cadir /etc/openvpn/pki
cd /etc/openvpn/pki
./easyrsa init-pki
yes "" | ./easyrsa build-ca nopass
./easyrsa gen-dh
./easyrsa build-server-full server nopass
openvpn --genkey --secret ta.key
# server.conf
cat >/etc/openvpn/server.conf <<EOF
port 1194
proto udp
dev tun
topology subnet
server 10.8.0.0 255.255.255.0
push "route ${OFFICE_NET%/*} 255.255.0.0"
data-ciphers AES-256-GCM:AES-128-GCM:CHACHA20-POLY1305
data-ciphers-fallback AES-256-GCM
tls-version-min 1.2
tls-crypt /etc/openvpn/pki/ta.key
dh /etc/openvpn/pki/pki/dh.pem
ca /etc/openvpn/pki/pki/ca.crt
cert /etc/openvpn/pki/pki/issued/server.crt
key /etc/openvpn/pki/pki/private/server.key
keepalive 10 60
sndbuf 0
rcvbuf 0
mssfix 1400
user nobody
group nogroup
persist-key
persist-tun
status /var/log/openvpn-status.log
log-append /var/log/openvpn.log
verb 3
EOF
systemctl enable --now openvpn@server
# 策略路由表
grep -q '^100[[:space:]]\+cn2$' /etc/iproute2/rt_tables || echo "100 cn2" >> /etc/iproute2/rt_tables
ip route add default via "$CN2_GW" dev "$CN2_DEV" table cn2 || true
iptables -t mangle -A PREROUTING -i tun0 -d "$OFFICE_NET" -j MARK --set-mark 0x1 || true
ip rule add fwmark 0x1 lookup cn2 || true
echo "[OK] Base done. Generate clients under /etc/openvpn/pki with easyrsa."
如果你已经看到这里,基本可以把这套方案原样落地。