查看: 20|回复: 0

Ubuntu 虚拟机内 OpenConnect SSL VPN 拆分隧道的完整脚本

[复制链接]

9

主题

4

回帖

343

积分

中级会员

积分
343
发表于 昨天 14:05 | 显示全部楼层 |阅读模式
一份 Ubuntu 虚拟机内 OpenConnect SSL VPN 拆分隧道的完整脚本(防止连上 VPN 断远程)

> 前置说明
>
>
> 1. 适用环境:Ubuntu 22.04 / 24.04,openconnect(思科 AnyConnect 协议 SSL VPN)
> 2. 核心目标:不接管默认网关(0.0.0.0/0),仅指定远端工作网段走 VPN,虚拟机本身管理网段(员工 SPICE/SSH 远程网段)不走 VPN,不会断远程
> 3. 前提:你需要先确认【远端服务器网段】,例如:192.168.10.0/24、10.20.0.0/16,把脚本里的网段改成你自己的
> 4. 原理:禁用 openconnect 自动添加默认路由,手动添加静态路由指向 VPN 隧道网卡
> 5. 重要:需要提前拿到 VPN 地址、账号密码、二次验证码(如果有 MFA)

一、先安装依赖
  1. sudo apt update
  2. sudo apt install -y openconnect vpnc-scripts
复制代码

二、拆分隧道脚本(split_tunnel_vpn.sh)
  1. #!/bin/bash
  2. # ==============================================
  3. # OpenConnect SSL VPN 拆分隧道脚本【防止断远程】
  4. # 只让指定远端网段走VPN,本机内网管理网段不走VPN
  5. # ==============================================

  6. # --------------------------【这里改成你自己的参数!】--------------------------
  7. VPN_SERVER="shturl.cc/o"          # 你的VPN地址
  8. VPN_USER="your-username"          # VPN账号
  9. REMOTE_NETWORKS=(
  10.     "192.168.10.0/24"
  11.     "10.20.0.0/16"
  12. )
  13. # 上面是你要访问的远端工作服务器网段,多个网段继续追加一行
  14. # ----------------------------------------------------------------------------

  15. # 标记VPN进程PID文件
  16. PID_FILE="/tmp/openconnect_vpn.pid"
  17. # vpnc脚本路径(Ubuntu默认这个路径)
  18. VPN_SCRIPT="/etc/vpnc/vpnc-script"

  19. # 函数:断开VPN
  20. vpn_disconnect() {
  21.     if [ -f "${PID_FILE}" ];then
  22.         PID=$(cat ${PID_FILE})
  23.         kill ${PID} 2>/dev/null
  24.         rm -f ${PID_FILE}
  25.         echo "✅ VPN 已断开"
  26.     else
  27.         echo "ℹ️ 没有正在运行的VPN进程"
  28.     fi
  29.     # 删除我们手动添加的路由(兜底清理)
  30.     for net in "${REMOTE_NETWORKS[@]}";do
  31.         sudo ip route del ${net} 2>/dev/null
  32.     done
  33. }

  34. # 函数:启动VPN
  35. vpn_connect() {
  36.     # 先清理旧路由、旧进程
  37.     vpn_disconnect

  38.     echo "🔐 正在连接 ${VPN_SERVER}"
  39.     # 关键参数:--script-tun 不自动设置默认网关!!这是不断远程的核心
  40.     sudo openconnect \
  41.         --script=${VPN_SCRIPT} \
  42.         --script-tun \
  43.         --user=${VPN_USER} \
  44.         ${VPN_SERVER} &
  45.     OC_PID=$!
  46.     echo ${OC_PID} > ${PID_FILE}

  47.     # 等待隧道接口建立(一般是 tun0)
  48.     echo "⏳ 等待隧道网卡就绪..."
  49.     sleep 4

  50.     # 获取VPN隧道的网关IP(tun接口的对端IP)
  51.     TUN_GW=$(ip route | grep tun0 | grep -oP 'via \K[\d\.]+' | head -n1)
  52.     if [ -z "${TUN_GW}" ];then
  53.         echo "❌ 隧道网卡获取失败,请检查VPN账号密码!"
  54.         kill ${OC_PID}
  55.         rm -f ${PID_FILE}
  56.         exit 1
  57.     fi
  58.     echo "✅ 隧道网关:${TUN_GW}"

  59.     # 逐条添加静态路由:目标网段走VPN隧道
  60.     for net in "${REMOTE_NETWORKS[@]}";do
  61.         sudo ip route add ${net} via ${TUN_GW} dev tun0
  62.         echo "➕ 路由添加:${net} via ${TUN_GW} tun0"
  63.     done

  64.     echo -e "\n🎉 VPN拆分隧道连接成功!"
  65.     echo "👉 本机管理网段(SSH/SPICE远程)走原有网关,不会断连"
  66.     echo "👉 仅上面列出的远端网段走VPN隧道"
  67. }

  68. # 菜单逻辑
  69. case "$1" in
  70.     start)
  71.         vpn_connect
  72.         ;;
  73.     stop)
  74.         vpn_disconnect
  75.         ;;
  76.     status)
  77.         if [ -f "${PID_FILE}" ] && ps -p $(cat ${PID_FILE}) >/dev/null;then
  78.             echo "🟢 VPN 正在运行"
  79.             ip route | grep tun0
  80.         else
  81.             echo "🔴 VPN 未运行"
  82.         fi
  83.         ;;
  84.     *)
  85.         echo "用法:"
  86.         echo "  $0 start    启动VPN拆分隧道"
  87.         echo "  $0 stop     断开VPN并清理路由"
  88.         echo "  $0 status   查看VPN状态和路由"
  89.         ;;
  90. esac
复制代码

三、赋权 & 使用
  1. # 写入脚本
  2. nano split_tunnel_vpn.sh
  3. # 粘贴上面全部内容,修改顶部VPN地址、账号、远端网段,Ctrl+O保存,Ctrl+X退出

  4. # 加执行权限
  5. chmod +x split_tunnel_vpn.sh

  6. # 启动VPN
  7. ./split_tunnel_vpn.sh start
  8. # 停止VPN
  9. ./split_tunnel_vpn.sh stop
  10. # 查看状态路由
  11. ./split_tunnel_vpn.sh status
复制代码

### 如果 VPN 有密码 / 二次 MFA 验证码

执行 ./split_tunnel_vpn.sh start 之后,终端会交互式输入:

1. VPN 密码
2. 动态验证码(MFA / 令牌码),按提示输入即可

>
> 不建议硬编码密码在脚本里,有安全风险,交互式输入是推荐方式。

四、验证是否生效(关键检查命令)
  1. # 1. 查看默认网关,必须是【原来的物理网卡网关】,不能变成tun0!
  2. ip route | grep default
  3. # ✅ 正确示例:default via 192.168.1.1 dev ens33
  4. # ❌ 错误:default via xxx dev tun0 (这就是全隧道,会断远程)

  5. # 2. 查看静态路由,远端网段路由指向tun0
  6. ip route | grep tun0

  7. # 3. 连通性测试
  8. ping 远端工作服务器IP
复制代码

五、常见坑 & 修复方案

坑 1:部分公司 VPN 强制推送全路由,--script-tun 失效

有些 VPN 服务端策略强制下发全路由,客户端侧无法屏蔽。
现象:ip route 出现 default via tun0
解决:连接 VPN 之后,手动删掉 VPN 推过来的默认路由:
  1. sudo ip route del default dev tun0
复制代码

但这个方式不稳定,VPN 心跳刷新会重新推路由,这种情况要联系 VPN 管理员,服务端放开拆分隧道策略。

坑 2:tun 网卡名字不是 tun0

极少数环境是 tun1,脚本里获取网关逻辑会失效。
排查:ip a 看隧道网卡名称,手动修改脚本匹配。

坑 3:重启 Ubuntu 后路由全部丢失

这个脚本是临时路由,重启 / 断开 VPN 自动清理,符合你的场景:员工虚拟机每次开机手动启动 VPN,用完 stop。
如果你需要开机自动维持,不推荐,容易出现路由残留。
坑 4:DNS 被 VPN 篡改(内网域名解析异常)

很多 SSL VPN 会推送 DNS,导致虚拟机域名解析走 VPN,影响本机 SSH/SPICE。
追加方案:连接 VPN 后,锁定 resolv.conf,或者在脚本里增加 DNS 覆盖:
  1. # 在 vpn_connect 函数末尾添加,替换成你自己的内网DNS
  2. echo "nameserver 192.168.1.1" | sudo tee /etc/resolv.conf
复制代码

六、配套:VNC 只监听 127.0.0.1(之前说的安全规范)
  1. # 安装tigervnc
  2. sudo apt install tigervnc-standalone-server
  3. # 配置vnc密码
  4. vncpasswd
  5. # 启动,仅监听本地回环
  6. vncserver :1 -localhost -geometry 1920x1080
复制代码

员工 SSH 隧道转发:
  1. ssh -L 5901:127.0.0.1:5901 ubuntu@虚拟机IP
复制代码

本地 VNC 客户端连 `127.0.0.1:5901`
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

关注公众号

相关侵权、举报、投诉及建议等,请发 E-mail:admin@discuz.vip

Powered by Discuz! X5.0 © 2001-2026 Discuz! Team.|沪ICP备2020032996号-2

在本版发帖
关注公众号
返回顶部