Systemd 与 NetworkManager:开机时究竟是谁拉起 VPN?
三种机制都能拉起同一个 Ubuntu 配置,但触发时机各不相同。把它们放在一起比较,只保留一个负责方,把重复的那条关掉。
两台来自同一镜像的 Ubuntu,同一份 VPN 配置,登录时的结果却相反:一台在桌面出现之前隧道就已建立,另一台则永远起不来。这里的变化因素不是配置。差别在于三个组件中究竟是哪一个被告知去启动它,而这三者并不共用一个开关。
把它们摆在一起比较比猜测更快。每一种失败都有可辨认的特征,这正是比较值得做的原因。
三种触发方式,三种启动者
打包的 openvpn 服务是最古老的一条路。它的 init 脚本会从 /etc/default/openvpn 读取自动启动列表。若该列表被注释掉或留空,脚本启动的就是该目录下它能找到的每一份配置,而不是你添加的那一份。为一个隧道添加的一个文件,可能悄悄拉起好几个。
| Starter | What triggers it | Typical failure |
|---|---|---|
| Init script and templated units | Boot, plus anything in the autostart list | Starts every config, not the one you meant |
| NetworkManager secondary | Activation of the base Wi-Fi or Ethernet profile | Nothing happens until that base link comes up |
| Hand-written unit or wrapper | Whatever its WantedBy and ExecStart say | Runs a different binary or environment than your shell |
第二行是人们最容易配错的那个。NetworkManager 保存着导入的配置并且会启动它,但只是作为另一条连接的第二连接,也就是说触发条件属于网络链路,而不属于 VPN。
比较搭建成本与适用范围
init 脚本这条路只花一行,却覆盖机器上的每一份配置。对路由器合适,对需要在不同网络间切换的笔记本就不合适。NetworkManager 这条路要花一次 UUID 查询,并把隧道绑定到某一条基础连接上。它在公司 SSID 上会起来,在酒店网络里则一直不启动。
手写 unit 成本最高,换来的控制也最多,包括相对于其他服务的顺序。决定因素是适用范围。如果机器只有一个网络、一条隧道,打包服务既诚实又可预期。如果隧道应当跟随某个特定的 Wi-Fi,三者之中只有第二连接能不做额外脚本就表达这个条件。
选定一个负责方,把其余的关掉
由两个负责方启动的配置,会产生每次开机都不一样的时序,这比只用其中任何一种都糟。先选定负责方,然后有意识地而不是偶然地关掉竞争的那条路径。
# who is configured to start something?
grep -v '^#' /etc/default/openvpn
systemctl list-units 'openvpn*'
nmcli -g connection.secondaries connection show 'Work Wi-Fi'
# NetworkManager: attach the tunnel to the base link by UUID
TUNNEL=$(nmcli -g UUID connection show 'Corp Tunnel')
nmcli connection modify 'Work Wi-Fi' +connection.secondaries "$TUNNEL"
如果要退役的是打包路径,顺序要正确:把 AUTOSTART 设为 none,执行 systemctl daemon-reload 让生成的 unit 读到新列表,然后再停用你不再需要的模板实例。跳过重新加载会让旧 unit 继续生效,看起来就像这次改动毫无作用。
停不下来的停止是另一个问题,答案也是有边界的。TimeoutStopSec 限定 systemd 在发出 SIGKILL 之前等多久。合适的位置是 drop-in,因为直接改打包的 unit,升级一次改动就丢了。
核实真正起来的是哪一个
重启,然后去读证据,而不是相信配置文件。拥有这条隧道的 unit 会显示 active 状态和与开机时间对得上的启动时间戳,VPN 接口也会在你登录之前就存在。
systemctl is-enabled openvpn-client@office
systemctl status openvpn-client@office --no-pager
nmcli -f GENERAL.STATE,connection.secondaries connection show 'Work Wi-Fi'
ip -brief link show tun0
这里还该多查一项:确认 unit 的 ExecStart 指向的确实是你以为的那个可执行文件。本地编译的客户端或 snap 包往往需要显式路径或 snap run 包装,因为 systemd 不继承你 shell 的搜索顺序。把 command -v openvpn 和 unit 文件对一下,几秒钟就能定论。
把选定的负责方写进机器的备注,就写在配置名旁边。下一个人排查隧道不出现时,会从这一行开始,而不是把整套比较再做一遍。