NetworkManager 导入 OpenVPN 和 WireGuard 文件失败
通常坏掉的是解析器,而不是配置。检查插件、读取导入后的连接、修正 secret 标志和路由,然后测试重启。
我在 GNOME 设置里选择了从文件导入…,挑了一个在厂商客户端下可用的配置,对话框却关掉了,列表里什么也没多。第二台笔记本上导入完成了,连接以配置的名字出现,随后在第一次认证提示时失败。NetworkManager 从不原样运行你的 .ovpn 或 wg-quick 文件。它把文件交给解析器,解析器把它转换成 NetworkManager 自己的连接格式,此后隧道都基于这个解析后的对象构建——密钥、路由、DNS 等等。所以有用的问题不是文件为什么坏了,而是解析有没有发生、它保留了哪些内容。
导入到底对文件做了什么
OpenVPN 的解析器位于插件包中,它创建的连接指向 org.freedesktop.NetworkManager.openvpn 这样的 D-Bus 服务。没有插件就没有解析器:nmcli 不认识这个类型,图形化的添加 VPN 列表甚至可能根本不显示导入项。WireGuard 有一点不同——NetworkManager 从 1.30 系列起就自带 WireGuard 设置,因此没有单独的插件包,但 wg-quick 读取器只存在于随附它的构建中。
无论哪种方式,文件只被读取一次。结果写成 keyfile,放在 /etc/NetworkManager/system-connections/ 下,以连接命名,归 root 所有,因为含密钥材料而对其他用户不可读。这个 keyfile 现在就是连接;之后再编辑原始 .ovpn 不会有任何改变。
三种症状对应三个阶段。没有导入项或类型未知,说明插件或版本缺失。导入报错并点名某个指令,说明解析器读了文件但拒收了一部分。连接存在却从不激活,说明解析出的对象不完整,或者服务端在拒绝它。动手修改之前先判断你属于哪一种。
排查配置之前先确认插件
缺少插件看起来和损坏的配置一模一样,所以先检查软件包。在 Debian 和 Ubuntu 上,OpenVPN 支持以 network-manager-openvpn 加一个桌面辅助程序的形式提供;Fedora 上是 NetworkManager-openvpn 和 NetworkManager-openvpn-gnome;Arch 上是 networkmanager-openvpn。
dpkg -l network-manager-openvpn-gnome | tail -n 2 rpm -q NetworkManager-openvpn pacman -Q networkmanager-openvpn
然后用绝对路径导入:nmcli connection import type openvpn file /home/you/profile.ovpn。相对路径通常也能用,但切过目录的 shell 会把一次快速导入变成绕路。对 WireGuard,检查 nmcli --version,并用 modprobe wireguard 确认内核侧存在;在没有该模块的机器上导入成功,只会留下一个设备永远不出现的连接。nmcli 参考记录了 import 子命令和可接受的类型名。如果 type wireguard 被直接拒绝,说明这个构建早于该功能,正确的做法是装发行版软件包或回移植,而不是把配置复制到随便一个目录里指望有人去读。
读 OpenVPN 连接,而不是 .ovpn
连接建立后,去读解析后的设置,而不是重读文件:nmcli connection show "Work VPN" | grep -E '^vpn\.'。你需要 remote 和端口、传输方式、身份材料——ca、cert、key、tls-auth 或 tls-crypt——以及认证方式。带私钥口令的配置,加上 sudo nmcli --show-secrets connection show "Work VPN" 再看 secret 标志:0 是 none,密钥就在 keyfile 里;1 是 agent-owned;2 是 not-saved,即从不写入 keyfile;4 是 not-required,即完全不需要密钥。它们是位,不是刻度,所以 3 就是 agent-owned 加 not-saved。每次会话问一次的密钥是 agent-owned (1),由代理去索要;没有单独的标志位编号。标志不匹配是配置在桌面小程序里能连、在脚本或启动时却失败的常见原因。
看解析器丢了什么,最快的办法是让插件把文件写回去,然后做 diff:nmcli connection export "Work VPN" /tmp/roundtrip.ovpn。在 diff 里消失的指令,连接时就不存在。钩子通常是嫌疑人——script-security、up、down 和 route-up 期望以提权方式运行脚本,因此被有意丢弃。不要为了导入成功而降低校验或授予额外权限;不受支持的选项通常反映插件的功能范围,而不是服务器不可达。把这种行为移到 NetworkManager 的 dispatcher 脚本或服务端策略里。
还要确认身份字段活了下来:CA 引用、remote-cert-tls,以及配置依赖的任何名称检查。没有它们也能起来的连接,可能在和那个地址上随便什么应答者说话。
读 WireGuard 连接,而不是 wg-quick 文件
wg-quick 文件是带 shell 语义的格式;导入后的连接是一组设置。PrivateKey 变成接口私钥,Address 变成一个或多个 IPv4 和 IPv6 地址,DNS 映射到 NetworkManager 的 resolver 属性,MTU 映射到 wireguard.mtu(wg-quick 自己的默认值是 1420),每个 peer 块变成一个 peer 条目,含公钥、endpoint、允许的 IP 和 persistent keepalive。用 nmcli --show-secrets connection show wg0 打印存下来的内容;如果 peer 列表没出现在输出里,就读 keyfile:
sudo sed -n '/^\[wireguard\]/,/^\[/p' /etc/NetworkManager/system-connections/wg0.nmconnection
活不下来的是 wg-quick 的 shell 那一半。PreUp、PostUp、PreDown、PostDown 和 Table 是那个工具的行为,不是 WireGuard 接口的属性,所以导入后没人执行它们。如果原文件用 Table = off 让默认路由保持不动,较新的 NetworkManager 为同样目的提供了 ip4-auto-default-route 和 ip6-auto-default-route;请显式设置它们,而不是假设连接会像脚本那样表现。推送自定义路由的钩子需要把那条路由加进连接的 routing 设置,否则它就会缺失。
让连接只有一个所有者
经典的后续故障是:连接能用,直到下次重启就开始循环。通常是 [email protected] 还开着,来自早先的试验,或者厂商客户端在启动自己的守护进程,两个所有者都在为同一个目的地写路由和 DNS。NetworkManager 走自己的路径设置 resolver,wg-quick 通过 resolvconf 写入,最后写的赢——于是名字解析能用,然后停,然后又能用。检查 systemctl status wg-quick@wg0、nmcli connection show --active 和 ip link show type wireguard,然后禁用第二个所有者,而不是去调它的 metric。
如果你确实两个都需要,就给它们单独的接口和互不相交的允许 IP,并让其中恰好一个安装默认路由。两条 metric 不同的默认路由不是分离隧道;那是一场由第二个启动的服务决定的竞赛。重连之后要再检查一遍实际状态——一次性的手动 resolver 修改让事情能跑,并不能证明导入已经完整。
用流量验证,并扛过一次重启
用 nmcli connection up "Work VPN" 拉起连接,然后去读状态、地址和路由,而不是相信图标:nmcli device show wg0 | grep -E 'GENERAL.STATE|IP4.ADDRESS|IP4.ROUTE'、ip route show default,以及真正查询一个只在隧道内解析的名字。握手或 ping 成功只能证明加密可用;它对转发、分离隧道前缀或私有 DNS 一无所知,而这些恰恰是经常坏掉的部分。
然后重启并重复。autoconnect 行为、被重命名的接口、在设备之后才加载的插件,都是只有干净启动时才看得见的故障。journalctl -u NetworkManager -b --no-pager | tail -n 40 显示守护进程在这次启动中对这个连接做了什么。给第二台电脑,复制 keyfile 并保持 root 所有、权限 600,或者对 OpenVPN 用 nmcli connection export 再重新导入生成的 .ovpn——导出路径在 WireGuard 没有对应物,所以那个方向要么是文件复制,要么是从受保护的原始文件重新导入。
简短版,按顺序:确认插件或 NetworkManager 版本,用绝对路径导入,读解析后的设置(含 secret 标志),把 OpenVPN 导出与原始文件对比,检查 WireGuard 侧的钩子和路由,禁用任何第二个所有者,然后重启后验证名字解析和路由表。当一条连接通过这些,启动它的配置文件就可以回到受保护的存储里。