你的网关会接受一张它从未见过的客户端证书,因为配置里写的是受信任的签发者,而证书在握手中途才到达。协议本来就是这么设计的,这也正是服务器上的目录列表算不上好清单的原因。签发者知道自己签发过什么,服务器知道谁连上来了,吊销状态知道哪些被撤回了。把这三类记录混在一起的清单,最后会断言一件它们谁都没说过的事。

网关信任的是签发者,而不是文件列表

校验会把出示的证书与信任锚做比对,所以同一签发者昨天新建的身份,即使从未在服务器磁盘上出现过,也能完成连接。这一切不需要复制任何文件,也不需要有人手工批准。因此服务器自己的记录描述的是到达,而不是可能到达的身份总数。

反过来的缺口同样尴尬。目录里可能躺着过期证书、已吊销证书,以及为从未部署的设备签发的身份。数文件只能回答有几个文件,而这是个没人问过的问题。不如从有含义的记录开始。

四个问题,四种不同的记录

每类记录都只擅长回答一个问题,对其余问题都不擅长,所以把它们合并起来的报告看起来完整,却在某个具体方面是错的。下表按它们能解决什么问题来排序,而不是按产生它们的系统来排。

Question you are answeringWhere the answer livesHow quickly it changesWhat it will never show
Was this identity ever created?The issuer's issuance databaseOnly when something is issuedWhether the device still exists
Is a copy sitting on this host?The paths you actually inspectedWhenever anyone copies filesCertificates that were never copied here
Has the issuer withdrawn it?Published revocation stateWhen the issuer next publishesWithdrawals that are not published yet
Is it connected right now?The live session tableContinuouslyAnything about past or future sessions

公开证书的归档不是秘密,因为它不含私钥,但也不适合外传。主体和设备名会暴露运行细节,报告应当只留在需要它的人手里。私钥完全不在清单范围内,因为清单需要的是元数据,而不是凭据。

签发者加序列号才是唯一稳定的键

主体会被回收,文件名会被重复使用,所以去年标识某台设备的名称,如今可能标识另一台。两者都不是键,建立在其中任何一个之上的报告,最终会把两个身份并成一行。

签发者与序列号这一对才能唯一标识一张证书,而这两个字段都来自证书本身。把它们连同主体、有效期日期、副本来源以及负责人或设备一起记录下来。把吊销状态和部署状态作为独立字段另记,并各自带观测时间,因为处在有效期内的证书并不会自动获得授权。这两件事分属不同系统,把它们合并会掩盖这样一种情况:身份已被撤回,而它的文件看上去仍然有效。

一次只读一张证书

通配符展开整个目录算不上批量接口,它还隐藏了哪个文件产生了哪段输出。元数据要逐张证书去读,吊销一侧要从它自己的产物里读。OpenSSL x509 参考列出了那些选项会打印的字段。

openssl x509 -in <cert>.pem -noout -issuer -serial -subject -dates -fingerprint -sha256
openssl crl -in <issuer>.crl -noout -text | head -n 40

对于受管理的 Easy-RSA PKI,要通过工具支持的工作流去查询签发数据库,而不是假定某种目录结构,并在整理清单期间把原件保持为只读。台账格式足够规整,不用手工打开数据库,也能数出有效条目并列出被撤回的条目。

grep -c '^V' pki/index.txt
grep '^R' pki/index.txt | cut -f4,5

能扛过下一次签发的清单

目录的一次性导出在下一张证书签发的那一刻就过时了,所以报告要从签发流程里生成,并让它作为日常运行的一部分自动更新。这同时让报告可复现,在两个人对签发内容有分歧时这一点很重要。

把结果用于续期和退役决策,而不是当成表格练习。删除本地副本并不会让已经存在的远端副本失效。通过配置好的吊销与授权流程撤回该身份,然后连同日期记录这次撤回。从服务器上消失、但签发者仍然信任的身份,并没有真正退役,正是这一点区分了准确的清单和仅仅让人安心的清单。

相关文章