OpenVPN 变成服务后 client-config-dir 被忽略
相对路径的 client-config-dir 是按守护进程的工作目录解析的。先读 unit,再逐级检查路径权限,最后在日志里确认查找结果。
隧道起来了,客户端也连上了,但固定地址和 你为那一个用户定义的推送路由都不见了。日志里并没有 “file not found”,正是这一点把人引去重签证书。这类情况大多是因为守护进程的工作目录和你测试时的 shell 不同,而 client-config-dir 从未按你设想的方式解析。要修的是路径,不是凭据。
动配置之前先读 unit
从真正运行的进程入手,而不是你改过的那个文件。向 systemd 询问可执行行、工作目录以及降权后的账号。这三个答案能解释这一类故障的大部分:
systemctl show openvpn-server@server -p ExecStart -p WorkingDirectory -p User -p RootDirectory空的 WorkingDirectory 意味着 unit 在 / 启动,于是相对路径 client-config-dir ccd 会在 /ccd 下查找。这个目录几乎从不存,而守护进程并不把它的缺失当作致命错误。
把它和你测试时的交互式启动对比一下:那时 shell 的当前目录多半就是配置目录。同一个文件、同样的关键字,结果却有两样。
相对路径是相对什么解析的
关键字是相对「使用该选项那一刻」的进程工作目录解析的,并不表示「在我用 --config 传入的配置文件旁边」。OpenVPN 会很乐意从绝对路径读取配置,却仍然在别处解析相对值,这正是这里的陷阱。OpenVPN 2.6 手册在 --client-config-dir 和 --cd 两处都写明了这条规则。
cd 关键字会在读取其他文件之前改变这个上下文,所以先设 cd /etc/openvpn/server 再设 client-config-dir ccd 的 unit 会解析到 /etc/openvpn/server/ccd。如果你加上 cd,要检查指令顺序,并记住它会影响同一文件里其他所有相对引用。
以守护进程的账号走一遍路径
路径可能正确,却依然读不到。降权之后守护进程不再是 root,因此每一级父目录都要给该账号搜索权限,客户端文件本身要有读权限。父目录少一个执行位就足以让文件隐身,而症状只是客户端悄悄拿到默认地址池。
namei 会按顺序打印每一层的权限,比逐个目录检查快得多:
namei -l /etc/openvpn/server/ccd/client1然后以服务账号而不是你自己的身份测试读取:
runuser -u openvpn -- test -r /etc/openvpn/server/ccd/client1 && echo readable如果 unit 用了 chroot,目录必须存在于监狱内部,配置里的路径也是在应用 chroot 之后解释的。放在宿主机监狱之外的文件,从里面是看不到的。
确认查找结果,而不是猜
为一次重启提高日志级别,读一读服务端对「针对单个客户端的选项」说了什么。在 verb 4 级别下,守护进程会报告它导入了哪些按客户端配置,所以缺少那一行就是「这次连接没有读到任何文件」的直接证据。
openvpn --config /etc/openvpn/server.conf --verb 4 --client-config-dir /etc/openvpn/server/ccd在一个空闲端口上前台运行它,并连一个客户端。这样就把 systemd 完全排除在外:如果覆盖项在这里出现、在服务下却不出现,差别就在 unit 的上下文,而不在文件。
还要决定没有匹配文件时该怎么办。没有 ccd-exclusive 时,客户端只带全局地址池设置被放行,这就是前面说的静默失败。加上它,缺文件就变成拒绝连接,这种拒绝更容易被注意到,但在有负载时更难排查。
修正、重启、验证
把配置里的目录写成绝对路径,并保持如此。相对路径只有在 unit 固定了工作目录时才安全,而固定本身又是一个额外的活动部件,迟早会被人删掉。
client-config-dir /etc/openvpn/server/ccd
ccd-exclusive重启实例,重新连接一个已知客户端,读它拿到的地址以及推给它的路由。如果地址与那个客户端文件里的 ifconfig-push 行一致,查找就成功了。如果客户端仍然落进通用地址池,回到日志行数上:零次导入意味着路径或身份不对,一次导入但内容不对,说明你改的文件和守护进程读的不是同一个。
回滚时把上一个 unit 和配置一起恢复,然后再重启。这些操作完全不需要重签证书,而把目录问题当成凭据问题,正是五分钟的修复变成维护窗口的原因。