一台旧设备出现,连不上,看起来最快的修法就是把整个主机削弱到连接成功为止。这笔交易在当时看不出来,实践中却会永久保留,因为没有人会撤掉一个终于可用的下限。压力来自两种截然不同的故障,其中只有一种与密码套件有关。先给故障分类,再把例外放到无法扩散的地方。

改指令之前,先把故障拆成两类

服务器日志里说没有共享密码套件,或协议版本不受支持,这类行来自控制通道:两端无法就如何保护协商本身达成一致。客户端日志里说没有启用任何服务器证书验证方法,这类行来自完全不同的地方,因为那个客户端根本没有检查是谁在应答。改数据密码套件对第二种情况毫无作用,加角色检查对第一种情况也毫无作用。

取每一侧最早的那条失败行,而不是断开前的最后一行,因为最后一行通常只是结果。然后确认客户端实际打开的是哪个文件:导入的配置文件常常以不同显示名存在两份,改那份没人读的副本会制造出很有说服力的“没变化”。正在运行的进程保留它启动时的设置,所以保存过的文件不等于已加载的文件。下面的命令把客户端的协商过程与服务器对同一次尝试的看法并排显示。

openvpn --config <profile>.ovpn --verb 4 2>&1 | head -n 40
journalctl -u [email protected] --since '-10 min' | grep -iE 'cipher|tls|verify'

更弱的下限应当放在第二个监听器上

当设备在数据通道为 2.5 之前、只能到 TLS 1.0 时,服务器必须提供一个更低的控制通道下限,以及旧客户端认得的数据密码套件。回退指令正是为这类对端准备的,而密码套件列表让现代客户端留在现代套件上。这两条指令属于监听器,而不属于某个用户或某条连接,之后也没有任何按客户端设置的选项能收窄它们。所以主端口上的一台旧设备,是关乎所有共用该端口的对端的决定。

在平台允许的地方,把旧监听器作为独立实例跑在单独的端口上,只允许设备地址访问那个端口,主服务保持不动。把第二个监听器为何存在写进承载它配置的文件里,否则日后清理会把它当成残留物。只有当更弱的下限止步于设备所在之处,这个拆分才值得。

tls-version-min 1.0
data-ciphers AES-128-GCM:AES-256-GCM:AES-256-CBC
data-ciphers-fallback AES-256-CBC

旧提供商证书真正破坏的是什么

验证错误其实是缺少角色绑定:没有它,客户端会接受任何能链到受信任 CA 的证书,无论那张证书是否为服务器签发。先把证书绑定到服务器角色,并把绑定当成要求而不是调节旋钮。只有在这一步之后仍然失败的配置文件,才存在真正的证书问题。

旧提供商证书的密钥长度或签名算法不再满足 OpenSSL 默认策略时,才属于在控制通道上有限度地降低安全级别的情形。在那里降级别并不会关闭验证,而且绝不能与空 CA、关闭验证或未校验主机名组合使用。把这一行留在唯一需要它的配置文件里;把放宽的 OpenSSL 配置全局导出,等于把同样的放宽交给机器上的每个程序。

remote-cert-tls server
tls-cipher "DEFAULT:@SECLEVEL=0"

读协商出的值,而不是启动横幅

在 verbosity 4 下,两端都会打印它们商定的控制通道 TLS 版本与密码套件,以及会话的数据密码套件。要读这些行,而不是初始化横幅,横幅报告的是成功,而不是成功的条件。然后把现代客户端重新连到主监听器,确认它仍然落在现代套件上,因为泄漏到共享配置里的例外会最先在这里显现。你抄下来的值会成为下一次复核的基线。

单独测试旧设备:它应当能连上,而且应当是日志中唯一显示更弱参数的会话。如果第二个对端也在协商旧套件,说明例外比预想的更宽,监听器拆分实际上没有发生。两边的观察都要记录,而不只是结论。

每个例外都需要负责人和日期

没有移除条件的例外会悄悄变成默认值,所以把设备、负责人、原因以及它消失的日期,放在它所辩护的那份配置旁边。没有这一行,更弱的下限会比需要它的硬件活得更久。

追求的结果应当是换掉客户端,或者向提供商索取当前配置;例外只是拖延,不是设计。设备退役时,在同一次变更里移除监听器和配置文件,让任何一半都不再继续运行。这一切都不会让旧参数变安全,只是防止一台设备的极限变成所有人的基线。

相关文章