用按配置文件的探测维持多个 vpnc 隧道
随包提供的重连脚本只假设一条隧道。为每个 vpnc 配置文件分配独立端口,探测真实服务,并把探测串成一条固定顺序。
一个 vpnc 配置文件在漫游后能干净地重连;三个则会轮流破坏彼此的套接字、路由和解析器状态。随包提供的重连脚本是为单条隧道写的,所以只要其中任意一条掉线,它就会重启所有配置文件。要修好这一点需要三样东西:独立的本地端口、为每个配置文件探测真实服务,以及一条绝不并行执行两次连接的固定链。
给每个配置文件分配独立的本地端口
留在默认本地端口上的 vpnc 配置文件会与已在运行的配置文件抢占。第一条连接占着端口时,第二条就会失败,而日志行看起来像网关问题,而不是本地冲突。改为每个配置文件写一行:
Local Port 0零让内核挑选临时端口,因此两条配置文件可以同时建立。按其所服务的网关和分组为每个文件命名,只保留 root 可读,并且 始终用匹配的配置文件连接,而不是共用的默认配置。共用默认文件正是有人在重命名配置文件后,脚本却重连错隧道的原因。
配置文件里带着凭据,所以权限属于设计的一部分,而不是打扫工作。/etc/vpnc 下人人可读的文件会把分组密钥交给每个本地用户。把模式保持在 0600 并由 root 拥有,让脚本以 root 身份读取文件,而不是把密钥复制进参数。这也是探测脚本绝不该把文件内容写进日志的原因。
每个配置文件探测一个服务,而不是隧道地址
向隧道自身的地址发回声请求,多半只证明接口存在。它说明不了其后的主机是否还能到达那条让隧道成立的应用。接口可以是在位的,而其后的路由早已消失。
用较短的 TCP 超时为每个配置文件探测一个服务,只重连探测失败的那一个。两秒足以区分死路径和慢路径,而 TCP 探测不依赖沿途某处是否放行 ICMP。配置文件之间只改动目标和配置名:
#!/bin/bash
set -eu
if ! nc -z -w 2 10.63.7.20 8443; then
/usr/sbin/vpnc-connect ops.conf
fi把它保存为 /usr/local/bin/ensure-vpn-ops.sh,赋予可执行权限,并对每个配置文件重复,只改探测目标和配置名。回答协议问题的探测,能挺过那些丢弃回声却仍转发用户所关心服务的网关。
把探测串成链,使其永不竞争
随包提供的重连脚本可以按顺序调用你的探测,而顺序正是关键。两个 vpnc-connect 调用同时运行,会以无人控制过的顺序重写路由表,健康的配置文件就是这样丢掉路由的。
让调用保持在前台,为下层接口留出短暂的稳定等待,并从 root 的 cron 表调度这条链,因为只有 root 能重写隧道路由:
#!/bin/bash
sleep 20
/usr/local/bin/ensure-vpn-ops.sh
/usr/local/bin/ensure-vpn-lab.sh让启动脚本和 cron 条目保持完全相同的顺序,因为启动时以不同顺序运行的链实质上就是另一个程序。当某个配置文件依赖另一个的路由时,把依赖放在前面,并让后一个探测在前一条隧道还在启动时无害地失败。
看住日志,然后故意弄坏一条
每次运行记录一行,包含配置文件、探测目标、结果和所采取的动作。没有它,每分钟抖动的隧道看起来和从未掉线的一模一样,探测缺陷也无法与网关中断区分。日志还能证明这条链只碰了失败的那个配置文件。把它写到重启后仍在的地方,否则证据会随进程一起消失。
然后故意弄坏一条隧道,观察会发生什么。停掉单个配置文件,确认哪些进程和路由还在:
pgrep -a vpnc
ip -4 route show | grep -E '10[.]63|10[.]90'
tail -n 5 /var/log/ensure-vpn.log只有被停掉的那个配置文件应当被重连,其他会话应当同时保住进程和路由。如果有两个配置文件被重启,说明你的链在某处并行运行了。
看门狗不该做的事
一出问题就重启一切的看门狗,在测试时看着健康,却会在维护窗口造成中断。按配置文件重启,绝不全局重启。
当网关为同一协议提供受维护的客户端时优先使用它,绝不把密码写死在脚本里,也不在命令行上传密码,因为配置文件已经保存了密钥。如果隧道建立后某项任务需要以普通用户运行,应在脚本内部降低权限,而不是让整条链都在那个账户下运行。最后,重连之后要重读路由,因为配置文件可能带着另一个分配地址回来,留下一条陈旧的走法,而任何瞄准隧道地址的探测都永远不会注意到它。