远端网关重启,一分钟内就回来了,而本地 strongSwan 守护进程仍在运行,隧道却还是断的。用 cron 任务 grep ESTABLISHED 然后重启守护进程,最终会掩盖这个问题,同时还会打断机器上所有无关的隧道,而且可能刚好漏掉出问题的那一条。恢复应当属于那条必须回来的连接,用重载后依然生效的设置来表达。本手册针对单条连接完成这件事,并按名称验证结果。

不再全局重启,保留证据

重启守护进程有两个具体缺陷:它破坏每条连接的状态,而且完全不告诉你到底是哪个事件失败了。你 grep 到的状态文本可能来自另一条连接,而真正需要的 child 因为没有任何东西触发它而闲置。

在动手之前先读已加载的连接和活动的关联,这样你手上有变更前的画面,等 peer 回来后再对比。

swanctl --list-conns
swanctl --list-sas

把发起和重试放到要恢复的那条连接上

在 swanctl.conf 部署中,这些设置位于连接和 child 层级,而不是全局策略里。下面的片段是发起方一侧的示例,它假设 proposals、认证和 selectors 已经正确。

connections {
    branch {
        keyingtries = 0
        dpd_delay = 30s
        children {
            private-lan {
                start_action = trap|start
                dpd_action = restart
            }
        }
    }
}

start_action 决定 child 加载时发生什么:start 立即发起,而 trap 安装一条策略,在出现匹配流量时触发发起。写 trap|start 需要 strongSwan 5.9.6 或更高版本,所以在照抄这一行之前先确认版本。dpd_action=restart 会在 peer 被判定为死亡后请求重新协商,连接级的 keyingtries=0 则允许继续尝试,直到成功或遇到永久错误。在 IKEv2 下 dpd_timeout 完全没有效果,因为 peer 是否无响应由普通的重传决定。

那个示例里的延迟只是起点,不是通用时限。请根据 peer 重启需要多久以及 你愿意忍受多少重传。

弄清楚你在从哪个事件中恢复

三种故障在仪表盘上看起来一样,却需要不同的设置。从未起来的初始连接是配置或可达性问题。停止应答的 peer 是 DPD 的情况。peer 明确关闭的 child 是带自己 close_action 的策略事件,DPD 从来观察不到它。读一下故障前后的日志,就知道你遇到的是哪一种,而答案会改变修复方式。

不要给运维人员为了维护而主动关闭的连接加上激进的重新发起,否则隧道会在维护窗口中间重新出现。对于按需触发的设计,保留 trap 就够了,因为匹配的流量会在没有任何定时器的情况下重建 child。

按名称验证,而不是看守护进程状态

在 peer 还没回来时按名称读取连接和它的 child,回来后再读一次;两次输出的差异就是证据。

swanctl --initiate --child private-lan
swanctl --terminate --ike branch

然后确认的是服务,而不是关联。新的 child 安全关联,加上对隧道后面私有目的地的一次成功请求,才是关键的两个事实;而守护进程在运行、另有一条无关的健康隧道,这两点都证明不了。

如果 child 从不发起,检查测试流量是否与配置的 selectors 完全一致,包括协议和端口。如果它发起后失败,就去看认证或 proposal 的错误,而不是把它藏在无限循环后面,因为 keyingtries=0 会心甘情愿地永远重复一个永久性拒绝。

在维护窗口里加载变更并重新测试

重载比重启守护进程更安全,但它依然不是免费的:替换或卸载 child 可能关闭已经建立的关联,或者丢掉它的 trap。

在维护窗口里加载,保留上一个文件,并确认连接带着你想要的设置出现,而不是默认值。

swanctl --load-conns
swanctl --list-conns

回滚时恢复上一个文件并再次加载,然后重复同样的两条命令。只有重启守护进程之后才回来的隧道并没有被修好,而是被重置了。

相关文章