服务器还是客户端:双角色 Pi 上该用哪个 OpenVPN 单元
两个 OpenVPN 实例共享一台 Pi:一个接受漫游用户,另一个向上游提供商拨号。重启其中任何一个之前,先读单元名和目录。
一台 Pi 上运行着两个 OpenVPN 进程,它既接受漫游用户,又连接到上游提供商,而两者除硬件外几乎不共享任何东西。一个监听入站隧道,另一个向外拨号。各自读取自己的目录,也各有各的故障模式。重启错的那个,是客户端修复反而把监听端搞乱的最常见方式。
把单元名与它加载的目录对应起来
Upstream 2.6 提供两个模板单元:[email protected] 和 [email protected]。服务器模板在 /etc/openvpn/server 中启动,并从那里加载 %i.conf。客户端模板在 /etc/openvpn/client 中启动。名为 roaming 的实例可以同时存在于两处,既是服务器又是客户端,拥有两份互不相关的配置,除了这个字符串之外彼此毫无关联。
去问 systemd,而不是文件系统,因为打包者会加入 drop-in,可能改变工作目录或二进制文件收到的参数。
systemctl list-units --type=service 'openvpn-*@*' systemctl cat [email protected] systemctl show -p WorkingDirectory -p ExecStart [email protected]
第一条命令列出所有已实例化的单元,包括失败后仍保持加载的那些。第二条打印单元文件及其任何覆盖。第三条是看清决定这个问题的两个事实的最快方式:进程启动所在的目录,以及传给它的配置路径。
按数据包方向判断,而不是按接口名判断
服务器实例拥有监听套接字并接受连接。它的加密传输在 Pi 上是本地输入与本地输出,而它替用户转发的流量走转发路径,遵循另一套规则。客户端实例在那个意义上没有监听者:它向对端拨号,所以它的加密传输是朝底层网络的本地输出。
这个差别告诉你一条规则该放在哪里。匹配转发链内接口的规则只放行路由已经选定的流量;它从不选择路由。当漫游用户的数据包从错误的路径出去时,要检查的链是 forward,而不是 output。
先看套接字,再谈理论。绑定到 udp/1194 的服务器会显示监听套接字。客户端显示的是与提供商地址的关联,完全没有监听。
ss -lunp | grep openvpn ss -unp state established '( dport = :1194 or sport = :1194 )'
一个实例的默认路由可能截走另一个实例的应答
上游客户端推送的 redirect-gateway 会安装一条默认路由,与机器上其他一切竞争,包括服务器发给漫游用户的应答。OpenVPN 会为自己的对端地址留出例外,好让隧道保持可达,但这个例外对酒店网络里的手机毫无说法。
症状通常是不对称的。握手完成,客户端报告已连接,而流量在第一个应答包之后就死了。基于源的策略路由,或用 --mark 在服务器实例上打的包标记,能把两股流量分开;两者各自都不是完整的规则集,而且都需要在它们所选的表里拥有有效路由。
要测试本地决策,而不是隧道状态,因为内核在 OpenVPN 看到数据包之前就已选定源地址和接口。
ip route get 203.0.113.44 ip rule show
重启一个角色,并证明另一个还活着
当两个角色依赖同一个底层网络时,顺序很重要。如果必须先有上游路由才能转发用户流量,就先重启客户端;如果你只改它的监听者或地址池,就先重启服务器。无论如何之后都要确认两个单元,因为失败的重启可能让另一个角色带着过时状态继续运行。
改动之前先写下停止条件:失去上游隧道是挡住漫游用户,还是回落到 Pi 自己的上行链路?DNS 和 IPv6 也需要明确答案,因为被推送的解析器往往比让它有用的那条路由活得更久。
systemctl restart [email protected] systemctl is-active [email protected] journalctl -u [email protected] -n 20 --no-pager
只有当三件事同时成立时才接受这次改动:新的入站连接完成、被转发的应用经预期出口到达目的地、两个进程仍然报告各自预期的对端。三者中有一个失败,就说明你改错了实例或改错了路由,下一步是重读单元,而不是去重启别的东西。