Envoy 集成测试证书体系全解析:身份设计、生成流程与测试应用
2026/9/15 17:03:23 网站建设 项目流程

Envoy 集成测试证书体系全解析:身份设计、生成流程与测试应用

【免费下载链接】envoyCloud-native high-performance edge/middle/service proxy项目地址: https://gitcode.com/GitHub_Trending/en/envoy

Envoy 是云原生场景下的高性能边缘/中间/服务代理,其 TLS/mTLS 能力(下游 TLS 终止、上游 TLS 发起、SDS 动态证书下发、OCSP 装订等)都依赖一套可复现、覆盖各种边界场景的测试证书。本文以 test/config/integration/certs/README.md 为核心,系统拆解 Envoy 集成测试证书库中 6 类身份的设计意图、基于@envoy_toolshed//certs:gen的 Bazel 构建期生成流程,以及这些证书如何被集成测试消费。读完本文,你将能看懂 Envoy 测试证书的命名约定与信任链结构,掌握重新生成/更新证书的具体命令,并能在自己的项目中复刻这套“规范驱动、密钥入库、证书生成”的测试凭证管理方案。

一、六类身份:测试证书库的信任模型总览

该目录下共定义了 6 类身份(identities),它们分别对应 Envoy 集成测试中不同的信任域。README 给出的原始清单如下:

身份角色证书文件私钥文件签发者签名配置
CAClient 与 Server 的证书颁发机构cacert.pem(自签名)cakey.pem自身
ClientmTLS 客户端身份clientcert.pemclientkey.pemCA
Server下游服务端身份servercert.pemserverkey.pemCA(使用servercert.cfgservercert.cfg
Upstream CAUpstream 的证书颁发机构upstreamcacert.pem(自签名)upstreamcakey.pem自身
Upstream上游集群身份upstreamcert.pemupstreamkey.pemUpstream CA(使用upstreamcert.cfgupstreamcert.cfg
Upstream localhost本机回环上游身份upstreamlocalhostcert.pemupstreamlocalhostkey.pemUpstream CA(使用upstreamlocalhostcert.cfgupstreamlocalhostcert.cfg

关键设计点:

  • 两套独立的信任域:Client/Server 由“CA”签发,Upstream/Upstream localhost 由“Upstream CA”签发。这样在测试中,下游链路与上游链路可以分别做信任配置、互不干扰,例如下游验证客户端证书信任 CA,而上游验证服务端证书信任 Upstream CA。
  • Upstream localhost 与 Upstream 的唯一区别:前者在证书中带有localhost的 SAN(Subject Alternative Name)。从 upstreamlocalhostcert.cfg 可以看到,其alt_names段包含DNS.2 = localhostIP.1 = 127.0.0.1IP.2 = ::1,专门用于集成测试中通过 127.0.0.1/::1/localhost 访问本机测试上游时主机名校验通过。
  • 命名约定:目录命名统一为“名字 + cert”(如servercert.pem),这与多数项目“server_cert.pem”的写法不同,是历史沿袭的约定,certs.spec中特别注释了这一点。

需要特别强调的是,这些私钥均为测试专用、刻意公开。目录下的 KEYS.md 明确声明:每一个*_key.pem/*key.pem文件都是有意公开的测试私钥,它们从未保护过任何真实数据,任何拿到仓库副本的人都知道这些密钥,绝不允许在 Envoy 测试套件之外使用。密钥被检入仓库是为了保证证书产物可复现——生成器只创建证书、从不创建密钥,因此同一份 spec 总是产出相同的公钥材料。安全扫描器对这些文件报警属于预期行为,不是安全事故。

二、证书是如何生成的:certs.spec 驱动的 Bazel 构建期生成

README 明确指出:目录中的证书、证书链、OCSP 响应以及*cert_hash.h*cert_info.h头文件,都是在Bazel 构建期@envoy_toolshed//certs:gen生成的;只有私钥和*.cfgOpenSSL 配置被检入仓库

2.1 重新生成证书的命令

README 给出的核心命令:

$ bazel build //test/config/integration/certs:certs $ ls bazel-bin/test/config/integration/certs/

第一条命令构建certs目标,其定义在 BUILD 中:

  • 使用generated_certs()宏(来自@envoy_toolshed//certs:certs.bzl);
  • srcs = glob(["*.cfg", "*key.pem"])——输入是检入仓库的 OpenSSL 配置与私钥;
  • outs = CERTS——输出由 certs.spec 生成的全部证书、链、OCSP 响应及头文件(共 40 余个文件,包括cacert.pemservercert.pemserver_ecdsacert.pemserver_ocsp_resp.derservercert_hash.hservercert_info.h等);
  • static_srcs = glob(["*.cfg", "*key.pem", "google_root_certs.pem", "pqc_cacert.pem", "pqc_cacert_info.h", "san_nul_servercert.pem"])——这些静态产物不经过生成器,原样保留。

第二条命令查看bazel-bin/test/config/integration/certs/目录,即可确认生成结果。

2.2 certs.spec 的语义:一张“测试证书清单”

certs.spec 是生成流程的“输入清单”,使用 INI 风格的区块描述每张证书,主要字段:

区块字段含义
[cert <name>]key该证书对应的私钥文件(已检入仓库)
cfgOpenSSL 配置(已检入仓库)
issuer签发者,self表示自签名,否则引用其他[cert ...]区块名
out生成的证书文件名
hash_header生成的证书 SHA-256 哈希头文件(*_hash.h
info_header生成的证书信息头文件(*_info.h
validity有效期策略,如expired表示生成已过期证书
[concat <file>]parts按顺序拼接多个证书区块,生成证书链文件
[ocsp <file>]cert/issuer/responder/status/next_update_days为指定证书生成 OCSP 响应,responder指明签署响应的 CA

Spec 中最值得注意的几组设计:

  • CA 与证书链ca(自签名)→intermediate_ca(由 ca 签发)→intermediate_ca_2(由 intermediate_ca 签发),并通过[concat intermediate_ca_cert_chain.pem](parts = ca, intermediate_ca, intermediate_ca_2)与[concat intermediate_partial_ca_cert_chain.pem](parts = intermediate_ca, intermediate_ca_2)生成两条不同深度的链,用于测试不同信任链长度下的校验逻辑。
  • 多算法服务端证书:除了 RSA 的server,还定义了server_ecdsa(复用servercert.cfg,ECC 密钥)、server_ecdsa_p384server_ecdsa_p521,用于覆盖不同椭圆曲线签名算法的证书路径。
  • 多级信任的客户端clientca直接签发,client2intermediate_ca_2签发并生成完整链client2_chain.pem(parts = client2, intermediate_ca_2, intermediate_ca, ca),client_ecdsa则复用clientcert.cfg使用 ECDSA 密钥,用于测试 mTLS 下不同客户端证书链与算法的组合。
  • 过期证书[cert expired_]区块(末尾下划线是刻意保留的历史命名,对应常量TEST_EXPIRED__CERT_HASH)通过validity = expired让生成器直接写入过期的 notBefore/notAfter 字段。README 特别说明:生成过期证书不再需要docker run ... faketime,生成器直接写入时间字段即可,简化了 CI 依赖。
  • OCSP 响应:5 个[ocsp ...]区块(server、long_server、server_ecdsa、server_ecdsa_p384、server_ecdsa_p521)全部使用status = unknown——因为从未填充过 CA 索引(CA index),每个响应对相应证书都报告unknownnext_update_days = 730指定响应的有效期窗口。

2.3 三个“不走生成器”的静态证书

pqc_cacert.pemgoogle_root_certs.pemsan_nul_servercert.pem(以及pqc_cacert_info.h)不在生成范围内,而是原样检入

  • pqc_cacert.pem/pqc_cakey.pem:后量子(Post-Quantum Cryptography)CA,服务于 PQC 相关测试场景;
  • google_root_certs.pem:Google 根证书包,用于模拟真实公网信任链的测试;
  • san_nul_servercert.pem:由 generate_nul_cert.py 生成的、SAN 中内嵌 NUL(\x00)字节的畸形证书,用于验证主机名校验在 NUL 注入攻击下的行为。

三、NUL-in-SAN 证书:安全边界测试的专门造物

san_nul_servercert.pem 是一种刻意构造的畸形证书,其生成脚本 generate_nul_cert.py 展示了完整的构造手法:

  1. 复用既有材料:脚本从当前目录加载upstreamcacert.pem/upstreamcakey.pem作为签发 CA,加载upstreamkey.pem提取公钥——即“用既有 CA 签发、复用既有服务器公钥”,保证与 Upstream 证书的密钥材料一致。
  2. 构造恶意 SAN:核心是san = x509.SubjectAlternativeName([x509.DNSName("example.com\x00.attacker.com")]),在example.com.attacker.com之间嵌入 NUL 字节。
  3. 附加标准扩展BasicConstraints(ca=False)KeyUsage(digitalSignature + keyEncipherment)、ExtendedKeyUsage(SERVER_AUTH)均设为 critical,SAN 为非 critical。
  4. 写入文件comment块中详细注释了攻击原理——存在缺陷的主机名校验器(例如 Envoy 修复前的 verifySubjectAltName 路径)会在 NUL 字节处截断 SAN,从而把该证书错误匹配为合法的example.com。脚本注释还给出了 SAN 的原始十六进制:65 78 61 6d 70 6c 65 2e 63 6f 6d 00 2e 61 74 74 61 63 6b 65 72 2e 63 6f 6d,NUL 位于偏移 11 处。

这类证书的价值在于:它能以真实证书形态驱动 TLS 握手,验证 Envoy 在处理 NUL 注入式 SAN 时不会发生截断误匹配,是安全回归测试的重要固定装置。

四、OpenSSL 配置:每张证书的“身份说明书”

检入的*.cfg文件是 OpenSSL 的 req/x509 配置,定义了证书的 DN(Distinguished Name)、扩展与 SAN。以 cacert.cfg(CA 自签名)为例:

  • DN 使用 Envoy 早期开发方 Lyft 的测试信息(O=LyftOU=Lyft EngineeringCN=Test CA);
  • basicConstraints = critical, CA:TRUEkeyUsage = critical, cRLSign, keyCertSign——CA 证书必须的能力约束;
  • 非自签名证书(如 servercert.cfg)则使用CA:FALSEkeyUsage = nonRepudiation, digitalSignature, keyEnciphermentextendedKeyUsage = clientAuth, serverAuth

各证书 SAN 的设计意图也各有侧重:

配置SAN 内容用途
servercert.cfgspiffe://lyft.com/backend-teamhttp://backend.lyft.comlyft.comwww.lyft.com同时覆盖 SPIFFE URI、HTTP URI 与 DNS 三种 SAN 类型
clientcert.cfg上述 URI/DNS 之外再加IP.1 = 1.2.3.4IP.2 = 0:1:2:3::4覆盖 IPv4/IPv6 地址 SAN
upstreamcert.cfgDNS.1 = *.lyft.comIP.1 = 127.0.0.1IP.2 = ::1通配符域名 + 本机回环地址
upstreamlocalhostcert.cfgDNS.2 = localhostIP.1 = 127.0.0.1IP.2 = ::1回环主机名校验(Upstream localhost 专属)
expired_cert.cfgDNS.1 = server1.example.com过期场景的基础身份

值得一提的还有 long_servercert.cfg:它在[v3_req][v3_ca]段通过自定义 OID2.3.4.5 = ASN1:UTF8String:<4KB 随机串>注入约 4KB 随机数据(注释说明来自dd if=/dev/random ... | base64),刻意把证书撑得“异常巨大”,用于验证证书压缩(certificate compression)场景下大证书无法被压缩(随机数据压缩率接近于 0),以及大证书在握手中的传输与缓冲处理。

五、集成测试如何消费这些证书

证书与私钥通过runfilesPath("test/config/integration/certs/...")注入到集成测试进程,最典型的两个消费者:

静态 SDS 测试test/integration/sds_static_integration_test.cc 直接以静态路径引用证书:

TestEnvironment::runfilesPath("test/config/integration/certs/cacert.pem"); TestEnvironment::runfilesPath("test/config/integration/certs/servercert.pem");

动态 SDS 测试test/integration/sds_dynamic_integration_test.cc 则覆盖更广:

  • 下游 mTLS:cacert.pem作为 CA、servercert.pem/serverkey.pem作为服务端证书、clientcert.pem/clientkey.pem作为客户端身份;
  • 证书轮换(key rotation):将servercert.pem/serverkey.pem复制到{{ test_tmpdir }}/root/current/供 SDS 监听,并在测试中替换为新证书验证轮换生效;
  • 多算法:server_ecdsacert.pem/server_ecdsakey.pemserver2cert.pem/server2key.pem等覆盖 ECDSA 与多证书组合。

此外,test/integration/quic_mtls_integration_test.cc、test/integration/sds_dynamic_key_rotation_setup.sh、test/integration/stats_integration_test.cc 也引用了该目录的证书,分别用于 QUIC 上的 mTLS、SDS 密钥轮换脚本和连接统计相关断言。

配套的 Bazel 库目标(见 BUILD)让 C++ 测试可以直接包含头文件:

  • //test/config/integration/certs:hashes:导出全部*_hash.h(如servercert_hash.hupstreamlocalhostcert_hash.h),测试可用预计算的 SHA-256 哈希断言证书内容未被改动;
  • //test/config/integration/certs:certs_info:导出*_info.h(如cacert_info.hservercert_info.hexpired_cert_info.h),供测试读取证书主题、序列号、有效期等元数据。

六、给项目复刻者的实践清单

从 Envoy 这套方案可以提炼出可复用的测试证书管理范式:

  1. 私钥与配置入库,证书不入库:只检入*.key.pem*.cfg,所有派生证书、链、OCSP 响应和头文件交给构建期生成器,保证产物始终与 spec 一致、可复现。
  2. 用 spec 声明证书矩阵:一份 certs.spec 即可表达多 CA、多级中间 CA、多算法(RSA/ECDSA P-384/P-521)、过期证书、畸形证书、OCSP 响应等全部变体,新增场景只需增补区块。
  3. 显式标记测试密钥:用独立的 KEYS.md 声明“这些密钥是测试专用、可公开、禁止外流使用”,避免误用,并让安全扫描器的告警可解释。
  4. 安全边界要有专门的畸形样本:NUL-in-SAN、超大证书这类“反模式”固定装置,是验证主机名校验、证书压缩等安全与性能逻辑的关键输入,应像正常证书一样纳入生成与版本管理。

七、总结

Envoy 的 test/config/integration/certs 目录并非一摞随手生成的 PEM 文件,而是一套“身份设计明确、构建期自动生成、测试广泛消费”的证书工程体系:6 类身份构成两套独立信任域,certs.spec+@envoy_toolshed//certs:gen在 Bazel 构建期产出证书、链、OCSP 与哈希/信息头文件,私钥和 OpenSSL 配置以“刻意公开的测试密钥”身份入库以保证可复现性,而 NUL-in-SAN、超大随机证书、过期证书等特殊样本则专为安全与边界测试服务。理解这套机制,既能帮助你读懂 Envoy 各类 TLS/mTLS 集成测试的信任设置,也能为自建代理或网关的测试基础设施提供一份可直接借鉴的范式。

</|DSML|parameter> </|DSML|invoke> </|DSML|tool_calls>

【免费下载链接】envoyCloud-native high-performance edge/middle/service proxy项目地址: https://gitcode.com/GitHub_Trending/en/envoy

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询