看到密码无效的提示,通常会得出一个结论:VPN 不喜欢标点符号。这个结论通常是错的,因为该消息往往来自本地处理,而任何网关都还没看过账户。两种设计传递同一个密码,图形输入框和凭据文件,它们在不同地方失败。在把本来没问题的密码简化之前,先比较这些容器。

错误发生在网关之前

本地校验、被锁定的密钥库、缺失的代理以及格式错误的凭据文件,都会产生用户感受到的同一次拒绝。它们都与认证服务器无关。网关自身的拒绝是另一回事,通常伴随不同的消息和不同的时机。它发生在隧道已经存在、凭据已经传输之后。

所以第一个问题不是哪些字符合法,而是这次尝试停在了哪里。从未打开过连接的客户端没有被任何人拒绝。把你观察到的阶段记下来,因为后续每一步都取决于这个分类。

这个区别也改变了由谁来修。容器问题属于设备及其所有者,而账户被拒绝属于运行认证后端的人。

比较容器,而不是字符

图形密码框、钥匙串条目、两行凭据文件和自动化密钥,都以不同的信封承载同一个值。信封有自己的规则,而这些规则通常才是出问题的地方。值本身很少是问题;值的传输才是。把这两个概念分开,诊断就会更短。

Where the value stopsWhat you seeWhat you checkWho owns the fix
GUI field or managed profileA validation message before any connection opensThe selected authentication mode and whether the field holds the secret or the usernameThe device owner or the profile author
Keychain or secret agentA prompt that keeps reappearing, or an agent errorWhether the agent is unlocked and holds an entry for this profileThe user who owns the keychain
Two-line credential fileA prompt for a password that was already suppliedLine count, line order and whether a trailing newline is presentWhoever generated the file
Shell argument or environmentWorks interactively but fails in automationWhether the shell expanded, trimmed or logged the valueWhoever wrote the automation

Community OpenVPN 2.6 客户端选项 描述了 auth-user-pass 使用的那个两行文件,包括密码行缺失时的行为:客户端会转而提示输入。这个细节解释了一类它向我要一个我已经提供的密码的报告。它也告诉你,空的第二行是一种有意义的状态,而不是无害的格式选择。

引号又是另一层:普通凭据文件里的引号会作为数据保存,而 shell 会在程序看到任何内容之前把它们去掉。同一个字符在一个地方无害,在另一个地方却有结构意义。表格无法告诉你客户端用了哪种容器,所以在选择某一行之前,先读客户端自己的配置。

换行同样是结构性的。以行分隔字段的文件无法承载包含换行的值,这是格式的限制,而不是对密码强度的判断。请使用部署所支持的投递机制,而不是为了凑合一个临时文件去裁剪密钥。

在不让 shell 破坏的前提下生成凭据文件

最安全的本地做法是让密钥不出现在 shell 历史和进程参数列表中。以交互方式读取两个字段,然后写入客户端期望的那两行。其余一切都可以留在便于审阅的配置文件里。

read -r VPN_USER
read -rs VPN_PASS
printf '%s\n%s\n' "$VPN_USER" "$VPN_PASS" > /etc/openvpn/client/creds
chmod 600 /etc/openvpn/client/creds
openvpn --config /etc/openvpn/client/office.conf --auth-user-pass /etc/openvpn/client/creds

前两条命令先读取用户名,再在不回显的情况下读取密码;第三条写入这两行;第四条限制文件权限;最后一条用该文件启动客户端。注意 read -r 会保留反斜杠,这正是省略后会把一个能用的密码变成失败密码的那类细节。

避免用未加引号的变量或充满 shell 展开的 here-document 来生成文件。两者都可能在字符到达文件之前改变它们,由此产生的失败看起来像 VPN 拒绝,因为磁盘上的密码确实与你所知道的密码不同。如果你怀疑这一点,请比较文件的字节长度与预期值,而不要打印密钥本身。差一两个字节正是引号事故的特征。

从第一条错误读出阶段

保留一次尝试中的第一条具体消息,然后对它分类。本地校验错误、缺失的私钥、不可用的密钥代理以及网关认证失败,是四种不同的结果,有四位不同的负责人。只有其中一种与账户有关。

如果网关确实在拒绝一份投递正确的凭据,请与管理员一起检查账户、认证后端以及任何必需的挑战。通用的认证失败不会指出被禁止的字符。

通过正确的机制恢复已批准的值,而不是把它缩减成字母和数字。密码策略属于运营方,而悄悄绕过它的变通办法并不是修复。它还会向后来者隐藏真正的缺陷,那个人会遇到同样的错误,却用着更弱的密码,也没有更好的主意。

相关文章