这个报错指向 .ovpn 文件的第一行,并在本该是 client 的位置引用了一串看起来像乱码的字符。文件在你的编辑器里打开完全正常,第一行读作 client,指令本身也毫无特别之处。这两种观察同时成立,因为编辑器在解码字节,而客户端没有。你遇到的几乎总是编码问题,而不是语法问题。

误区:编辑器看到的就是文件内容

文本编辑器用自行识别或猜测的编码把字节解码成字符。当它猜成 UTF-16 时,文件在屏幕上看起来完美无缺,而底下的字节其实是成对的,每个拉丁字母后面都跟着一个零字节。OpenVPN 却把这些字节当作字符来读,于是不一致就出现在它尝试解析的第一条指令上。

所以屏幕上显示的行和实际交付的行是两回事。删掉你看到的那些奇怪字符只改变了显示,并没有改变编码,下一次解析仍会因为同样的原因失败,只是报错略有不同。这就是为什么顺着报错里的那串字符追下去总是没有结果。字节在源头就已经错了,下游的每个读取者都在猜同一段损坏的序列。

这种故障的具体形态值得记住,因为它有一种特定的诊断价值。问题出现在文件最开头,而这份配置在别的机器上一直能用,那它问的是字节序,不是词汇量。

开头那几个字节到底说了什么

去读字节,而不是读字符。两条小命令几秒钟就能定论,而且都不需要把文件放进可能改动它的程序里:

file client.ovpn
head -c 24 client.ovpn | od -An -tx1

要找的模式是一对起始字节,后面跟着每隔一个字节就是零的文本。FF FE 表示 UTF-16 小端序,FE FF 表示大端序,EF BB BF 表示带字节序标记的 UTF-8。普通的无标记 UTF-8 文件直接从第一个字母的字节开始。

字母之间夹着零字节就是线索。在小端序 UTF-16 里,client 这个词占十二个字节而不是六个,因为每个 ASCII 字符后面都跟着一个零。这种结构编辑器的界面显示不出来,删掉两个字节也去不掉。

这里有个重要的细节:OpenVPN 的配置读取器确实会跳过文件开头的 UTF-8 字节序标记,2.6 版配置读取器就是这样。这种容忍只针对那一个标记,对于字母被零填充的 UTF-16 文件毫无作用。把两种标记当成同一个问题,就是这个误区的后半段。

转换是另存为,不是删除

动手之前,先保留一份运营方原件的未改动副本,并在单独的文件上操作。配置文件里可能含私钥或账户资料,所以这份副本既是你的退路,也是你的参照。

然后做一次真正的编码转换:要么用编辑器里明确的编码菜单,要么用会声明输入、输出编码的转换工具:

iconv -f UTF-16 -t UTF-8 client.ovpn > client-utf8.ovpn
file client-utf8.ovpn

要验证结果,而不是相信命令的退出状态。转换应当保留每一条指令,而且必须在自己的标记内部逐字节保留任何内联的证书或密钥块。重新折行 PEM 数据、改动块内的行尾、或在转换过程中重新排版配置,都会在修好旧问题的同时制造新问题。

让客户端或服务指向转换后的文件,看它报出的第一个错误。如果乱码的字符消失了,编码这件事就算做完,之后如果再失败就是另一个问题了。

修好编码修不了什么

编码决定文本怎么被读取;它并不说明这些选项对你面前的客户端是否有效。为另一种实现导出的配置可以解析得很干净,却仍然因为当前运行的版本不支持某个选项、或某条指令的参数在那个客户端里另有含义而被拒绝。

后面这些错误值得单独查:确切的拼写、参数的语法,以及接受它的版本。不要把解析成功当成配置可用。

查的时候请留在本地。公开的在线转换服务和粘贴出来的 hex 转储,对一份可能含私钥的文件来说是很糟的去处,因为编码问题好修,泄露的私钥不好修。

相关文章