acme.sh 通配符证书验证失败排查复盘一、事件概述1. 事件背景在主机 root@156.226.176.52 上使用 acme.sh 签发 op123.ren 及其多子域通配符证书时，DNS 验证失败，报错提示在 _acme-challenge.n9e.op123.ren 处未找到 TXT 记录。2. 影响范围A. 影响功能无法为以下域名签发或续期证书：op123.ren、.op123.ren、.ngrok.op123.ren、.tools.op123.ren、.atzlinux.op123.ren、.api.op123.ren、.mcp.op123.ren、*.n9e.op123.ren。B. 影响时长自执行命令至根因确认并给出修复方案为止。C. 严重程度证书无法签发会导致上述域名无法使用有效的 HTTPS，属配置类问题，修复后即可恢复。3. 执行命令与报错执行命令为：/root/.acme.sh/acme.sh --issue -d op123.ren -d "*.op123.ren" \ -d "*.ngrok.op123.ren" -d "*.tools.op123.ren" -d "*.atzlinux.op123.ren" \ -d "*.api.op123.ren" -d "*.mcp.op123.ren" -d "*.n9e.op123.ren" \ --dns dns_tencent --challenge-alias sddts.cn --force报错摘要：Invalid status. Verification error details: No TXT record found at _acme-challenge.n9e.op123.renRemoving DNS records 时仅删除了 _acme-challenge.sddts.cn 的 TXT 记录伴随 libcurl error code 3 等次要错误二、事件时间线1. 问题发生用户执行上述 acme.sh 命令，使用腾讯云 DNS API（dns_tencent）与挑战别名（challenge-alias sddts.cn），期望在 sddts.cn 所在 DNS 写入一条 TXT 完成所有域名验证。2. 问题定位通过分析报错与 acme.sh 官方文档可知：使用 --challenge-alias 时，acme.sh 只会把验证用 TXT 写在 _acme-challenge.sddts.cnLet's Encrypt 仍会按域名逐个查询 _acme-challenge.<域名>因此必须在 op123.ren 的 DNS 中，为每个参与签发的（子）域名预先配置 CNAME：_acme-challenge.<该域名> 指向 _acme-challenge.sddts.cn报错指向 _acme-challenge.n9e.op123.ren，说明该名的 CNAME 缺失或指向错误。3. 根因确认用户执行 dig 验证：dig _acme-challenge.n9e.op123.ren CNAME +short返回结果为：_acmhe-challenge.sddts.cn.（多了一个字母 h）。正确目标应为 _acme-challenge.sddts.cn.。CNAME 目标拼写错误，导致 Let's Encrypt 跟随 CNAME 后查询的是 _acmhe-challenge.sddts.cn 的 TXT，而 acme.sh 实际写入的是 _acme-challenge.sddts.cn，因此验证失败。4. 解决方案在 op123.ren 的 DNS 中，将 _acme-challenge.n9e.op123.ren 的 CNAME 目标从 _acmhe-challenge.sddts.cn. 修改为 _acme-challenge.sddts.cn.，等待解析生效后重新执行 acme.sh 签发命令。三、问题分析1. 直接原因Let's Encrypt 在查询 _acme-challenge.n9e.op123.ren 时，通过 CNAME 被指向 _acmhe-challenge.sddts.cn，在该处无法获得 acme.sh 写入的 TXT（TXT 实际在 _acme-challenge.sddts.cn），因此报「No TXT record found at _acme-challenge.n9e.op123.ren」。2. 根本原因（5 Whys 分析）A. 为什么会出现这个问题？CNAME 记录的目标主机名配置错误，写成了 _acmhe-challenge.sddts.cn 而非 _acme-challenge.sddts.cn。B. 为什么没有在首次配置时发现？多子域、多 CNAME 时，易在手工添加或复制时产生拼写错误，且 DNS 配置后未逐条用 dig 校验 CNAME 目标是否正确。C. 为什么其他子域可能未报错？若其他子域（如 op123.ren、ngrok、tools 等）的 _acme-challenge 的 CNAME 已正确指向 _acme-challenge.sddts.cn，则仅 n9e.op123.ren 会验证失败，与报错仅指向 n9e 一致。3. 深层反思使用 DNS alias 模式时，每个域名/子域都依赖一条正确的 CNAME，漏配或写错任一条都会导致该域名验证失败。建议在批量配置 CNAME 后，用脚本或逐条 dig 检查每条 _acme-challenge.<子域> 的 CNAME 是否指向 _acme-challenge.sddts.cn.。四、解决方案1. 临时方案修改 _acme-challenge.n9e.op123.ren 的 CNAME 目标为 _acme-challenge.sddts.cn.，等待 TTL 生效后重跑 acme.sh 命令。2. 永久方案A. 配置清单对使用 --challenge-alias sddts.cn 的证书，确保 op123.ren 的 DNS 中存在以下 CNAME（目标均为 _acme-challenge.sddts.cn.）：主机记录（或等效）类型目标_acme-challengeCNAME_acme-challenge.sddts.cn._acme-challenge.ngrokCNAME_acme-challenge.sddts.cn._acme-challenge.toolsCNAME_acme-challenge.sddts.cn._acme-challenge.atzlinuxCNAME_acme-challenge.sddts.cn._acme-challenge.apiCNAME_acme-challenge.sddts.cn._acme-challenge.mcpCNAME_acme-challenge.sddts.cn._acme-challenge.n9eCNAME_acme-challenge.sddts.cn.B. 验证方法签发前可用下列命令确认 CNAME 是否正确：dig _acme-challenge.n9e.op123.ren CNAME +short # 应返回: _acme-challenge.sddts.cn.3. 预防措施新增子域时，同步在 DNS 中为该子域添加 _acme-challenge.<子域> 的 CNAME 记录。使用文档或清单列出所有需 CNAME 的子域，避免漏配或拼写错误。重要变更后使用 dig 或脚本批量校验 CNAME 目标。五、经验总结1. 做得好的地方报错信息明确指向 _acme-challenge.n9e.op123.ren，便于缩小范围。结合 acme.sh 官方 DNS alias 文档可快速理解「每个域名需独立 CNAME」的机制。通过 dig 直接发现 CNAME 目标拼写错误（acmhe vs acme），定位根因。2. 需要改进的地方初次配置多子域 CNAME 时未逐条校验，导致错误在签发时才暴露。可考虑在文档或运维流程中固化 CNAME 清单与校验步骤。3. 流程优化建议将「acme.sh + challenge-alias」所需 CNAME 列表与校验命令写入内部运维文档或 Runbook。证书续期或新增子域后，增加一次 dig 抽查或脚本校验，确保所有 _acme-challenge 的 CNAME 指向正确。六、参考资料acme.sh DNS alias modeacme.sh 官方仓库
Let's Encrypt 发布 DNS-PERSIST-01：基于 DNS 的持久化挑战验证新模型
Let's Encrypt 发布 DNS-PERSIST-01 新挑战类型简化证书自动化一、新闻概述1. 标题Let's Encrypt 发布 DNS-PERSIST-01：基于 DNS 的持久化挑战验证新模型2. 发布时间2026 年 2 月 18 日3. 来源Let's Encrypt 官方博客二、核心内容1. 事件摘要A. 主要内容Let's Encrypt 宣布正在实施支持一种全新的 ACME 挑战类型 DNS-PERSIST-01，该类型基于新的 IETF 草案规范。B. 核心亮点持久化授权记录，一次配置长期有效无需每次续期时更新 DNS 记录将授权直接绑定到 ACME 账户和 CA支持范围控制和可选过期时间特别适合 IoT 部署、多租户平台和批量证书操作场景2. 关键信息A. 版本信息基于 IETF 草案规范：draft-ietf-acme-dns-persist-00B. 重要数据CA/Browser Forum 投票 SC-088v3 于 2025 年 10 月全票通过IETF ACME 工作组于同月采纳该草案预计 2026 年第一季度末在测试环境推出生产环境预计 2026 年第二季度推出C. 涉及技术ACME 协议DNS TXT 记录验证Pebble（Let's Encrypt 的 CA 软件 Boulder 的迷你版本）lego-cli 客户端实现3. 背景介绍A. 传统 DNS-01 挑战DNS-01 挑战是 Let's Encrypt 提供的域名验证方式之一，特别适用于需要通配符证书或不愿将基础设施暴露到公网的用户。其工作原理是客户端在 DNS TXT 记录中发布 Let's Encrypt 生成的令牌，Let's Encrypt 通过查询 DNS 来验证域名控制权。B. 存在的问题传统 DNS-01 挑战虽然广泛支持且经过实战验证，但存在显著的运营成本：DNS 传播延迟导致验证等待时间每次续期都需要更新 DNS 记录自动化流程通常需要在基础设施中分发 DNS 凭据三、详细报道1. 主要内容A. DNS-01 的重复验证机制传统 DNS-01 挑战依赖 Let's Encrypt 生成的一次性令牌。ACME 客户端需要将包含该令牌的 TXT 记录发布到 _acme-challenge.<YOUR_DOMAIN>，Let's Encrypt 查询 DNS 确认值匹配。由于每次授权都需要新令牌，DNS 更新成为签发工作流程的必要环节。优势在于每次成功验证都提供了当前控制域名 DNS 的新证明。但在实践中，这意味着：DNS API 凭据存在于签发管道的某个位置验证尝试涉及等待 DNS 传播DNS 变更频繁发生——大型部署中可能每天多次B. DNS-PERSIST-01 的持久化授权机制DNS-PERSIST-01 采用不同的验证方法。它不是为每次签发发布新的挑战记录，而是发布一个持久的授权 TXT 记录，标识允许为此域名签发证书的 CA 和特定 ACME 账户。对于主机名 example.com，记录位于 _validation-persist.example.com：graph LR subgraph DNS_01["传统 DNS-01 流程"] A1[ACME 客户端] -->|每次签发/续期| B1[更新 _acme-challenge 记录] B1 --> C1[Let's Encrypt 验证] C1 -->|等待 DNS 传播| D1[颁发证书] end subgraph DNS_PERSIST_01["DNS-PERSIST-01 流程"] A2[ACME 客户端] -->|一次性配置| B2[设置 _validation-persist 记录] B2 --> C2[Let's Encrypt 验证] C2 -->|无需等待传播| D2[直接颁发证书] D2 -->|后续续期| D2 end记录格式示例：graph TB A["_validation-persist.example.com. IN TXT"] --> B["letsencrypt.org;"] A --> C["accounturi=https://acme-v02.api.letsencrypt.org/acme/acct/1234567890"] style A fill:#e1f5ff style B fill:#fff3cd style C fill:#fff3cd一旦该记录存在，就可以用于新的签发和所有后续续期，从操作上将 DNS 变更从关键路径中移除。2. 技术细节A. 安全权衡DNS-01 中的敏感资产是 DNS 写入权限。在许多部署中，DNS API 凭据分布在签发和续期管道中，增加了攻击者可能泄露它们的位置数量。DNS-PERSIST-01 将授权直接绑定到 ACME 账户，允许 DNS 写入权限在初始设置后保持更严格的控制。权衡在于，由于授权记录随时间持续存在，保护 ACME 账户密钥成为核心关注点。B. 范围和生命周期控制DNS-PERSIST-01 引入了显式的作用域控制。默认情况下，授权仅适用于经过验证的完全限定域名（FQDN），并且无限期有效。通配符证书支持添加 policy=wildcard 可将授权范围扩展到经过验证的 FQDNN、通配符证书（如 *.example.com）以及后缀与经过验证的 FQDN 匹配的子域名：graph TB A["_validation-persist.example.com. IN TXT"] --> B["letsencrypt.org;"] A --> C["accounturi=https://acme-v02.api.letsencrypt.org/acme/acct/1234567890;"] A --> D["policy=wildcard"] style A fill:#e1f5ff style B fill:#fff3cd style C fill:#fff3cd style D fill:#d4edda可选过期时间不希望授权无限期持续的订阅者可以包含可选的 persistUntil 时间戳。这限制了记录可用于新验证的时长，但也意味着必须在过期前更新或替换记录。使用此功能的用户应确保有充分的提醒或监控机制，以防止授权意外过期。时间戳表示为自 1970-01-01 以来的 UTC 秒数：graph TB A["_validation-persist.example.com. IN TXT"] --> B["letsencrypt.org;"] A --> C["accounturi=https://acme-v02.api.letsencrypt.org/acme/acct/1234567890;"] A --> D["persistUntil=1767225600"] style A fill:#e1f5ff style B fill:#fff3cd style C fill:#fff3cd style D fill:#f8d7da多 CA 授权可以通过在 _validation-persist.<YOUR_DOMAIN> 发布多个 TXT 记录来同时授权多个 CA，每个记录包含要授权的 CA 的 issuer-domain-name。在验证期间，每个 CA 查询相同的 DNS 标签并仅评估与其自己的 issuer-domain-name 匹配的记录。C. 架构对比graph TB subgraph 传统["传统 DNS-01 验证"] direction TB C1[客户端] -->|获取令牌| C2[更新 DNS TXT 记录] C2 -->|等待传播| C3[Let's Encrypt 验证] C3 -->|颁发证书| C4[证书签发] C4 -->|90天后| C5[需要续期] C5 --> C2 end subgraph 新方案["DNS-PERSIST-01 验证"] direction TB P1[客户端] -->|一次性| P2[设置持久授权记录] P2 --> P3[Let's Encrypt 验证] P3 -->|颁发证书| P4[证书签发] P4 -->|90天后| P5[需要续期] P5 -->|无需 DNS 更新| P3 end style 传统 fill:#fff3cd style 新方案 fill:#d4edda3. 数据与事实A. 规范进展CA/Browser Forum 投票 SC-088v3 定义 3.2.2.4.22 DNS TXT Record with Persistent Value DCV 方法2025 年 10 月全票通过IETF ACME 工作组同月采纳草案B. 实现支持Pebble（Boulder 的迷你版本）现已支持lego-cli 客户端实现正在开发中四、影响分析1. 行业影响A. 技术趋势从临时验证转向持久授权模式减少对 DNS API 的依赖，降低凭据泄露风险优化自动化证书管理流程B. 标准化进展IETF 草案虽然仍处于活跃状态，但描述的核心机制预计不会有实质性变化。2. 用户影响A. 现有用户可继续使用传统 DNS-01 挑战迁移到 DNS-PERSIST-01 可简化操作流程B. 潜在用户特别适合以下场景：IoT 设备部署（DNS 操作困难）多租户平台（统一授权管理）批量证书操作（减少 DNS 更新次数）C. 迁移考虑需要一次性设置 DNS 记录ACME 账户密钥安全变得更重要建议等待生产环境正式推出后再迁移关键业务3. 技术趋势A. 证书管理演进从频繁操作转向一次性配置关注点从 DNS 操作转向账户安全B. 生态影响其他 CA 可能采纳类似机制ACME 客户端需要更新支持五、各方反应暂未收集到具体的社区反馈和评价。六、相关链接1. 官方公告Let's Encrypt 博客原文IETF 草案：draft-ietf-acme-dns-persist-002. 相关规范CA/Browser Forum 投票 SC-088v3ACME 挑战类型文档3. 技术资源Pebble 项目lego-cli 客户端参考资料DNS-PERSIST-01: A New Model for DNS-based Challenge Validation - Let's Encrypt
X509 证书交叉签名工作原理深度解析一、概述1. 简介A. 是什么X.509 证书交叉签名（Cross Signing）是一种证书信任链技术，允许同一个证书通过多个不同的证书颁发机构（CA）进行签名，从而建立多条通向不同受信任根证书的验证路径。B. 为什么重要解决 CA 根证书过期问题：当旧的根证书即将过期时，可以通过交叉签名无缝迁移到新根证书扩大兼容性范围：使同一证书能被更多操作系统和浏览器信任简化证书管理：无需重新颁发终端用户证书即可完成信任链切换C. 学完能理解什么X.509 证书的基本结构和关键字段数字签名的工作原理和验证流程交叉签名的技术实现和实际应用TLS 握手过程中的证书验证机制2. 前置知识A. 必备技能基本的密码学概念（公钥加密、数字签名）了解 HTTPS 和 TLS 基础对证书颁发机构（CA）有基本认识B. 推荐知识ASN.1 编码格式PKI（公钥基础设施）体系二、证书格式基础1. 证书数据结构X.509 证书本质上是一个封装了公钥和元数据的数字信封。证书包含以下核心信息：主体信息（Subject）：证书持有者的名称、组织、国家等颁发者信息（Issuer）：签发此证书的 CA 信息公钥（Public Key）：证书持有者的公钥有效期（Validity）：证书的生效和失效时间数字签名（Signature）：颁发者对证书内容的数字签名2. 编码格式证书使用 ASN.1（Abstract Syntax Notation One）格式定义数据结构，通常采用 DER（Distinguished Encoding Rules）或 BER（Basic Encoding Rules）编码。以下是证书数据的简化 JSON 表示示例：{ "subject": { "common_name": "R3", "country": "US", "organization": "Let's Encrypt", "names": ["US", "Let's Encrypt", "R3"] }, "issuer": { "common_name": "DST Root CA X3", "organization": "Digital Signature Trust Co.", "names": ["Digital Signature Trust Co.", "DST Root CA X3"] }, "serial_number": "85078157426496920958827089468591623647", "not_before": "2020-10-07T19:21:40Z", "not_after": "2021-09-29T19:21:40Z", "sigalg": "SHA256WithRSA", "authority_key_id": "C4:A7:B1:A4:7B:2C:71:FA:DB:E1:4B:90:75:FF:C4:15:60:85:89:10", "subject_key_id": "14:2E:B3:17:B7:58:56:CB:AE:50:09:40:E6:1F:AF:9D:8B:14:C2:C6", "public_key": ["..."], "signature": ["..."] }三、数字签名工作原理1. 签名与验证机制数字签名使用非对称加密技术，确保证书的完整性和真实性。A. 签名过程CA 使用其私钥对证书数据生成数字签名签名附加在证书上，形成完整的证书文件B. 验证过程获取证书颁发者的公钥使用颁发者公钥验证证书上的数字签名如果验证通过，确认证书确实由该颁发者签发且未被篡改关键点：验证过程只需要颁发者的公钥，不需要其私钥。这使得任何人都可以验证证书的真实性。2. 证书验证流程graph TD A[开始验证证书] --> B[检查证书有效期] B -->|证书已过期| C[验证失败] B -->|证书有效| D[查找颁发者证书] D -->|找不到颁发者| E[验证失败] D -->|找到颁发者| F[使用颁发者公钥验证签名] F -->|签名无效| G[验证失败] F -->|签名有效| H[是否为根证书] H -->|是根证书| I[检查是否为受信任根CA] I -->|不受信任| J[验证失败] I -->|受信任| K[验证成功] H -->|非根证书| L[递归验证颁发者证书]A. 验证代码示例以下是一个简化的证书验证伪代码：def validate(cert): now = datetime.now() # 检查证书有效期 if now < cert.not_before or now > cert.not_after: return False, "证书已过期" # 查找颁发者证书 for issuer_cert in lookup_cert_by_name(cert.issuer): # 检查颁发者证书有效期 if now < issuer_cert.not_before or now > issuer_cert.not_after: continue # 验证签名 if validate_signature(cert.data, cert.signature, issuer_cert.public_key): # 检查是否为根证书 if cert.issuer is None: if is_trusted_root_ca(cert): return True, "证书有效" else: return False, "未知的根CA" # 递归验证颁发者证书 return validate(issuer_cert) return False, "找不到父证书"3. 验证关键点通过颁发者名称查找颁发者证书使用颁发者的公钥验证数字签名递归向上验证，直到找到受信任的根证书四、交叉签名原理1. 核心概念交叉签名的核心思想是：同一个证书（具有相同的主体名称和公钥）可以被多个不同的颁发者签名，从而建立多条独立的信任链。A. 关键发现证书验证过程中，只有两个字段是必需的：颁发者名称（用于查找颁发者证书）公钥（用于验证数字签名）证书的其他元数据（如有效期、序列号、指纹等）在验证过程中不会被使用。2. 交叉签名示例A. 标准证书链graph LR A[www.example.com] -->|由Z1签发| B[My Encrypt Authority Z1] B -->|由Root Sep21签发| C[My Encrypt Root Sep21] C -->|自签名| CB. 添加交叉签名后的证书链graph TB subgraph 原有信任链 A1[www.example.com] -->|由Z1签发| B1[My Encrypt Authority Z1] B1 -->|由Root Sep21签发| C1[My Encrypt Root Sep21] C1 -->|自签名| C1 end subgraph 新增信任链 A2[www.example.com] -->|由Z1签发| B2[My Encrypt Authority Z1] B2 -->|由Y4签发| C2[Their Encrypt Y4] C2 -->|自签名| C2 end B1 -.相同名称和公钥.- B2C. 证书数据示例原有中间证书：{ "name": "My Encrypt Authority Z1", "issuer": ["My Encrypt Root Sep21"], "sig": "DEFG", "public_key": 3456 }交叉签名中间证书：{ "name": "My Encrypt Authority Z1", "issuer": ["Their Encrypt Y4"], "sig": "HGYE", "public_key": 3456 }注意两个证书具有相同的名称和公钥，但颁发者不同，因此数字签名也不同。3. 为什么交叉签名有效验证代码只关心：能否通过名称找到颁发者证书颁发者证书的公钥能否验证数字签名因此，即使创建一个具有相同名称和公钥的新证书（由不同颁发者签发），也能建立一条完全有效的信任链。4. 实际应用案例：Let's EncryptLet's Encrypt 的 R3 中间证书就是一个典型的交叉签名案例：graph TB R3[R3 中间证书] -->|由DST Root CA X3签发| DST[DST Root CA X3] R3 -->|由ISRG Root X1签发| ISRG[ISRG Root X1] DST -->|自签名| DST ISRG -->|自签名| ISRGR3 证书实际上只携带一个数字签名，但可以追溯到两个不同的根证书。这是因为存在两个 R3 证书实例：一个由 DST Root CA X3 签发一个由 ISRG Root X1 签发两个实例具有相同的名称和公钥，但由不同的根 CA 签发。五、TLS 握手中的证书验证1. 证书传递过程在 TLS 握手过程中，服务器不仅发送自己的证书，还会发送中间证书链。sequenceDiagram participant C as 客户端 participant S as 服务器 S->>C: 发送服务器证书 S->>C: 发送中间证书 C->>C: 使用中间证书验证服务器证书 C->>C: 查找本地受信任根证书 C->>C: 使用根证书验证中间证书 alt 验证成功 C->>S: 继续TLS握手 else 验证失败 C->>S: 终止连接 end2. 证书链选择策略客户端通常按照以下策略选择验证路径：优先使用服务器提供的中间证书在本地证书存储中查找匹配的根证书使用找到的根证书完成验证通过发送中间证书，服务器可以引导客户端使用特定的验证路径，无需客户端进行额外的网络请求。3. 路径长度约束某些中间证书设置了路径长度约束（Path Length Constraint），限制其可以签发的子证书层级。例如，R3 证书的路径长度约束为 0，意味着它不能签发有效的子证书。六、交叉签名的实际意义1. 用途场景A. 根证书过渡当旧的根证书即将过期时，可以通过交叉签名将信任迁移到新的根证书，无需重新颁发所有终端证书。B. 扩大兼容性使证书能被更多平台和设备信任，特别是那些只信任特定根 CA 的老系统。C. 容灾备份提供多条信任路径，提高证书验证的可靠性。2. 安全考虑A. 任何人都可以添加父证书由于证书的公钥和名称是公开的，理论上任何人都可以创建一个具有相同名称和公钥的证书，并由自己的 CA 签发。但只要用户的系统不信任这个新 CA，这条链就不会被接受。B. 信任的根基整个系统的安全性最终依赖于用户系统中受信任根证书的管理。3. 交叉签名术语的误导性"交叉签名"这个术语可能会让人误解为一个证书上有多个签名。实际上：一个证书只能携带一个数字签名交叉签名通过创建多个具有相同名称和公钥但由不同颁发者签发的证书实例来实现七、常见问题解答1. 什么是交叉签名证书交叉签名证书是指由多个证书颁发机构签发的、具有相同主体名称和公钥的证书集合。这使得同一个证书可以通过多条信任路径进行验证。2. 什么是 OCSP 签名证书OCSP（Online Certificate Status Protocol）签名证书是一种特殊证书，由受信任的 CA 颁发，用于 OCSP 响应器对证书状态查询进行数字签名。3. TLS 证书和签名证书的区别TLS 证书：用于建立加密通信通道，确保服务器身份和数据传输安全签名证书：用于对数字内容（如代码、文档、其他证书）进行签名，验证内容的完整性和真实性4. OCSP 的风险隐私风险：客户端需要向第三方服务器查询证书状态，可能暴露用户行为可用性风险：如果 OCSP 服务器宕机，可能导致证书验证失败性能影响：需要额外的网络请求，可能延长连接建立时间5. OCSP 的优势实时性：提供近实时的证书撤销状态查询高效性：相比下载完整的 CRL（证书撤销列表），带宽占用更小精确性：可以精确查询单个证书的状态6. 如何验证 OCSP客户端从证书的 AIA（Authority Information Access）字段提取 OCSP URL，向 OCSP 响应器发送签名请求，响应器返回"良好"、"已撤销"或"未知"状态。可以使用 openssl ocsp 等工具进行手动检查。参考资料How cross signing works with X509 certificates
Let's Encrypt 交叉签名：免费兼容老系统的策略分析
Let's Encrypt 交叉签名：免费兼容老系统的策略分析一、概述1. 背景Let's Encrypt 作为互联网上最大的免费证书颁发机构，其成功不仅体现在免费提供 SSL/TLS 证书，更体现在通过创新的交叉签名策略，解决了新 CA 面临的信任分发难题，实现了零成本兼容老系统。2. 核心问题新成立的证书颁发机构面临的核心挑战是：其根证书尚未被操作系统和浏览器广泛信任。若无法解决此问题，即使用户获得了 Let's Encrypt 颁发的证书，老设备仍会显示证书错误。3. 解决方案概述Let's Encrypt 与 IdenTrust 合作，通过交叉签名技术，使自己的中间证书同时被 Let's Encrypt 自有的 ISRG Root X1 和 IdenTrust 广受信任的 DST Root CA X3 签名，从而实现双路径信任验证。二、交叉签名技术原理1. 基本概念交叉签名是一种证书信任机制，允许一个证书被多个根证书签名。这意味着客户端可以通过任何一条有效路径验证证书链，从而扩大兼容性范围。2. Let's Encrypt 的实现架构graph TD A[用户网站证书] -->|被签发| B[R3 中间证书] B -->|路径1| C[ISRG Root X1 Let's Encrypt 自有根 有效期至 2035] B -->|路径2| D[DST Root CA X3 IdenTrust 根 已过期] style D fill:#f9f,stroke:#333,stroke-width:2px style C fill:#bbf,stroke:#333,stroke-width:1px架构说明：R3 中间证书同时拥有两个签名路径 1：通向 Let's Encrypt 的自有根证书 ISRG Root X1路径 2：通向 IdenTrust 的 DST Root CA X3（已于 2021 年 9 月 30 日过期）3. 验证流程sequenceDiagram participant C as 客户端浏览器 participant S as HTTPS 服务器 participant I as 中间证书 R3 C->>S: 发起 HTTPS 连接 S-->>C: 返回证书链 C->>I: 验证证书链 alt 老设备（2016年前） I->>I: 使用 DST Root CA X3 路径验证 I-->>C: 验证成功（2021年前） else 现代设备 I->>I: 使用 ISRG Root X1 路径验证 I-->>C: 验证成功 end三、经济效益分析1. 传统 CA 成本结构传统证书颁发机构建立信任需要：向现有根 CA 购买交叉签名：数十万至数百万美元申请加入各大操作系统和浏览器的信任根程序：耗时数年维护根证书基础设施：高昂的运营成本2. Let's Encrypt 的零成本策略Let's Encrypt 通过以下方式实现零成本交叉签名：策略说明效果公益合作与 IdenTrust 达成公益性质的合作协议无需支付授权费用非排他性交叉签名不限制 Let's Encrypt 发展自有根保持独立性期限约定约定特定的交叉签名有效期明确过渡期3. 商业模式创新Let's Encrypt 的交叉签名策略证明了免费 CA 的可持续性：降低行业准入门槛推动 HTTPS 全网普及建立公益化的互联网基础设施四、时间线与演进1. 发展历程timeline title Let's Encrypt 交叉签名策略时间线 2015 : Let's Encrypt 成立 获得 ISRG Root X1 2015 : 与 IdenTrust 达成 交叉签名协议 2016-2020 : DST Root CA X3 交叉签名 保障老设备兼容 2021-09 : DST Root CA X3 过期 交叉签名失效 2024-06 : 停止提供任何交叉签名链 全面转向 ISRG Root X12. 关键节点2015 年：Let's Encrypt 与 IdenTrust 签署交叉签名协议2016 年：开始大规模颁发证书，交叉签名发挥作用2021 年 9 月 30 日：DST Root CA X3 根证书过期2024 年 6 月：Let's Encrypt 完全停止交叉签名服务五、兼容性影响1. 受影响的系统DST Root CA X3 交叉签名过期后，以下设备受到影响：设备类型受影响版本原因Android7.1.1 及更早版本未包含 ISRG Root X1Windows XP所有版本证书存储已过时macOS10.12.1 及更早版本未信任 ISRG Root X1iOS10 及更早版本根证书存储未更新Ubuntu14.04 及更早版本系统证书库过时2. 影响范围评估2021 年前：交叉签名让全球数百万老设备能够访问使用 Let's Encrypt 证书的网站2021 年后：随着设备自然淘汰，影响逐渐减少2024 年后：绝大多数用户已使用现代设备，不再依赖交叉签名3. 解决方案对于仍受影响的系统：系统更新：升级操作系统以获取 ISRG Root X1手动导入：手动安装 ISRG Root X1 证书替代方案：使用其他仍提供交叉签名的 CA六、策略的成效与局限1. 成效分析A. 技术成效实现了新 CA 的快速冷启动保障了数年间的老设备兼容性平滑过渡到自有根证书B. 社会成效推动 HTTPS 从奢侈品变为标配降低了网站安全化的经济门槛促进了互联网加密基础设施的民主化C. 数据指标颁发证书数量：超过 30 亿张活跃证书覆盖网站数量：全球超过 3 亿个网站HTTPS 普及率：从 2015 年的约 40% 提升至 2024 年的超过 90%2. 局限性A. 临时性交叉签名本质上是一种过渡方案依赖第三方 CA 的商业决策存在明确的有效期限制B. 最终终止2024 年 6 月，Let's Encrypt 完全停止交叉签名老设备最终仍需升级或替换不再提供新的兼容性解决方案七、技术细节1. 证书链结构传统链（2021年前）网站证书 ↓ (由 R3 签发) R3 中间证书 ↓ (由 DST Root CA X3 交叉签名) DST Root CA X3 (IdenTrust)现代链（2021年后）网站证书 ↓ (由 R3 签发) R3 中间证书 ↓ (由 ISRG Root X1 签名) ISRG Root X1 (Let's Encrypt)2. 客户端验证逻辑现代浏览器和操作系统的证书验证逻辑：优先选择最短的有效证书链检查根证书是否在本地信任存储中验证每个证书的签名和有效期八、行业影响与启示1. 对 CA 行业的影响打破了传统 CA 的高价垄断证明了免费 CA 的商业可行性推动了证书颁发模式的创新2. 对互联网安全的启示基础安全设施可以公益化运营技术创新可以大幅降低安全成本行业协作可以加速安全普及3. 未来展望随着交叉签名的终止，Let's Encrypt 已完成其历史使命：ISRG Root X1 已被所有主流系统信任老设备自然淘汰，兼容性问题逐渐消失免费证书成为互联网基础设施的一部分九、总结Let's Encrypt 的交叉签名策略是一次成功的行业创新，通过零成本获取第三方根证书的信任支持，实现了免费 CA 的快速普及。这一策略不仅解决了技术难题，更重要的是证明了公益化安全基础设施的可行性，为互联网安全带来了深远影响。随着 2024 年交叉签名服务的完全终止，Let's Encrypt 已从依赖第三方信任的新兴 CA，成长为拥有独立信任基础、服务全球数亿网站的互联网基础设施提供者。这一转变，标志着免费 SSL/TLS 证书时代的真正成熟。参考资料Let's Encrypt 证书兼容性文档DST Root CA X3 过期说明（中文）Let's Encrypt 证书链（官方）缩短 Let's Encrypt 信任链交叉签名技术原理解析Let's Encrypt 社区：交叉签名终止讨论2024 年 Let's Encrypt 证书链变更影响Let's Encrypt 与 IdenTrust 延长交叉签名的分析X2-by-X1 交叉签名根证书讨论2021 年部分旧 Android 设备不再支持 Let's Encrypt 证书
httpscheck SSL/TLS 证书检查工具设计一、背景与目标1. 项目背景A. 业务场景SSL/TLS 证书是现代互联网安全的基石。证书管理不善会导致服务中断、安全漏洞等问题。系统管理员和开发人员需要一个可靠的工具来检查 HTTPS 证书的有效性、信任链状态和系统配置。B. 痛点分析OpenSSL 命令复杂，输出难以解析缺少中文友好的检查工具需要验证证书在系统中的实际信任状态跨平台检查工具匮乏2. 设计目标A. 功能目标完整的证书链验证证书有效期检查（过期、即将过期、有效）系统 CA 证书路径测试彩色终端输出B. 非功能目标跨平台支持（Linux、macOS、Windows）纯 Go 实现，无外部 OpenSSL 依赖静态编译，单文件部署高性能，支持超时控制二、总体设计1. 设计原则单一职责：每个包职责明确依赖倒置：通过共享类型打破循环依赖接口隔离：CLI 层与业务逻辑分离开闭原则：易于扩展新的检查功能2. 系统架构graph TB User[用户] --> CLI[CLI 层 cmd/] CLI --> Root[root.go 根命令] Root --> Chains[chains.go 证书链检查] Root --> CACert[cacert.go CA 路径测试] Root --> Output[output.go 简单输出] Chains --> Checker[pkg/checker/] CACert --> Checker Output --> Checker Checker --> CertUtil[pkg/certutil/] Checker --> Types[pkg/types/] CertUtil --> Types Checker --> Logger[pkg/logger/] Logger --> Stdout[(标准输出)]3. 模块说明模块文件职责CLI 层cmd/*.go命令行解析、参数处理、用户交互检查器pkg/checker/chains.go证书链获取、验证、系统信任检查证书工具pkg/certutil/*.go证书解析、格式化、系统 CA 路径类型定义pkg/types/types.go共享数据结构、枚举常量日志系统pkg/logger/logger.go彩色输出、日志级别控制4. 数据流sequenceDiagram participant U as 用户 participant C as CLI 层 participant K as Checker participant P as CertUtil participant L as Logger U->>C: 执行命令 C->>K: CheckChain() K->>K: retrieveCertificates() Note over K: 建立 TLS 连接 获取证书链 K->>P: ParseCertificate() Note over P: 解析证书信息 计算指纹 判断状态 P-->>K: CertificateInfo K->>K: verifyChain() Note over K: 验证证书链 检查系统信任 K-->>C: ChainCheckResult C->>P: FormatTable() P-->>C: 格式化字符串 C->>L: 输出 L-->>U: 彩色结果三、核心模块设计1. 类型系统A. 证书类型判断type CertType string const ( CertTypeServer CertType = "服务器证书" CertTypeIntermediate CertType = "中间证书" CertTypeRoot CertType = "根证书" )判断逻辑：服务器证书：链中的第一个证书根证书：链中最后一个且自签名中间证书：介于两者之间B. 证书状态判断type CertStatus string const ( CertStatusValid CertStatus = "有效" CertStatusExpired CertStatus = "已过期" CertStatusExpiring CertStatus = "即将过期" // < 30天 )阈值设定：30 天作为即将过期的警告阈值C. 核心数据结构type CertificateInfo struct { Index int Type CertType Subject pkix.Name SubjectStr string Issuer pkix.Name IssuerStr string NotBefore time.Time NotAfter time.Time DaysRemaining int SerialNumber string Fingerprints map[string]string PublicKeyInfo PublicKeyInfo SignatureAlg string Status CertStatus }2. 证书链检查A. 获取证书链graph TD A[开始] --> B[创建 Dialer] B --> C[配置 TLS Config] C --> D[建立 TCP 连接] D --> E{连接成功?} E -->|否| F[返回错误] E -->|是| G[执行 TLS 握手] G --> H{握手成功?} H -->|否| F H -->|是| I[提取 PeerCertificates] I --> J[返回证书链和连接]关键代码：func retrieveCertificates(ctx context.Context, domain string, port int, timeout time.Duration) ([]*x509.Certificate, *tls.Conn, error) { dialer := &net.Dialer{Timeout: timeout} conn, err := tls.DialWithDialer(dialer, "tcp", fmt.Sprintf("%s:%d", domain, port), &tls.Config{ ServerName: domain, InsecureSkipVerify: true, // 获取证书但不验证 }) if err != nil { return nil, nil, err } state := conn.ConnectionState() return state.PeerCertificates, conn, nil }B. 验证证书链graph TD A[开始] --> B[获取系统 CA 池] B --> C[创建中间证书池] C --> D[添加非服务器证书到中间池] D --> E[配置验证选项] E --> F[调用 Verify 方法] F --> G{验证成功?} G -->|是| H[返回 true] G -->|否| I[返回错误]验证策略：使用系统 CA 证书池作为信任根将除服务器证书外的所有证书加入中间证书池验证第一个证书（服务器证书）的完整链C. 系统信任检查graph LR A[根证书] --> B{遍历系统证书池} B --> C[解析 DER 证书] C --> D{主题匹配?} D -->|是| E[比较 SubjectKeyId] D -->|否| B E --> F{公钥匹配?} F -->|是| G[找到匹配] F -->|否| B匹配策略：先比较主题字符串，再比较 SubjectKeyId 确保公钥一致3. 证书解析A. 公钥信息提取支持三种公钥类型：类型算法标识密钥长度获取RSARSAN.BitLen()ECDSAECDSACurve.Params().BitSizeEd25519Ed25519固定 256B. 指纹计算func GetFingerprints(cert *x509.Certificate) map[string]string { fingerprints := make(map[string]string) sha256Hash := sha256.Sum256(cert.Raw) fingerprints["SHA256"] = hex.EncodeToString(sha256Hash[:]) sha1Hash := sha1.Sum256(cert.Raw) fingerprints["SHA1"] = hex.EncodeToString(sha1Hash[:]) return fingerprints }C. 状态判断func DetermineCertStatus(notAfter time.Time) types.CertStatus { daysRemaining := CalculateDaysRemaining(notAfter) if daysRemaining < 0 { return types.CertStatusExpired } if daysRemaining < 30 { return types.CertStatusExpiring } return types.CertStatusValid }四、跨平台支持1. 平台检测func GetPlatformCAPaths() []string { switch runtime.GOOS { case "linux": return getLinuxCAPaths() case "darwin": return getMacOSCAPaths() case "windows": return getWindowsCAPaths() default: return []string{} } }2. CA 证书路径A. Linux 平台发行版路径Debian/Ubuntu/etc/ssl/certs/ca-certificates.crtRedHat/CentOS/etc/pki/tls/certs/ca-bundle.crtArch Linux/etc/ssl/certs/ca-bundle.crt通用目录/etc/ssl/certsB. macOS 平台路径/etc/ssl/cert.pem/usr/local/etc/openssl/cert.pemC. Windows 平台使用 Windows 证书存储，无需文件路径3. 颜色输出func detectColorSupport() bool { // 检查是否为终端设备 if fileInfo, _ := os.Stdout.Stat(); (fileInfo.Mode() & os.ModeCharDevice) == 0 { return false } // Windows 特殊处理 if runtime.GOOS == "windows" { return os.Getenv("WT_SESSION") != "" || os.Getenv("ConEmuANSI") == "ON" || os.Getenv("ANSICON") != "" } return true }五、构建与部署1. 跨平台编译graph LR A[源码] --> B[go build] B --> C{CGO_ENABLED?} C -->|0| D[静态编译] C -->|1| E[动态链接] D --> F[单一可执行文件] E --> F F --> G[不同平台目标]2. 支持平台操作系统架构输出文件Linuxamd64httpscheck-linux-amd64Linuxarm64httpscheck-linux-arm64macOSamd64httpscheck-darwin-amd64macOSarm64httpscheck-darwin-arm64Windowsamd64httpscheck-windows-amd64.exeWindowsarm64httpscheck-windows-arm64.exe3. 版本信息注入VERSION="1.0.0" BUILD_TIME=$(date +%Y-%m-%d-%H:%M:%S) ldflags="-s -w" ldflags="${ldflags} -X 'main.version=${VERSION}'" ldflags="${ldflags} -X 'main.buildTime=${BUILD_TIME}'" go build -ldflags "${ldflags}" -o httpscheck .六、使用示例1. 默认模式（完整检查）./httpscheck example.com依次执行：cacert 检查 → chains 验证 → output 输出2. 证书链检查./httpscheck chains -p 443 -t 10 -v example.com3. CA 路径测试./httpscheck cacert --all-paths example.com4. 简单输出./httpscheck output --full example.com七、设计亮点1. 纯 Go 实现无外部 OpenSSL 依赖使用 crypto/tls 和 crypto/x509 标准库跨平台编译简单2. 分层架构通过 pkg/types 打破循环依赖：graph LR A[checker] --> D[types] B[certutil] --> D C[logger] --> D3. 用户体验中文界面彩色输出状态图标（有效/过期/即将过期）详细错误信息4. 高性能支持超时控制context 传递连接复用八、未来改进1. 功能扩展JSON 输出格式并发检查多个域名证书监控告警证书自动续期提醒2. 质量提升单元测试覆盖基准测试集成测试3. 工程化CI/CD 流水线自动化发布Homebrew 安装支持参考资料Go crypto/x509 包文档Go crypto/tls 包文档Cobra CLI 框架项目源码仓库
acme.sh 跨 DNS 服务商多域名证书申请指南一、概述1. 问题背景在实际使用场景中，用户可能拥有多个分散在不同 DNS 服务商的域名。例如：a.com 及其子域名的 DNS 解析托管在阿里云b.com 及其子域名的 DNS 解析托管在腾讯云 DNSPod用户希望生成一张 SSL 证书，同时覆盖 a.com、.a.com、b.com、.b.com 这些域名。2. 核心结论重要提示：acme.sh 官方不支持在单个证书中混合使用多个 DNS 服务商的 API。这是 acme.sh 的设计限制，而非配置问题。二、技术原理1. acme.sh 的工作原理acme.sh 使用 ACME 协议申请证书，支持 DNS-01 验证方式。在 DNS-01 验证中，acme.sh 需要在每个域名的 DNS 解析中添加一条 TXT 记录，证明申请者对该域名的控制权。2. 多 DNS API 的技术限制当申请一个包含多个域名的证书时，acme.sh 会为所有域名使用相同的 DNS API 插件。这是因为：acme.sh 的证书申请命令只接受一个 --dns 参数每个域名的 DNS 验证必须通过该指定的 DNS API 完成如果域名不在该 DNS 服务商处，API 调用将失败graph TD A[acme.sh 申请证书] --> B{指定 DNS API} B -->|阿里云 API| C[验证 a.com] B -->|阿里云 API| D[验证 b.com 失败] C --> E[证书申请失败] D --> E三、解决方案方案一：分别为每个域名生成独立证书（推荐）1. 方案说明为每个 DNS 服务商处的域名分别生成证书，这是最简单、最可靠的方式。2. 阿里云域名证书申请A. 获取阿里云 API 密钥登录阿里云控制台进入访问控制（RAM）-> 用户创建新用户或使用现有用户为用户添加 DNS 管理权限创建 AccessKey，记录 AccessKey ID 和 AccessKey SecretB. 配置环境变量export Ali_Key="your_aliyun_access_key_id" export Ali_Secret="your_aliyun_access_key_secret"C. 申请证书acme.sh --issue --dns dns_ali \ -d a.com \ -d *.a.com3. 腾讯云域名证书申请A. 获取腾讯云 API 密钥登录腾讯云控制台进入访问管理 -> API 密钥管理创建密钥，记录 SecretId 和 SecretKeyB. 配置环境变量export DP_Id="your_tencent_secret_id" export DP_Key="your_tencent_secret_key"C. 申请证书acme.sh --issue --dns dns_dp \ -d b.com \ -d *.b.com4. 优点与缺点优点：配置简单，不易出错证书续期互不影响灵活性高，可独立管理符合最佳实践缺点：需要管理多个证书文件Web 服务器需要配置多个证书方案二：统一 DNS 服务商1. 方案说明将所有域名的 DNS 解析迁移到同一个服务商，然后使用单个证书覆盖所有域名。2. 实施步骤A. 选择目标 DNS 服务商根据需求选择阿里云或腾讯云作为统一 DNS 服务商。B. 迁移 DNS 解析在目标 DNS 服务商添加域名复制所有 DNS 记录到新服务商修改域名 NS 记录，指向新的 DNS 服务器等待 DNS 生效（通常需要 24-48 小时）C. 申请统一证书# 假设全部迁移到阿里云 export Ali_Key="your_aliyun_access_key_id" export Ali_Secret="your_aliyun_access_key_secret" acme.sh --issue --dns dns_ali \ -d a.com \ -d *.a.com \ -d b.com \ -d *.b.com3. 优点与缺点优点：单一证书管理简单减少证书续期任务缺点：需要迁移 DNS 服务迁移过程可能影响服务失去多云冗余优势方案三：DNS 别名模式（不推荐）1. 方案说明acme.sh 支持 DNS 别名模式，但配置复杂，不建议普通用户使用。2. 技术原理DNS 别名模式允许通过 CNAME 记录，将一个域名的 ACME 挑战记录指向另一个域名。这样：a.com 的 ACME 挑战通过阿里云 DNS 验证b.com 的 ACME 挑战通过 CNAME 指向 a.com，间接通过阿里云验证3. 配置复杂度需要手动配置 CNAME 记录需要理解 ACME 协议细节故障排查困难四、最佳实践1. 证书管理建议A. 使用独立证书推荐为每个 DNS 服务商处的域名使用独立证书，这是官方推荐的方式。B. 自动续期配置acme.sh 默认配置自动续期，无需额外操作：# 查看已安装的证书 acme.sh --list # 查看证书续期cron任务 crontab -l | grep acme.shC. 证书部署申请成功后，acme.sh 会自动保存证书，可以通过以下命令部署：acme.sh --install-cert -d a.com \ --cert-file /path/to/cert.pem \ --key-file /path/to/key.pem \ --fullchain-file /path/to/fullchain.pem \ --reloadcmd "service nginx reload"2. 安全建议A. API 密钥权限最小化为 acme.sh 创建专用的 API 密钥，仅授予 DNS 管理权限，避免使用主账号密钥。B. 定期轮换密钥建议每 3-6 个月更换一次 API 密钥。C. 监控证书有效期虽然 acme.sh 会自动续期，但仍建议配置监控告警，确保证书正常续期。3. 故障排查A. 常见问题问题 1：DNS API 验证失败# 检查 API 密钥是否正确 echo $Ali_Key echo $Ali_Secret # 手动测试 DNS API curl "https://alidns.aliyuncs.com/"问题 2：证书申请超时检查网络连接确认 DNS 服务商 API 可用增加等待时间参数：--dnssleep 300问题 3：域名验证失败确认域名已在对应 DNS 服务商处检查 API 密钥是否有该域名的管理权限B. 日志查看# 查看 acme.sh 日志 acme.sh --issue --dns dns_ali -d a.com -d *.a.com --debug五、实际案例案例：Web 服务器多证书配置1. Nginx 配置示例当使用多个证书时，需要在 Nginx 中为每个域名配置对应的证书：# a.com 使用阿里云申请的证书 server { listen 443 ssl; server_name a.com *.a.com; ssl_certificate /path/to/a.com.fullchain.pem; ssl_certificate_key /path/to/a.com.key.pem; # 其他配置... } # b.com 使用腾讯云申请的证书 server { listen 443 ssl; server_name b.com *.b.com; ssl_certificate /path/to/b.com.fullchain.pem; ssl_certificate_key /path/to/b.com.key.pem; # 其他配置... }2. Apache 配置示例<VirtualHost *:443> ServerName a.com ServerAlias *.a.com SSLEngine on SSLCertificateFile /path/to/a.com.cert.pem SSLCertificateKeyFile /path/to/a.com.key.pem SSLCertificateChainFile /path/to/a.com.chain.pem </VirtualHost> <VirtualHost *:443> ServerName b.com ServerAlias *.b.com SSLEngine on SSLCertificateFile /path/to/b.com.cert.pem SSLCertificateKeyFile /path/to/b.com.key.pem SSLCertificateChainFile /path/to/b.com.chain.pem </VirtualHost>六、总结1. 核心要点acme.sh 不支持在单个证书中混合使用多个 DNS API推荐为每个 DNS 服务商处的域名分别生成证书证书自动续期功能由 acme.sh 内置处理多证书配置在 Web 服务器中简单易行2. 推荐方案最佳实践：使用方案一，分别为每个 DNS 服务商申请独立证书。这种方式：配置简单，维护容易符合 acme.sh 的设计理念证书续期互不影响灵活性最高3. 参考资料参考资料acme.sh 官方 GitHub 仓库如何同时使用 CloudFlare 和 DNSPod 的两个域名？Issue #2791acme.sh 支持多个 DNS 服务商讨论 - Let's Encrypt 社区acme.sh 自动解析并申请证书 - DNSPod 官方文档使用 acme.sh 为在腾讯云（DNSPod）解析的域名自动申请证书 - 腾讯云使用 acme.sh 与阿里云 DNS 自动签发免费数字证书 - Kubecc Blogacme.sh 多域名最佳实践教程
Certificate Inspector：基于 Playwright 的全链路 SSL 证书检测工具
Certificate Inspector：基于 Playwright 的全链路 SSL 证书检测工具一、新闻概述1. 标题Certificate Inspector：使用 Playwright 捕获网页完整网络足迹的 SSL 证书检测工具2. 发布时间2025 年 1 月（GitHub 开源项目）3. 来源GitHub 开源仓库：shanselman/cert-inspector二、核心内容1. 事件摘要A. 主要内容Certificate Inspector 是一款基于 Node.js 和 Playwright 的 Web 工具，能够检测网页加载过程中所有域名的 SSL 证书和 DNS 记录。其核心创新在于使用无头浏览器捕获完整请求树，包括 JavaScript 动态加载的资源、广告、追踪器和 API 调用。B. 核心亮点完整请求树捕获：通过 Playwright 无头 Chromium 拦截所有网络请求多维度证书检测：SSL 详情、证书链、TLS 版本、HSTS 状态健康度仪表板：颜色编码状态、进度条可视化、按紧急程度排序多视图展示：详细表格、摘要卡片、时间线三种视图多种部署方式：独立可执行文件、Docker、Codespaces、本地开发2. 关键信息A. 技术栈后端：Node.js + Express浏览器自动化：Playwright（无头 Chromium）证书解析：Node.js 原生 tls 模块B. 开源协议MIT LicenseC. 部署选项Windows/macOS/Linux 可执行文件Docker 容器GitHub Codespaces（云端运行）本地开发环境3. 背景介绍A. 痛点分析传统 SSL 检测工具存在以下局限：仅检测主域名，无法发现第三方资源无法捕获 JavaScript 动态加载的连接无法识别隐藏的追踪器和 API 调用缺乏可视化的健康度评估B. 解决方案通过 Playwright 模拟真实用户访问，拦截所有网络请求，实现对网页完整网络足迹的全面检测。三、详细报道1. 主要功能A. 深度检测完整请求树捕获：使用无头 Chromium 加载页面并捕获所有网络请求DNS 查询：每个域名的 IPv4/IPv6 地址和 CNAME 记录SSL 证书详情：主题、颁发者、有效期、序列号、指纹证书链：查看完整的信任链TLS 版本：检测域名是否使用 TLS 1.2 或 1.3HSTS 状态：检查是否启用 Strict-Transport-Security响应时间：测量到每个域名的连接延迟B. 健康度仪表板颜色编码状态：绿色（健康）、黄色（即将过期，小于等于 30 天）、红色（已过期/无效）、灰色（无 HTTPS）进度条：证书健康状况的可视化分解按紧急程度排序：问题优先显示剩余天数：大号可扫描的数字显示C. 多视图展示详细视图：完整表格，可展开证书详情摘要视图：紧凑卡片网格，快速扫描时间线视图：证书过期的可视化时间线D. 工具与过滤器搜索：按域名过滤状态过滤：按健康状态显示/隐藏颁发者过滤：按证书颁发机构分组（Let's Encrypt、DigiCert 等）深色模式：护眼模式点击复制：域名、指纹、序列号导出：下载 JSON 或 CSV 格式结果2. 技术架构A. 系统组成graph TB User[用户浏览器] -->|访问| WebUI[Web 界面] WebUI -->|提交 URL| API[Express API 服务器] API -->|创建页面| Playwright[Playwright 无头浏览器] Playwright -->|加载页面| Target[目标网页] Playwright -->|拦截请求| Network[网络请求拦截器] Network -->|提取域名| DNS[DNS 解析器] Network -->|提取证书| TLS[TLS 证书解析器] DNS -->|返回结果| API TLS -->|返回结果| API API -->|返回数据| WebUIB. 核心工作流程sequenceDiagram participant U as 用户 participant W as Web 界面 participant P as Playwright participant T as 目标网页 participant D as DNS 解析 participant C as 证书检查 U->>W: 输入 URL W->>P: 创建浏览器上下文 P->>T: 加载目标页面 P->>P: 拦截所有网络请求 loop 每个请求 P->>D: 解析 DNS 记录 P->>C: 检查 SSL 证书 D-->>P: IP 地址、CNAME C-->>P: 证书详情、链、TLS 版本 end P-->>W: 返回完整结果 W-->>U: 显示健康度仪表板C. 数据处理流程graph LR A[网络请求] --> B[域名提取] B --> C[DNS 解析] B --> D[TLS 握手] C --> E[IP/CNAME 记录] D --> F[证书链获取] F --> G[证书解析] G --> H[健康度评估] E --> H H --> I[结果聚合] I --> J[视图渲染]3. 部署方案A. 独立可执行文件无需安装 Node.js首次运行自动下载浏览器支持 Windows、macOS、LinuxB. Docker 容器适合团队和服务器环境需要 Docker DesktopC. GitHub Codespaces完全在浏览器中运行无需安装任何软件GitHub 提供每月 60 小时免费使用D. 本地开发适合贡献者和开发者需要 Node.js 环境4. API 接口# 获取 JSON 格式结果 curl -H "Accept: application/json" "http://localhost:3000/inspect?url=https://example.com" # 获取 HTML 格式结果（默认） curl "http://localhost:3000/inspect?url=https://example.com"5. 应用场景A. 安全审计检查 Web 资产的证书健康状况B. 第三方风险评估查看网站依赖的外部域名C. 合规性验证验证 TLS 版本和 HSTS 部署D. 调试分析理解页面的完整网络足迹E. 证书监控在证书过期导致中断前捕捉四、影响分析1. 行业影响A. 填补工具空白传统 SSL 检测工具仅关注主域名，Certificate Inspector 首次实现了对网页完整网络足迹的检测，填补了第三方资源检测的空白。B. 提升安全可见性通过可视化展示所有依赖域的证书状态，帮助安全团队发现被忽视的风险点。2. 用户影响A. 安全团队快速识别证书过期风险全面掌握第三方依赖优先处理紧急问题B. 开发者调试网络请求问题验证 HTTPS 部署优化页面加载性能C. 运维人员监控证书过期时间规划证书续期确保 HSTS 正确配置3. 技术趋势A. 浏览器自动化应用Playwright 在安全检测领域的应用展示了浏览器自动化技术的潜力，未来可能催生更多类似工具。B. 主动式安全检测从被动检测转向主动式、全面化的安全评估，体现了安全工具的发展趋势。C. 可视化与用户体验安全工具越来越注重用户体验和可视化呈现，降低了技术门槛。五、各方反应1. 社区反馈项目采用 MIT 开源协议，欢迎社区贡献，PR 接受前需先讨论变更内容。2. 技术评价A. 优势全面的请求捕获能力多样化的视图展示灵活的部署方式友好的用户界面B. 适用场景中小型网站的证书监控安全审计和风险评估开发和调试环境六、相关链接1. 项目地址GitHub 仓库：https://github.com/shanselman/cert-inspector2. 技术文档Playwright 官方文档Node.js tls 模块文档参考资料Certificate Inspector - GitHub
LiteSSL ACME DNS-01 鉴权漏洞技术分析一、事件概述1. 事件背景2026 年 1 月，用户 00oo00 在 V2EX 社区披露了 LiteSSL ACME 服务存在严重的鉴权漏洞，该漏洞允许攻击者盗签他人已通过 DNS-01 验证的泛域名证书。LiteSSL 是 TrustAsia（亚洲诚信）推出的免费 ACME 证书服务。2. 影响范围A. 影响用户数初步排查涉及 100 多张证书B. 影响时长从服务上线至漏洞披露，持续数月C. 影响功能所有通过 DNS-01 方式验证的域名证书（包括泛域名证书和普通域名证书）3. 严重程度P1 级安全漏洞（CA 信任体系核心问题）二、漏洞原理1. 直接原因LiteSSL 的 DNS-01 challenges 验证缓存机制存在两个关键缺陷：验证缓存有效期过长（30 天）未验证签发请求是否来自同一 ACME 账户2. 根本原因分析graph TD A[攻击者发现目标域名] --> B[查询证书透明度日志] B --> C[找到目标域名的过往证书记录] C --> D[使用自己的 ACME 账户申请相同域名] D --> E{服务器检查缓存} E -->|缓存有效 30天| F[跳过 DNS 验证] E -->|缓存失效| G[要求 DNS 验证] F --> H[成功签发证书] G --> I[验证失败]3. 技术细节A. 正常 ACME DNS-01 验证流程sequenceDiagram participant C as ACME 客户端 participant S as ACME 服务器 participant D as DNS 服务器 C->>S: 请求域名授权 S->>C: 返回 DNS-01 挑战 C->>D: 更新 TXT 记录 C->>S: 通知验证就绪 S->>D: 查询 TXT 记录 D-->>S: 返回验证记录 S->>C: 验证通过，授权状态 VALIDB. 漏洞利用流程sequenceDiagram participant A1 as 攻击者 A (原账户) participant S as ACME 服务器 participant D as DNS 服务器 participant A2 as 攻击者 B (新账户) A1->>S: 申请 example.com 证书 S->>D: DNS-01 验证 D-->>S: 验证成功 S-->>A1: 签发证书 Note over S: 缓存验证结果 30 天 A2->>S: 使用新账户申请 example.com S->>S: 检查缓存 Note over S: 找到有效缓存 未检查账户一致性 S-->>A2: 直接签发证书C. IP 获取问题除核心鉴权漏洞外，还存在辅助问题：速率限制基于错误获取的内网 IP（10.254.14.70）将反向代理服务器的内网 IP 当做客户端 IP三、厂商响应1. 应急响应时间线时间事件T+0V2EX 帖子发布，漏洞披露T+2hTrustAsia 官方回应，关闭证书签发接口T+4h定位问题，开始吊销错误签发的证书T+12h完成受影响证书吊销（100+ 张）T+24hMozilla Bugzilla 报告发布T+36h问题修复，根因分析同步2. 处理措施A. 立即措施关闭 LiteSSL 证书签发接口将所有 ACME 授权域名状态从 VALID 重置为 REVOKED吊销所有受影响的证书（遵循 CAB/F 24 小时要求）B. 根本修复修复 DNS-01 验证缓存的账户隔离问题缩短验证缓存有效期修复 IP 获取逻辑C. 透明披露在 Mozilla Bugzilla 发布初步报告在 V2EX 原帖持续更新处理进展计划后续发布根因分析四、技术深度分析1. 漏洞本质A. EAB 绑定问题LiteSSL 使用 EAB（External Account Binding）标识账户验证缓存与 EAB 绑定，但未校验请求账户与缓存账户的一致性导致任何账户都可以利用其他账户的验证缓存B. 缓存策略缺陷30 天的缓存有效期远超行业标准Let's Encrypt 通常为 30 天，但配合账户隔离机制长有效期扩大了攻击窗口2. 实际攻击场景A. 攻击前提目标域名必须曾在 LiteSSL 签发过证书目标域名使用 DNS-01 方式验证验证缓存仍在有效期内B. 攻击限制无法用于任意域名（必须有过往签发记录）无法获取原证书的私钥需要配合其他攻击手段（如 DNS 劫持）才能实现中间人攻击3. 与历史事件对比事件CA问题后果DigiNotar (2011)DigiNotar恶意签发伪造证书CA 破产，根证书被吊销StartCom (2016)StartCom签发流程不规范被 Chrome/Firefox 移除信任TrustAsia (2026)TrustAsiaACME 验证缓存隔离失效主动披露，快速修复五、安全影响评估1. 直接影响A. 受影响证书类型仅影响通过 ACME 协议签发的免费证书商业证书和 API 签发的证书不受影响主要影响使用 DNS-01 验证的域名B. 攻击者能做什么为他人域名申请新证书证书私钥由攻击者控制可用于搭建钓鱼网站配合 DNS 劫持可实现中间人攻击C. 攻击者不能做什么无法获取原证书的私钥无法吊销他人的合法证书无法影响未在 LiteSSL 签发过的域名2. 间接影响A. 信任体系冲击CA 的核心职责是确保签发证书的鉴权准确性此漏洞违反了 CA/Browser Forum 基线要求可能影响 TrustAsia 的品牌信誉B. 生态影响用户对免费 ACME 服务的信任度下降引发对其他 CA 类似问题的担忧强调了证书透明度（CT）日志的重要性六、经验总结1. 厂商做得好的地方A. 响应及时收到通知后 2 小时内关闭服务24 小时内完成证书吊销积极与社区沟通B. 处理透明在原贴持续更新进展主动披露 Mozilla Bugzilla 报告承认问题，不推诿责任C. 修复彻底从审慎角度，重置所有授权状态计划发布详细根因分析考虑建立 SRC 安全响应中心2. 需要改进的地方A. 设计缺陷缓存机制未考虑账户隔离缓存有效期设置过长IP 获取逻辑存在基础错误B. 测试不足此类基础鉴权问题应在测试阶段发现缺少安全审计和渗透测试未充分考虑 ACME 协议的安全边界C. 监控缺失缺少异常签发行为监控没有证书透明度日志的自动监控告警3. 行业启示A. CA 运营教训免费服务和付费服务需同等安全标准ACME 协议实现需严格遵循 RFC 规范信任是 CA 的核心资产，一旦破坏难以恢复B. 安全开发建议鉴权系统需进行威胁建模缓存机制必须考虑权限边界多层防御：缓存隔离 + 速率限制 + 异常检测C. 社区监督价值白帽子披露对生态安全至关重要透明的漏洞披露流程有助于建立信任社区反馈渠道的有效性（邮件、在线客服）七、技术建议1. 对用户A. 立即行动检查是否使用 LiteSSL 签发的证书如受影响，按 TrustAsia 指引重新申请证书监控证书透明度日志，检测异常签发B. 长期措施启用证书透明度监控（如 Cloudflare CT Monitor）使用信誉良好的 CA（Let's Encrypt、DigiCert 等）定期审查证书来源和有效期2. 对 CA 运营者A. ACME 实现建议严格按照 RFC 8555 实现鉴权逻辑验证缓存必须与账户强绑定缩短缓存有效期或实现即时失效机制B. 安全加固graph LR A[ACME 请求] --> B{账户验证} B -->|失败| C[拒绝请求] B -->|成功| D{缓存检查} D -->|命中且账户匹配| E{缓存时效} D -->|未命中或账户不匹配| F[强制 DNS 验证] E -->|有效| G[使用缓存] E -->|失效| F F --> H[更新缓存] H --> I[签发证书] G --> IC. 监控告警监控同一域名的多账户签发行为异常证书签发行为告警定期审计证书透明度日志3. 对 ACME 客户端开发者A. 安全最佳实践为每个用户使用独立的 EAB避免共享 EAB 给多用户使用实现 ARI（ACME Renewal Information）特性B. 集成建议优先支持直连 ACME 模式如需代理模式，确保严格的账户隔离支持证书自动更新和吊销八、相关技术标准1. ACME 协议规范RFC 8555：ACME 协议定义RFC 8737：ACME 账户绑定外部账户RFC 8738：批量证书申请2. CA/Browser Forum 基线要求版本 1.8.0：域名验证要求证书透明度要求密钥泄露响应要求3. 证书透明度（CT）RFC 6962：证书透明度协议所有公开信任的证书必须录入 CT 日志用户可通过 crt.sh 等服务查询证书记录参考资料V2EX 原帖：LiteSSL 鉴权漏洞 可随意盗签他人泛域名证书Mozilla Bugzilla 报告：TrustAsia LiteSSL Security IncidentRFC 8555: ACME Protocol证书透明度日志查询
TLS Shunt TLS流量分流器技术分析一、概述1. 项目背景TLS Shunt Proxy 是一个专门用于分流 TLS 流量的工具，由 liberal-boy 开发并开源。该项目主要解决在同一端口上处理多种协议流量的需求，特别适用于 vmess + TLS + Web 方案实现，同时支持与 Trojan 共享端口。2. 核心功能A. SNI 分流根据 TLS Server Name Indication 扩展将流量分发到不同的虚拟主机B. HTTP 和无特征流量分流识别并分流 HTTP 流量和无特征的加密流量C. 静态网站服务内置静态网站服务器功能D. 自动证书管理支持从 LetsEncrypt 自动获取证书二、技术架构1. 工作原理graph LR A[客户端] -->|TLS 握手| B[TLS Shunt Proxy] B -->|SNI: vmess.example.com| C[VMess 后端] B -->|SNI: trojan.example.com| D[Trojan 后端] B -->|HTTP 流量| E[静态网站/代理] B -->|无特征流量| F[默认后端]2. 核心组件A. 监听模块支持 443 端口监听（HTTPS）支持 80 端口监听并重定向到 HTTPS可配置入站和出站缓冲区大小B. TLS 处理模块TLS 卸载：可选择是否解密 TLS 流量支持多种 TLS 协议版本（TLS 1.2、TLS 1.3）支持多种密钥类型（ed25519、p256、p384、rsa2048、rsa4096、rsa8192）C. 路由模块按 SNI 分流按 HTTP Path 分流按 ALPN 协议分流（HTTP/1.1、HTTP/2、h2c）D. 处理器模块proxyPass：流量转发fileServer：静态文件服务3. 流量识别流程sequenceDiagram participant C as 客户端 participant T as TLS Shunt participant V as VMess participant H as HTTP处理 participant S as 静态文件 C->>T: TLS 握手（携带 SNI） T->>T: 解析 SNI alt SNI = vmess.example.com T->>T: TLS 卸载 alt 流量是 HTTP T->>H: Path 匹配 H->>V: 转发到后端 H->>S: 静态文件服务 else 流量是 VMess T->>V: 直接转发 end else SNI = trojan.example.com T->>V: 不卸载 TLS，直接转发 end三、配置详解1. 基础配置# 监听地址 listen: 0.0.0.0:443 # HTTP 到 HTTPS 重定向 redirecthttps: 0.0.0.0:80 # 入站缓冲区大小（KB） inboundbuffersize: 4 # 出站缓冲区大小（KB） outboundbuffersize: 322. 虚拟主机配置每个虚拟主机对应一个 SNI，包含以下配置：A. TLS 卸载设置tlsoffloading: true # 是否解密 TLSB. 证书配置managedcert: false # 是否自动管理证书 keytype: p256 # 密钥类型 cert: /path/to/cert # 证书路径 key: /path/to/key # 私钥路径C. 协议配置alpn: h2,http/1.1 # ALPN 协议 protocols: tls12,tls13 # TLS 版本范围3. HTTP 流量处理http: paths: - path: /vmess/ws/ handler: proxyPass args: 127.0.0.1:40000 - path: /static/ trimprefix: /static handler: fileServer args: /var/www/static handler: fileServer args: /var/www/html4. HTTP/2 流量处理http2: - path: / handler: fileServer args: /var/www/rayfantasy - path: /vmess handler: proxyPass args: h2c://localhost:400025. 其他流量处理default: handler: proxyPass args: 127.0.0.1:40001四、部署方案1. 安装方法A. Linux AMD64 自动安装bash <(curl -L -s https://raw.githubusercontent.com/liberal-boy/tls-shunt-proxy/master/dist/install.sh)B. 配置文件位置/etc/tls-shunt-proxy/config.yamlC. 其他平台需自行编译安装2. 系统服务配置A. 赋予权限sudo setcap "cap_net_bind_service=+ep" /usr/local/bin/tls-shunt-proxyB. 运行用户程序以 tls-shunt-proxy 用户身份运行C. 调试运行sudo -u tls-shunt-proxy /usr/local/bin/tls-shunt-proxy -config /etc/tls-shunt-proxy/config.yaml3. 典型部署场景A. VMess + TLS + WebSocketvhosts: - name: vmess.example.com tlsoffloading: true http: paths: - path: /vmess/ws/ handler: proxyPass args: 127.0.0.1:40000 handler: fileServer args: /var/www/htmlB. Trojan 共享端口vhosts: - name: trojan.example.com tlsoffloading: false default: handler: proxyPass args: 127.0.0.1:8443C. 混合部署vhosts: - name: vmess.example.com tlsoffloading: true http: paths: - path: /vmess/ws/ handler: proxyPass args: 127.0.0.1:40000 - path: / handler: fileServer args: /var/www/html default: handler: proxyPass args: 127.0.0.1:40001五、技术特点1. 性能优化A. 缓冲区配置入站缓冲区：默认 4KB出站缓冲区：默认 32KB可根据吞吐量调整B. 内存与 CPU 权衡相同吞吐量和连接数下，缓冲区越大，内存消耗越大，CPU 消耗越少低吞吐量场景，过大的缓冲区可能增加延迟2. 协议支持A. TLS 协议TLS 1.2（仅支持 FS 且 AEAD 加密套件）TLS 1.3B. HTTP 协议HTTP/1.1HTTP/2h2c（HTTP/2 Cleartext）C. 密钥类型Ed25519P-256（默认）P-384RSA 2048RSA 4096RSA 81923. 高级功能A. Proxy Protocol 支持args: 127.0.0.1:40001;proxyProtocolB. Unix Socket 支持args: unix:/path/to/ds/fileC. Path 前缀修剪path: /static/ trimprefix: /static六、故障排查1. 常见问题A. 服务启动失败症状：service 无法启动解决方案：sudo setcap "cap_net_bind_service=+ep" /usr/local/bin/tls-shunt-proxy sudo -u tls-shunt-proxy /usr/local/bin/tls-shunt-proxy -config /etc/tls-shunt-proxy/config.yamlB. 证书权限错误错误信息：fail to load tls key pair for xxx.xxx: open /xxx/xxx.key: permission denied解决方案：确保 tls-shunt-proxy 用户有读取证书的权限C. 流量未正确分流排查步骤：检查 SNI 是否正确配置检查 tlsoffloading 设置是否正确检查 Path 匹配规则查看日志获取详细错误信息2. 调试技巧A. 配置验证使用测试配置文件运行，检查语法错误B. 流量抓包使用 tcpdump 或 Wireshark 分析流量特征C. 日志分析关注以下信息：连接建立日志SNI 解析结果路由匹配结果错误信息七、最佳实践1. 安全配置A. 证书管理生产环境建议使用 managedcert 自动获取证书确保 443 端口可被公网访问以满足 LetsEncrypt 要求定期检查证书有效期B. TLS 配置优先使用 TLS 1.3禁用不安全的加密套件合理选择密钥类型（P-256 平衡性能和安全性）2. 性能优化A. 缓冲区调优根据实际流量特征调整缓冲区大小：高吞吐量场景：增大缓冲区低延迟场景：减小缓冲区B. 连接复用启用 HTTP/2合理设置 keep-alive 超时3. 监控告警A. 关键指标连接数流量分布错误率响应时间B. 告警规则连接数异常证书即将过期后端服务不可用八、应用场景1. 科学上网服务VMess + TLS + WebSocketTrojan 共享端口多用户隔离2. Web 服务HTTPS 静态网站反向代理负载均衡3. 流量管理按 SNI 分流按 Path 分流协议识别和分类九、对比分析1. 与 Nginx 对比特性TLS ShuntNginxSNI 分流支持支持TLS 卸载支持支持无特征流量分流支持不支持配置复杂度简单较复杂性能优秀优秀2. 与 HAProxy 对比特性TLS ShuntHAProxy协议识别精细化基础配置方式YAML配置文件静态文件服务内置需配合后端学习曲线平缓陡峭十、总结TLS Shunt Proxy 是一个功能强大、配置简单的 TLS 流量分流工具，特别适合需要在一个端口上处理多种协议的场景。其核心优势在于：精细化的流量识别和分流能力灵活的配置选项良好的性能表现简单的部署方式对于需要实现 VMess + TLS + WebSocket、Trojan 或混合部署的用户来说，TLS Shunt Proxy 是一个理想的选择。参考资料TLS Shunt Proxy GitHub 仓库