上一篇 下一篇 分享链接 返回 返回顶部

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

发布人:Minchunlin 发布时间:2025-09-11 08:36 阅读量:1019


我坐在香港机房的 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;与专线方确认无维护窗口冲突。

安全基线(要点清单)

  1. 仅开放 1194/udp 与 22/tcp(限制来源段)。
  2. OpenVPN 用 tls-crypt(隐藏握手特征),强制 TLS1.2+ 与 AES-GCM。
  3. 客户端一人一证书,定期吊销离职账号,启用 CRL。
  4. Split Tunnel 最小暴露面,只推必要网段与 DNS。
  5. 审计与告警到位,连接异常/猜测攻击及时可见。

凌晨四点半,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."

如果你已经看到这里,基本可以把这套方案原样落地。

目录结构
全文