第 12 章 / 共 51 章
卷二 · 配置与服务装配

证书管理:从 PEM 到热替换

TLS 证书是配置里最容易配错的部分——错得静默尤其危险。XServer 对证书的每一道检查都给出点名诊断,并且支持不停机换证书。

本章导读

站位:证书三件事——格式对(12.2)、配对对(12.3)、能更新(12.4)。本章所有错误样例都来自 func_test 的 TLS 诊断矩阵,可复现。

12.1 引入

证书问题的传统体验是"握手失败,原因不明":也许是文件路径错、也许是格式坏、也许是 key 不配 cert、也许是链不全。XServer 的立场是把每一种失败变成点名的诊断——启动期能查的全部在启动期查,运行期更新走原子替换。这背后是自研 TLS 栈的优势:解析、匹配、分发全在自己手里。

12.2 PEM 严格解析

私钥文件按三种 PEM 标签解析,规则是严格配对:

  • BEGIN/END 标签必须成对计数——"expects exactly one supported PEM block"
  • 多个块或残缺的尾块都不会被静默忽略,启动失败并指明文件
  • 支持的三种私钥标签覆盖主流工具(OpenSSL 系)的输出形态

证书链同样在装配期构建完整(带回溯帧栈的链构建),链不完整不会等到握手时才暴露。

12.3 算法族匹配诊断

func_test 的 TLS 诊断矩阵里最经典的一条用例:把 PKCS#8 RSA 私钥的 PEM 类型声明改成 EC——期望的错误信息必须双向命名:

错误信息形态(测试断言原文)
certificate uses RSA but private key uses EC

为什么双向命名重要:它同时告诉你"证书是什么"与"私钥被当成了什么"——多半是文件拿错或标签被改,一眼定位。曲线族不匹配(cert P-256 + key P-384)同样有点名诊断。

12.4 SNI 表与热替换

装配期:hosts[] 的 (域名集, cert, key) → 构建 SNI 表 │ reload(端点/结构未变,证书文件变了) ▼ 证书热替换 TlsTableRefresh:锁内原子换表 旧 identity 由引用计数保护 → 在途握手用完旧证书才释放 │ ▼ 新握手全部使用新证书(无窗口期、无重启)

reload 协调器在受理意图时做配置级分流:结构比对未变(端点、监听、拓扑一致)→ 走证书热替换 + 脚本重载;有变 → 按第 13 章的结构变更规则处理。这意味着续期证书就是"覆盖文件 + 提交 reload"两个动作。

12.5 契约

  • 检查线:PEM 配对、算法族匹配、链构建全部在启动期完成,点名诊断。
  • 分发线:证书按 host 配置进 SNI 表,握手期按 SNI 选出,与 HTTP 层路由一致。
  • 更新线:结构未变的 reload 走热替换——锁内原子换表,旧 identity 引用保护到在途握手结束。

常见错误

fullchain 与 key 拿反
症状
启动失败,诊断指出文件内容不是预期的类型。
原因
tls_cert 填了私钥文件(或反之)。严格 PEM 解析让这种错误在启动期暴露而不是握手期。
换证书后直接重启
症状
续期证书时断开了所有连接。
原因
不知道证书有热替换路径——覆盖文件后提交 reload 即可,无需重启进程。
✗ 不必要
cp new-cert.pem host/tls/
systemctl restart xs   /* 断连 */
✓ 正确
cp new-cert.pem host/tls/
/* 提交 reload:证书热替换 */

编程练习

基础起一个 HTTPS 站

用任意自签工具生成 cert/key,给 demo 开 TLS 端口,curl -k 验证。

验收:TLS 端点握手成功并返回 /json。
进阶诊断矩阵复现

依次制造:标签改错的 key、cert/key 算法族不匹配、PEM 块残缺,记录三条诊断信息。

验收:三条信息都能指明"哪个文件、什么问题"。
挑战不停机换证书

起一个长连接(curl --http1.1 -k 持续请求循环),期间覆盖证书文件并提交 reload,观察连接是否断开。

验收:请求循环零失败;新握手用上新证书(openssl s_client 两次对比)。

小结

要点结论
严格解析PEM 标签配对计数;坏块不静默
点名诊断算法族不匹配双向命名(cert 是 X,key 是 Y)
SNI 分发host 级证书 → 握手期选出,与 HTTP 路由一致
热替换结构未变的 reload:原子换表 + 引用保护,零断连
xrt 延伸阅读