Logo for Sunlight

Let's Encrypt 很自豪地推出 Sunlight,这是一种新的证书透明度日志实现,我们从头开始构建,并牢记现代 Web PKI 的机遇和约束。我们与 Filippo Valsorda 合作,他领导了设计和实施,我们纳入了来自更广泛的透明度日志社区的反馈,包括 Google 的 Chrome 和 TrustFabric 团队,Sigsum 项目,以及其他 CT 日志和监控运营商。他们的见解对塑造项目的未来方向至关重要。

CT 在 Web PKI 中扮演着重要角色,增强了监控和研究证书颁发的能力。但是,随着证书数量的增加,CT 日志的运行面临着越来越大的挑战。例如,Let's Encrypt 每天签发超过 400 万张证书,每张证书都必须记录在两个独立的 CT 日志中。我们完善的“Oak”日志目前保存着超过 7 亿个条目,反映了这些挑战的重大规模。

在这篇文章中,我们将探讨 Sunlight 背后的动机以及其设计如何旨在提高 CT 生态系统的稳健性和多样性,同时还提高 Let's Encrypt 日志的可靠性和性能。

数据库瓶颈

Let's Encrypt 从 2019 年开始就 运行公共 CT 日志,我们积累了大量运行它们的运营经验,但并非一帆风顺。我们在为“Oak”日志部署的架构中面临的最大挑战是,数据存储在关系数据库中。我们已经通过将每年的数据拆分为一个“分片”并为每个分片分配一个单独的数据库来 扩展了它,后来又将分片缩小到覆盖六个月而不是一整年。

将数据拆分为越来越多的数据库的方法不是我们希望永远持续下去的方式,因为运营负担和成本会增加。目前 CT 日志分片的存储大小在 5 到 10 太字节之间。这对单个数据库来说已经足够大,足以引起关注:我们之前曾有一个测试日志因遇到 MySQL 中的 16TiB 限制而失败。

向上扩展读取容量需要具有快速磁盘和大量 RAM 的大型数据库实例,这些实例并不便宜。我们已经多次遇到 CT 日志因尝试读取日志中的所有数据而超载的客户端而超载,从而使数据库超载。当实施速率限制以防止超载时,客户端被迫缓慢地爬行 API,降低了 CT 作为快速检测错误颁发证书的机制的效率。

提供切片

最初,Let's Encrypt 只计划构建一个新的 CT 日志实现。但是,我们与 Filippo 的讨论让我们意识到,其他透明度系统已经改进了原始证书透明度设计,我们可以通过更改读取路径 API 来使我们的日志更加稳健和可扩展。特别是,Go 校验和数据库 受证书透明度的启发,但使用更有效的格式将其数据发布为一系列易于存储和缓存的切片。

证书透明度日志是一棵二叉树,每个节点都包含其两个子节点的哈希值。叶子级别包含日志的实际条目:证书,追加到树的右侧。树的顶部经过数字签名。这形成了一个称为默克尔树的加密可验证结构,可用于检查证书是否在树中,以及树是否是追加式的。

Sunlight 切片是包含 256 个元素的文件,每个元素要么是特定树“高度”的哈希值,要么是叶子级别的证书(或预证书)。Russ Cox 在他的博客上对 切片如何工作 做出了很好的解释,或者您可以阅读 Sunlight 规范的相关部分。即使是我们目前运行的 CT 实现 Trillian,也 使用类似于这些切片的子树系统 作为其内部存储。

与以前 CT API 中的动态端点不同,将树作为切片提供不需要任何动态计算或请求处理,因此我们可以消除对 API 服务器的需求。由于切片是静态的,因此它们可以有效地缓存,这与 get-proof-by-hash 等 CT API 形成对比,get-proof-by-hash 对于每个证书都有不同的响应,因此没有共享缓存。叶子切片也可以存储压缩,节省更多存储空间!

将日志公开为一系列静态切片的理念源于我们希望水平扩展读取路径并降低成本的愿望。我们可以直接在云对象存储(如 S3)中公开切片,使用缓存 CDN,或使用 Web 服务器和文件系统。

对象或文件存储唾手可得,可以轻松扩展,并且成本远低于云提供商的数据库。这似乎是前进的明智之举。实际上,我们已经 在现有的 CT 日志前面安装了 S3 支持的缓存,这意味着我们目前正在存储两份数据。

运行更多日志

切片 API 改进了读取路径,但我们还想简化写入路径上的架构。使用 Trillian,我们运行一组节点以及 etcd 用于领导者选举,以选择哪个节点处理写入。这有点复杂,我们相信 CT 生态系统允许不同的权衡。

关键的认识是,证书透明度本身就是一个分布式系统,客户端将证书提交给多个日志,并在任何不可用的日志中优雅地故障转移到其他日志。每个日志的写入路径不需要高度可用的领导者选举系统。一个简单的单节点写入器可以满足 CT 日志 计划 所需的 99% 服务级别目标。

单节点 Sunlight 架构使我们能够使用相同数量的计算能力运行多个独立的日志。即使每个日志的潜在正常运行时间都较低,这也提高了系统整体的稳健性。不再需要领导者选举。我们使用简单的比较和交换机制来存储检查点,并防止意外同时运行两个实例,这会导致树分叉,但这种机制的开销远低于领导者选举。

不再有合并延迟

CT 的目标之一是将提交到日志的延迟限制在最小范围内。名为合并延迟的设计功能被添加进来以支持这一点。将证书提交到日志时,日志可以立即返回已签名证书时间戳 (SCT),并承诺在日志的最大合并延迟内(通常为 24 小时)将证书包含在日志中。虽然这似乎是一个很好的权衡,可以避免减慢颁发速度,但已经发生了多次事件,并且几乎发生了多次事件,日志停止运行而未合并的证书,错过了最大合并延迟,并且违反了承诺。

Sunlight 采用了一种不同的方法,在日志中批处理和集成证书时,会保留提交,从而消除合并延迟。虽然这会导致延迟略微增加,但我们认为,为了避免最常见的 CT 日志故障情况之一,这种延迟是值得的。

它还使我们能够将最终的叶子索引嵌入到我们的 SCT 的扩展中,使 CT 更接近于直接客户端验证默克尔树证明。扩展还使客户端能够从新的静态切片式 API 中获取日志包含的证明,而无需服务器端查找表或数据库。

阳光明媚的未来

今天宣布 Sunlight 仅仅是开始。我们已经发布了 软件规范,并且正在运行 Sunlight CT 日志。前往 sunlight.dev 查找开始使用的资源。我们鼓励 CA 开始将测试提交到 Let's Encrypt 的新的 Sunlight CT 日志,以及 CT 监控器和审核员添加对使用 Sunlight 日志的支持,以及 CT 计划考虑信任运行此新架构的日志。我们希望 Sunlight 日志能够在未来由浏览器运行的 CT 计划用于 SCT,从而使 CA 能够依赖它们来满足浏览器的 CT 日志记录要求。

到目前为止,我们收到了积极的反馈,例如,“Google 的 TrustFabric 团队(Trillian 的维护者)支持这个方向和 Sunlight 规范。我们一直在努力为其他生态系统(使用 无服务器工具)实现相同的可缓存切片式日志目标,并将将其整合到 Trillian 和 ctfe 中,并添加对 Sunlight API 的支持。”

如果您对设计有任何反馈,请加入 ct-policy 邮件列表 上的讨论,或者在 transparency-dev Slack (邀请 加入) 的 #sunlight 频道中。

我们要感谢 Chrome 支持 Sunlight 的开发,以及 Amazon Web Services 对我们 CT 日志运营的持续支持。如果您的组织监控或重视 CT,请考虑进行经济上的支持。在 https://www.abetterinternet.org/sponsor/ 了解更多信息,或通过以下方式联系我们:sponsor@abetterinternet.org