共享 CA,各开各门:每个准入文件对应一项服务
通过共享机构校验的证书不是通往每项服务的门票。准入应放在每项服务自己的文件里,由守护进程读取。
一张对共享机构校验通过的证书,通过的是信任检验,而不是成员资格检验。这两件事常常被合并,因为都发生在连接时,而这种合并一直隐形,直到某个本不该在服务上的人被放了进去。修法不是第二套机构,而是第二个判断:按服务作出,并记录在守护进程会读取的位置。把两者分开,这个领域的大部分混乱就会消失。
可信链并不能说明服务成员资格
两项服务可以接受同一套签发层级,却拥有不同的人群。一台笔记本属于前者,不得进入后者,而同一层级下的其他客户端确实属于后者。把机构从该服务上移走会锁死所有合法使用者,所以这项工作属于准入,而不是信任。
不妨想象一扇门上有两把锁。证书打开外锁,按服务的判断打开内锁,二者不能互相替代。只有外锁的服务根本没有执行成员资格,它只是在认证。改任何东西之前,把两个判断都写下来,因为只以连接能通形式存在的策略无法审计。以下讨论的是内锁。
把可选覆盖变成准入闸门
客户端配置目录通常只是一种便利:里面的文件会覆盖它所指向的客户端设置,而没有文件的客户端就直接按服务器默认值运行。一条指令能把这种便利变成闸门,于是缺少文件就意味着连接被拒绝。
client-config-dir /etc/openvpn/service-a/clients
ccd-exclusive
社区版的 OpenVPN 2.6 ccd-exclusive 在其手册页中恰好把这一点记为条件,而按名查找文件取决于客户端实际生效的身份。其他构建和产品托管的服务器可能不同,因此在别处依赖同样语义之前,先确认已安装的版本。
把上面的路径当作示例,去检查实际部署的布局,而不是照着某篇指南凭空建一个目录。现有的证书校验、服务器身份校验和账号认证都保持原样,因为这段配置只是增加一个条件,并没有删掉任何东西。上线前先安排变更,因为拒绝若发生在错误的环节,看起来就像证书故障,会把排查引向机构方向。之后再确认:被拒客户端的日志行指向的是缺失的配置文件,而不是证书链。
每项服务一个目录,而不是一份共享名单
成员资格属于执行它的那项服务,因此每项服务各自保留自己的目录和文件。把两项服务指向同一份名单,等于让它们共享成员资格不管有没有人这么打算,由此产生的变更也很难回溯到某个决定。共享机构可以继续共享,准入文件不行。把它们分开也意味着,一项服务里的错误不会悄悄扩大另一项。
DEFAULT 条目正是容易误导人的情形。它会在找不到具名文件时提供兜底设置,但它并不是闸门所要求的具名文件,所以单靠它无法放行客户端。用一个完全没有条目的客户端测试,确认它被拒绝;如果它被放行,说明兜底正在生效,而ccd-exclusive在那个实例里并不生效。
实际生效的身份决定读取哪个文件
查找使用的是客户端实际认证所用的身份,通常是证书的通用名。如果服务器还校验用户名和密码,并把该名称映射到通用名,那么文件必须改为匹配已认证的用户名。一旦不匹配,目录里就会塞满看似合理、却永远不会被选中的文件名。
读取是另一半。守护进程通常在启动后放弃特权,因此降权后的账号需要对目录及其每一项的读权限。客户端不得能通过任何可写路径创建自己的条目,否则自助准入会彻底架空这套机制。要检查挂载与共享权限,而不只是文件模式,因为对一个账号关闭的目录,对另一个账号可能是开放的。然后确认运行中的进程真正使用的是哪个账号,而不是 unit 文件里提到的账号。只有没有任何其他因素改动它们时,这两者才相同。
分别观察拒绝、准入与吊销
试一个属于该服务的身份,确认它能连上。试一张没有条目的有效证书,确认它被拒绝。试一个已被另一项服务准入的身份,确认它在这里不被准入。把服务实例和生效身份记在每个结果旁,因为没有这层上下文的结论别人无法复现。
撤回与吊销是两项不同的操作。编辑准入文件只移除一项服务的权限,身份在其他地方仍然有效;而被泄露的密钥需要组织层面的吊销响应。提前决定已经建立的会话怎么办,因为改目录和吊销名单本身都不会结束它们。
准入也只是第一道闸门。加入一项服务,并不能说明它背后的子网、数据库或管理接口如何,那套资源策略必须单独编写并测试。