VPN 会耗尽熵吗?entropy_avail 究竟说明了什么
启动时卡住和已建立的隧道变慢是两类不同故障。在归咎于随机性之前,先读清阶段、计数器的真实含义和队列。
把 VPN 变慢归因于内核随机性耗尽,是个很有吸引力的解释,但对用户报告的症状来说几乎总是错的。等待随机输出的主机会在某个特定时刻卡住,通常是在启动早期或处理密钥期间。而一个连接很快、随后传输很慢的隧道,描述的其实是吞吐量。在为一个还没读完的数字装守护进程之前,请先把这两种观察区分开。
迷思为何长存
两个问题都真实存在,远看也很像。刚启动的虚拟机上确实会出现启动卡顿,而缓慢的既有隧道同样真实。错误在于:因为两边故事里都出现了密码学,就断定后者是前者造成的。加密一词从不意味着每传输一个字节就要消耗一个字节的环境噪声。
这些生成器不是那样工作的。密码学库持有一个带种子的生成器并从中取输出,按自己的策略重播种,所以昂贵的时刻是播种,而不是稳定传输。
生成器对每个数据包做了什么
在 OpenSSL 3 中,默认生成器层级由若干 DRBG 实例构成,它们按配置和状态播种与重播种。在 OpenVPN 2.6 中,AEAD 数据路径用包序号结合每会话的隐式值推导 nonce,而较早的 CBC 路径为每个包取伪随机 IV。两种模式都不支持“搬一吉字节就消耗一吉字节新鲜内核噪声”的规则。两者都支持这样一个想法:真正需要播种的是密钥生成与握手环节。
CBC 这个例子值得点名,因为它像迷思却不是:它每包的 IV 来自库自身的重播种计划所驱动的库生成器,从不来自每包一次的内核调用。当有人引用每字节的熵成本时,先问清他测的是哪个库版本、哪条代码路径。
等待究竟出现在哪里
把时间戳放在阶段两侧,而不是会话两侧:启动、认证、换钥和传输。隧道可用之前的等待,与稳定低速率是两种不同的抱怨,只有一种指向随机性。在 Linux 上,普通的 getrandom() 请求可能一直阻塞到内核随机源完成初始化,这就是该问题“启动版”在虚拟机和嵌入式板卡上真实存在的原因——它们早期可供喂养熵池的东西太少。
| 阶段 | 要运行什么 | 表示等待的读数 | 表示吞吐的读数 |
|---|---|---|---|
| 启动,任何隧道之前 | systemd-analyze critical-chain | 在隧道启动前干等数秒的单元 | 不适用,此时还没有任何传输 |
| 握手 | journalctl -u openvpn-server@server -b | 初始化序列完成前的长时间空档 | 不适用,隧道尚未建立 |
| 已建立的传输 | ss -tin 与 sar -n DEV 1 | 被阻塞、套接字队列为空的进程 | 队列满、重传,且只有一个线程在忙 |
这两条便宜的命令应当放在采集的最前面,它们能在表格还用不上时先解决容易的问题。它们读的计数器是熵的记账,而不是带宽表,单次快照无法显示进程是否被阻塞、等了多久,或它的库调用哪个接口。恢复之后取的样本通常会完全错过卡顿,所以请把内核版本记在读数旁边。
cat /proc/sys/kernel/random/entropy_avail
uname -r让纠正与读数相匹配
如果内核确实在等初始化,就去看平台支持的播种路径,而不是盯着隧道。虚拟机可以分配宿主机提供的随机设备,例如 virtio-rng;镜像准备也很重要,因为原样复制到许多来宾里的种子并不构成独立初始化。如果库在启动后报告播种失败,应检查它配置的随机源与提供者行为,而不是去改内核参数。
ss -tin dst 203.0.113.7
sar -n DEV 1 5OpenSSL 生成器文档说明了这个层级如何取得输入、何时重播种;这比迷思狭窄得多,而且只对那一种实现成立。熵守护进程不是万能提速方案,为了让计数器上升而安装它,等于在没有证明延迟的情况下改变了安全假设。至于吞吐,请沿着数据路径检查:单线程饱和、重传、外部丢包和队列溢出各自需要不同的处理,而缓冲区大小要对付套接字缓冲,需要它自己的队列证据。