两份配置,一份能连上、一份连不上,快速看出原因的办法就是 diff。麻烦在于,同一个 diff 会把内联私钥、静态密钥以及文件携带的任何凭据块一并打印出来,而人们往往还没意识到就已经把输出贴进了聊天窗口。先决定要比较什么,再运行命令。请在受保护的副本上比较结构与角色,需要给别人看时再单独做一份已脱敏的文件。

.ovpn 文件究竟携带什么

一个 .ovpn 文件是带有可选内联块的配置。它可能包含 CA 证书、客户端证书、私钥、tls-auth 或 tls-crypt 密钥,以及指向外部文件或要求输入凭据的指令。两份文件可能看起来几乎一样,差别只在某个块内部,或只在一行彻底改变传输方式的指令上。行数并不能告诉你究竟发生了哪一种。

密钥块内部的差异并不是任何事情的公开证据。重新签发的证书和换了新密钥的证书可能产生同样形状的变化,所以块不匹配只说明材料不同,并不说明是哪个决定造成的。把它当作要比较字段的信号,而不是结论。

按角色比较,而不是按行比较

按照连接本身的顺序走:先看端点和传输,再看信任与预期身份,再看用户认证,再看路由、DNS 和脚本钩子。这个顺序与客户端的行为一致,所以你遇到的第一个差异,就是最可能弄断连接的第一处。按字母顺序比较反而会把相关设置打散到整个输出里。

一次关注字段的检查会按这个顺序看这些项目。客户端交互式询问的内容属于认证那一行,而不属于信任那几行。

  • remote、proto 与端口,包括每个连接块。
  • ca 与 cert:签发者、主体和到期日期。
  • 是否存在私钥,而绝不是它包含什么。
  • tls-auth 或 tls-crypt 是否存在以及密钥方向。
  • 认证指令,包括对凭据文件的任何引用。
  • 连接时运行的路由、DNS 选项和脚本钩子。

当文件包含多个连接块时,把每个选项与其端点放在一起。只比较第一行 remote 可能漏掉客户端在该网络上实际选择的备选。

为什么普通 diff 是错的工具

普通 diff 是为文本而不是为秘密设计的。它会乐于把私钥打印进回滚缓冲,也不会告诉你眼前这条指令与故障无关。字段提取既更安全也更好读,因为证书材料可以按元数据而不是按字节来比较。

openssl x509 -in ca.crt -noout -subject -issuer -enddate
openssl x509 -in client.crt -noout -subject -issuer -enddate
grep -E '^(remote|proto|port|tls-auth|tls-crypt|route|dhcp-option)' profile.ovpn

前两条命令只打印身份与有效期,不碰密钥,所以输出可以安全粘贴。grep 只打印指令名和值,从不打印块的内容,因此当你怀疑传输或路由差异时,它是正确的第一遍。如果必须比较块本身,就比较它的哈希而不是内容。

为工单制作一份脱敏副本

去掉真正的秘密和不必要的标识,同时保留字段名和足以解释差异的结构。同样形状的占位标记仍会透露材料的位置和长度,所以要判断连这一点是否可接受。检查最终的附件本身,而不是相信你记忆中运行过的命令。不要为了做示例而改动正在使用的原件,因为脱敏后的配置未必可执行,也绝不应再次导入。

用文件名和哈希让原件保持可识别,而不是把它们复制到三个名字相似的目录里。一串哈希就足以证明检查过哪个文件,即使泄露也毫无代价。变更关闭后清理副本,让下一个人只继承一份权威文件。

让比较始终与一次故障挂钩

只有当比较回答的是某一次导入或某一次连接故障的问题时,它才值得付出成本。先说出失败的步骤,然后只比较该步骤依赖的字段;把客户端拥有的所有凭据收集到一个目录里,会让下一次泄露更可能发生、下一次诊断更慢。问题越窄,能回答它的字段清单就越短。

如果新文件来自运维人员,在替换信任材料之前先确认它的分发路径。变更之后,检查客户端实际选中的已导入副本和协商结果,因为另一版客户端可能忽略某条指令或以不同方式解读它。磁盘上的文件并不是连接运行过的证据。指令语义见 OpenVPN 2.6 手册.

相关文章