OpenVPN 3 Linux:先读懂 D-Bus 错误,再回滚版本
五个服务,一个前端。把 D-Bus 错误对应到拒绝它的组件,确认会话是否存在,然后在那道边界上修正。
OpenVPN 3 Linux 的故障以一条 D-Bus 方法错误的形式到来,后面挂着一个陌生的名字,而人的冲动是回滚软件包然后祈祷。客户端并不是一个程序:一个命令行前端通过总线与若干服务对话,而这些服务分别掌管配置文件、会话、VPN 引擎以及需要特权的网络改动。判断失败调用属于这五者中的哪一个,就是诊断的大部分工作,代价只是一条命令,而不是一次回滚。下文就是这个判断,按故障通常出现的顺序排列。
一条命令背后的五个所有者
关键的是五个进程,各自拥有自己的总线名:接受你命令的前端、保存配置文件及其访问策略的配置管理器、掌管会话生命周期的会话管理器、通过 OpenVPN 3 Core Engine 运行连接的后端,以及安装接口、路由与 DNS 设置的网络配置服务。因此,一条点名配置对象的错误不是后端错误,而一个起不来的后端也不是认证失败。把消息归入其中一行必须在任何改动之前完成,因为后面每一步都取决于哪一行才是真的。猜行,正是让一个小修补变成一下午折腾软件包的原因。
先问系统总线上有哪些服务,再问别的事情;然后确认已安装的部件在版本上是一致的。OpenVPN 3 Linux 把这些服务注册为系统单元,所以要查询的是系统总线,版本查询需要管理权限,而用 --user 列出的内容读的是你的登录会话,那里永远不会出现这些单元。服务按需激活,有些在空闲时会退出,所以在你第一次请求之前就缺失的服务,只是还没有被问过任何问题。下面三条命令在不打开 VPN 会话的情况下回答了存在性、版本与单元状态。
sudo busctl list | grep -i openvpn
sudo openvpn3-admin version --services
systemctl list-units 'openvpn3*'每份列表实际证明了什么
客户端自己的视图是能得到的最便宜的证据,而每一份都证明得比看上去少。配置列表里的一个配置文件,说明某个配置文件被导入了,却完全没有说明是否有人被允许用它连接。会话列表里的一个会话,说明会话对象存在,却完全没有说明远端认证是否完成。
openvpn3 configs-list
openvpn3 sessions-list这些输出里的对象路径指向总线上被管理的对象,而不是文件,所以它们不能被编辑;先前记录的路径会在该会话断开的那一刻失效。照笔记里抄一个来用,会产生与 VPN 毫无关系的 "no such object" 错误。下面的阶段表把连接的每一步建立了什么、又留下了什么分开,而回滚决定恰恰建立在这个区分上。先读与你证据相符的那一行,再重读错误原文。
| Stage | What exists | What proves it | What it does not prove |
|---|---|---|---|
| Profile imported | A configuration object held by the configuration manager | The profile is listed by the client | That the profile owner permits a connection |
| Session requested | A session object held by the session manager | The session appears in the session list | That the remote side accepted the credentials |
| Connect dispatched | An asynchronous attempt on the VPN backend | Status changes arrive for that session | That an interface was configured |
| Tunnel configured | An interface, routes and DNS settings | The network service reports success and routes appear | That traffic reaches the peer |
连接调用在隧道存在之前就返回了
连接调用交给你一个可以观察的会话就返回了,所以一次看似成功的调用之后陷入沉默,是正常的中间状态,而不是矛盾。前端仍然必须跟踪状态变化,并回答任何交互请求。一个需要凭证却收不到回答的配置文件,会停在看起来和卡死后端一模一样的状态里。这个等待状态值得被认出来,因为它把人引向错误的组件。
尝试进行期间,观察会话列表和前端自身的日志,针对那个配置文件。一个从不离开初始状态的会话,就点名了停住的那个组件,而日志通常在前几行就把它点出来了。把配置文件名放在手边,因为日志命令接受的是配置文件而不是会话路径,这也是要重读列表而不是复用路径的又一个理由。
openvpn3 sessions-list
openvpn3 log --config <profile> --level 6不打开会话也能读一个组件的日志
当前端报不出有用东西时,去 记录下这次失败的组件,而不是再开一次连接。配好 systemd-journald 记录器之后,管理员作用域内的日志视图能在不触碰总线策略、也不打开会话的情况下读取单个组件的事件,这一点很重要,因为会改动被测系统的诊断本身证明不了什么。按单元而不是按 syslog 标签过滤:各个服务单元并不保证共享同一个标识,所以按标签查询可能返回空页,而单元本身其实已经记下了失败。
向任何人求助之前先记下四个字段:错误的完整名字、涉及的组件与对象、时间戳,以及已安装软件包的版本。这些正是一个维护者需要的,其中没有一项要求把配置文件或凭证粘进工单。为了消音而放宽总线访问策略不是诊断,它删掉了那道原本替你点名组件的边界。配置文件的私密内容不进入共享报告,理由和凭证一样。
journalctl -u 'openvpn3-service-*' -n 50 --no-pager
openvpn3-admin journal --service <name>修正拒绝的那道边界
映射表里每一行都有自己的修正,而行与行之间不能互相替代。针对配置文件的被拒操作是策略与归属问题,而一个不激活的后端是服务与打包问题。拒绝认证的服务器两者都不是,所以反复改密码修不好本地的激活问题。选错修正,就是策略问题变成重装的过程。
更换软件包之后,要验证一套一致且受支持的安装,而不是把一个旧二进制拷回原位。受控回滚是对已确认回归的正当回答,前提是各软件包保持兼容,并且存在回到受支持维护的路径。要在服务停止启动之前把那条路铺好,而不是之后。
别的平台可以共用同一个 VPN 核心库,却不共用 Linux 的总线服务,所以 Windows 上看起来相似的故障说明不了这个故障。D-Bus 概览 记录了为配置与会话暴露的对象路径,而这正是本诊断所依赖的那部分架构。读一遍,下一次错误到来时就已经带着一个组件了。