暂停一个 OpenVPN 客户端:为什么吊销是错误的杠杆
杀掉会话是可逆的;吊销证书不是。先判断这个场景需要哪根杠杆,只有合适时才重建 CRL。
暂停一个客户端的访问与永久移除它看起来是同一件事,直到你试图撤销其中一项。吊销证书会写下一条服务器视为最终的记录,要逆转它就得手工修改签发者的数据库。丢弃活动隧道只是一条管理命令,不改变任何信任关系。选择杠杆,要问密钥是否仍然可信,而不是问请求显得有多紧急。
这种信念:吊销和暂停是同一回事
一名承包商的访问被暂停一周,第一反应就是吊销,因为吊销听起来像是把东西关掉。它看起来整齐,只要十秒钟,而且把改数据库留成唯一的回头路。这个反应可以理解,但它更接近烧掉护照,而不是关上一扇门。被暂停的设备和已泄露的密钥需要不同的处理,只有前者能便宜地撤销。
区别在于凭据,而不在于人。仍掌握在正确手里的密钥可以暂停再恢复,不会损失任何信任。可能被复制过的密钥则永远不该被恢复,因为恢复等于断言副本并不存在。
在触碰任一系统之前,先把答案写下来。如果你说不出密钥是否已暴露,保守的答案就是更换。
用管理接口丢掉活动会话
OpenVPN 提供一个管理接口,可以列出会话并按通用名称杀掉其中一个。把它在 localhost 上打开,让它远离公网接口,在会话必须立刻消失时发送杀掉命令。这条命令只关闭那个隧道,其他客户端照常工作。
这是对正在发生的问题的响应,不是暂停策略。同一个配置文件一分钟内就能重新连上,因为它的凭据没有任何变化。当会话必须离开、而长期决定还在讨论时,用它。
杀掉之前先确认会话确实连着,免得追查一个从未连上的客户端。下面的状态命令会打印当前连接的东西。
printf 'status 2\nquit\n' | nc 127.0.0.1 7506
printf 'kill kiosk-14\nquit\n' | nc 127.0.0.1 7506如果杀掉命令报告没有这样的客户端,那就说明证书里的通用名称不是你键入的那串字符。去读状态输出,而不是配置文件的名字。
CRL 到底控制什么
服务器并不查看证书里存的吊销标志。它在每次新连接时读取由 crl-verify 指定的证书吊销列表,并拒绝序列号出现在该列表中的证书。由此有两个后果,两者在运维上都很重要。文件必须对服务器降权后使用的账号可读,而且已经连上的客户端会继续运行,直到它重新连接。
第二个后果在事故现场常常让人意外。吊销证书并不会关闭正在使用它的隧道。如果会话必须立刻结束,就把吊销和一次管理接口的杀会话配对,并把两者当作运行手册里分开的两步。
检查列表,看看服务器到底会强制执行什么,而不是相信你上次复制文件的时间戳。真正重要的是序列号的条数。
openssl crl -in /etc/openvpn/server/crl.pem -noout -text | sed -n '1,14p'
ls -l /etc/openvpn/server/crl.pem没有条目的列表和有正确条目的列表在目录列表里看起来一模一样,所以值得把序列号条数打印出来。权限那一行同样重要,因为守护进程读不了的文件会制造出一种看起来像客户端被拒绝的故障。
恢复一条记录,要小心
恢复证书意味着把签发数据库里的一条记录反转过来,并发布一个新的列表。easy-rsa 3 把该数据库保存为 PKI 目录下的 index.txt,而更早的布局用的是另一种安排。一行以状态字母和到期日期开头。已吊销的行会在继续写序列号、证书文件名和可分辨名称之前,插入一个吊销时间戳。把一行放回去,就是删掉那个时间戳并翻转字母,其余一切都别动。
先备份文件,然后把改动写成脚本,而不是在文本编辑器里手动输入。一个改变了一行有多少字段的笔误,会破坏此后新增的每一张证书的签发。这就是要自动化改动、并在动手之前复制文件的原因。
同一个名字的第二次吊销也常常失败,因为第一次运行已经把那本证书和它的密钥移出了工具预期能找到它们的目录。于是工具找不到被要求吊销的东西,而错误信息并不总是把原因说清楚。
cp /etc/easy-rsa/pki/index.txt /root/index.txt.$(date +%F-%H%M)
grep -c '^R' /etc/easy-rsa/pki/index.txt
easyrsa gen-crl发布新列表,修正它的属主,让服务器读它。在多数安装上重载就够了,但确认必须来自一个全新的连接,因为现有会话从来没有再检查过任何东西。
判断这个场景配得上哪根杠杆
问那个能决定整份手册的问题:私钥是否仍然只在它该在的地方。如果是,杀掉会话加一次记录在案的暂停就能覆盖这个场景,不留任何永久记录。如果不是,唯一安全的答案是换新密钥,让旧证书保持吊销状态。
然后用一次连接尝试来验证结果,而不是看配置差异。差异告诉你原本想做什么,连接告诉你服务器到底在强制什么。
openssl crl -in /etc/openvpn/server/crl.pem -noout -text | grep -c 'Serial Number'
printf 'status 2\nquit\n' | nc 127.0.0.1 7506当恢复的客户端连上并出现在状态输出里,说明记录改动生效了。当被吊销的客户端被拒绝、日志点名 CRL,说明吊销生效了。两者之间的一切都是权限或重载问题,值得在下一个请求到来之前修好。