- 后端
- 认证鉴权
- 密钥管理
- 密码学
【免费下载链接】openbao
OpenBao is a software solution to manage, store, and distribute sensitive data including secrets, certificates, and keys.
OpenBao 的 PKI Secrets Engine 允许安全工程师以远低于传统工作流的成本创建 PKI 证书链。本文将深入剖析 OpenBao 前端仓库中ui/lib/pki这个独立 Ember Engine 的架构设计,重点讲解其数据模型(Model)、适配器(Adapter)与序列化器(Serializer)如何映射 PKI 后端复杂多变的 HTTP 端点,帮助你理解"为何 PKI 数据无法干净地映射到 CRUD 模型",并掌握在 UI 中签发证书、生成根 CA、导入 CA、配置自动 tidy 的完整实现原理。
为什么 PKI 在 UI 层需要一套"非典型"架构
PKI 是出了名的复杂:一次POST请求往往不产生单一实体,而是可能同时创建多个 issuer、key 与 certificate,且响应中部分敏感数据(如私钥、签发 CA 链)只在请求返回时出现一次。这与 Ember Data 惯用的 CRUD 范式(create / read / update / delete 单条记录)并不契合。
因此,OpenBao 前端为 PKI 设计了专门的模型、适配器与序列化器。这些代码并不位于ui/lib/pki引擎内部,而是主应用中(这也是 Ember Engine 的典型用法——引擎通过依赖注入使用主应用中的 Ember Data 层)。ui/lib/pki引擎则负责路由、控制器、组件与模板,构成完整的用户界面。
从目录结构看(ui/lib/pki/addon/routes),PKI 引擎拥有以下核心功能分区:
issuers/:根 CA 生成(generate-root)、中间 CA 生成(generate-intermediate)、导入(import)、轮换(rotate-root)、交叉签名(cross-sign)、签发中间证书(sign)certificates/:证书列表与详情keys/:密钥生成(create)与导入(import)roles/:角色的创建、编辑、签发(generate/sign)tidy/:手动 tidy、自动 tidy 配置configuration/:PKI 配置项的创建与编辑
除pki/action之外,其余每个模型在 UI 中都有对应标签页,指向其LIST视图。
pki/action:面向"动作"而非"实体"的操作模型
pki/action模型(ui/app/models/pki/action.js)用于执行各种POST请求。这些请求接收相似的参数,但并不会创建一个单一的 Ember Data 记录;一次动作可能产生多个包含与请求参数不同属性的条目。例如:
POST pki/generate/root/:type创建自签名的根 CA 证书(即一个 issuer)与私钥,私钥仅在type = exported时才返回POST pki/issuer/:issuer_ref/sign-intermediate签发证书,并返回仅出现一次的签发 CA 与 CA 链数据
从源码看,模型通过@tracked actionType在不同表单字段之间切换(ui/app/models/pki/action.js),支持的 actionType 包括import、generate-root、generate-csr、sign-intermediate、rotate-root。
适配器:actionType 到端点的映射
pki/action适配器(ui/app/adapters/pki/action.js)的核心逻辑在urlForCreateRecord中,它根据actionType把请求映射到对应端点:
| actionType | 使用 issuer 端点(新式) | 旧式端点(fallback) |
|---|---|---|
import | /issuers/import/bundle | /config/ca |
generate-root | /issuers/generate/root/${type} | /root/generate/${type} |
generate-csr | /issuers/generate/intermediate/${type} | /intermediate/generate/${type} |
sign-intermediate | /issuer/${issuerRef}/sign-intermediate | — |
rotate-root | /root/rotate/${type} | — |
这里有两个值得注意的设计细节:
新旧端点并存与能力探测:模型中使用
@lazyCapabilities对issuers/import/bundle、issuers/generate/root/:type等新端点做能力探测(ui/app/models/pki/action.js),并通过canImportBundle、canGenerateIssuerRoot、canGenerateIssuerIntermediate、canCrossSign等 getter 决定 UI 是否展示对应操作。如果用户没有新端点的权限,则回退到旧路径。请求 ID 作为记录 ID:由于 action 端点不对应单一实体,
createRecord在 POST 成功后以result.request_id(或随机 UUID)作为 Ember Data 记录 ID(ui/app/adapters/pki/action.js),并让序列化器根据actionType决定发送哪些属性。
使用pki/action模型的 PKI 工作流包括:根证书生成与轮换、导入 CA 证书与密钥、生成中间 CA CSR、签发中间证书。
关键属性与校验规则
pki/action模型承载了大量表单字段(ui/app/models/pki/action.js),其中最关键的有:
type:生成根/中间 CA 时的密钥处理方式,可选exported、internal、existing、kmscommonName:必需字段(presence 校验)keyType/keyBits/privateKeyFormat:密钥类型与格式,keyType可选rsa、ed25519、ec、mldsa;privateKeyFormat可选der、pkcs8format:证书输出格式,可选pem、der、pem_bundle,默认pemissuerName/keyName:自定义命名,二者均不能使用保留值default(见模型内自定义校验,ui/app/models/pki/action.js)- SAN 系列:
altNames(DNS SAN)、ipSans、uriSans、otherSans excludeCnFromSans:将 CN 从 DNS/Email SAN 中排除,适用于 CN 是人工可读标识符而非主机名的场景notBeforeDuration:证书回溯有效期,默认30s,用于纠正各系统间的时钟偏差customTtl/ttl/notAfter:证书有效期(TTL 或具体日期)maxPathLength:路径长度约束,默认-1
生成或签发 CSR 时还会用到csr、caChain、keyId、privateKey、privateKeyType等属性;其中certificate、issuingCa、privateKey等敏感字段被标记为masked: true,在 UI 中脱敏展示。
pki/certificate/base:证书数据模型
pki/certificate/base(ui/app/models/pki/certificate/base.js)用于与证书数据的具体交互。base 模型包含组成证书内容的通用属性,其子类certificate/generate与certificate/sign(ui/app/models/pki/certificate/generate.js、ui/app/models/pki/certificate/sign.js)在此基础上追加属性以完成各自的请求。
base 模型中的属性大致分三类:
- 请求参数:
commonName、customTtl、excludeCnFromSans、altNames、ipSans、uriSans、otherSans - API 返回内容:
caChain、certificate、expiration、issuingCa、privateKey(仅type=exported与/issue返回)、privateKeyType、revocationTime(格式化日期)、serialNumber - 解析结果:
parsedCertificate
parsedCertificate:前端证书解析器
parsedCertificate是一个对象,承载由parse-pki-cert.js工具(ui/app/utils/parse-pki-cert.js)返回的全部证书解析数据。
该工具基于asn1js、pkijs与pvutils在浏览器端完成 ASN.1 解码:先把 PEM 字符串剥离-----BEGIN/END CERTIFICATE-----与换行符,转成 DER 后再用 ASN.1 解析为证书对象(ui/app/utils/parse-pki-cert.js)。解析结果包含 Subject 字段、Extensions(含 OID、KeyUsage、SAN 类型等,常量定义在 ui/app/utils/parse-pki-cert-oids.js)、签名位数等。
错误处理约定也很明确:{ can_parse: false }表示外部库无法转换证书;{ parsing_errors: [] }表示证书可转换但存在解析问题——此时 UI 无法进行交叉签名,会提示用户改用 CLI 手动操作。certDisplayFields(certificate、commonName、revocationTime、serialNumber)则定义了证书详情页直接展示的字段(ui/app/models/pki/certificate/base.js)。
此外,模型通过@lazyCapabilities探测revoke端点,canRevoke决定 UI 是否展示吊销操作(ui/app/models/pki/certificate/base.js)。
pki/tidy:手动与自动清理的统一模型
pki/tidy(ui/app/models/pki/tidy.js)用于在不同上下文中管理 tidy 操作。以下三个端点共享几乎相同的参数,唯一的例外是enabled与intervalDuration(仅用于自动 tidy):
POST pki/tidy—— 执行一次手动 tidyPOST pki/config/auto-tidy—— 设置自动化 tidy 的配置GET pki/config/auto-tidy—— 读取自动 tidy 配置
注意:
pki/tidy-status是只读端点,因此不使用 Ember Data 模型。
适配器:手动与自动的分流
pki/tidy适配器(ui/app/adapters/pki/tidy.js)对两类操作做了严格区分:
- 自动 tidy 配置是唯一持久化的数据,因此
findRecord与updateRecord只与/config/auto-tidy端点交互:findRecord走GET,updateRecord走POST。模型 ID 就是后端挂载路径,整个 mount 只有一个持久化的pki/tidy记录。 - 每次手动 tidy 都是新记录,
save()时调用createRecord,且只使用/tidy端点。
适配器还会根据tidyType(auto/manual)抛错来阻止误用:手动模型不能findRecord,自动模型不能createRecord。此外,cancelTidy(backend)提供对POST pki/tidy-cancel的封装,用于取消正在运行的 tidy 操作(ui/app/adapters/pki/tidy.js)。
tidy 参数分组
模型源码将全部 tidy 参数组织为三组(ui/app/models/pki/tidy.js),这正是 UI 表单分组的依据:
Universal operations(通用操作):
tidyCertStore:清理证书存储tidyRevokedCerts:从存储中移除所有无效与过期证书tidyRevokedCertIssuerAssociations:清理已吊销证书与 issuer 的关联safetyBuffer:证书被清除前,需超过(本地时钟的)证书过期时间加上该缓冲时长,默认 72 小时pauseDuration:逐张清理证书之间的暂停时长,可释放吊销锁,让其他操作在 tidy 运行期间继续执行
ACME operations(ACME 操作):
tidyAcme:清理 ACME 账户、订单与授权acmeAccountSafetyBuffer:无订单账户被标记吊销、以及被标记吊销/停用后保留的时长
Issuer operations(Issuer 操作):
tidyExpiredIssuers:在 issuer 安全缓冲时长过后自动移除过期 issuertidyMoveLegacyCaBundle:将旧版 CA/issuer 捆绑包(来自 Vault 1.11 之前、OpenBao 分叉之前的版本)备份到config/ca_bundle.bak,迁移仅在 issuer 安全缓冲期过后发生issuerSafetyBuffer:issuer 在其 NotAfter 有效期之后应保留的时长,默认 365 天(8760 小时)
自动 tidy 专属参数为enabled(是否启用自动清理,默认false)与intervalDuration(两次自动 tidy 之间的间隔,注意:是从一次操作结束到下一次开始计算)。
CRUD 模型:issuer、role、key
与pki/action不同,以下模型更接近标准 CRUD 模式。
pki/issuer:证书颁发者
Issuer 由pki/action模型通过导入 CA 或生成根 CA 创建(ui/app/models/pki/issuer.js),本模型负责其 update / read / list:
- 只读字段:
issuerId、isDefault、keyId(默认密钥 ID)、caChain、certificate、serialNumber,以及从证书内容解析出的parsedCertificate、commonName、isRoot、各类 SAN - 可更新字段:
issuerName、leafNotAfterBehavior(叶子证书有效期超过 issuer 时的处理:err/truncate/permit)、usage(issuing-certificates/crl-signing/ocsp-signing)、manualChain(手动指定证书链,第一个元素必须是当前 issuer 的引用)、revocationSignatureAlgorithm(构建 CRL 时的签名算法,默认留空由 Go 自动选择)、issuingCertificates、crlDistributionPoints、ocspServers - 能力探测:
canRotateIssuer(根轮换)、canCrossSign(交叉签名)、canSignIntermediate(签发中间 CA)、canConfigure、canDeleteAllIssuers分别对应/root/rotate/*、/intermediate/cross-sign、/issuer/:issuerId/sign-intermediate、/issuer/:issuerId、/root端点的权限(ui/app/models/pki/issuer.js)
pki/role:签发角色
Role 是 PKI 中"策略化签发"的核心实体,支持 create/update、read、list。模型源码将全部配置项组织成清晰的表单分组(ui/app/models/pki/role.js):
- 默认组:
name、issuerRef(指定签发证书的 issuer,默认default,可通过read -field=default pki_int/config/issuers查询)、customTtl、notBeforeDuration(默认30s)、maxTtl(未设置时使用系统最大 lease TTL)、generateLease(签发的证书是否附带 lease)、noStore(不在存储后端保存证书——可提升大批量签发性能,但证书无法枚举或吊销)、addBasicConstraints - Domain handling:
allowedDomains、allowedDomainsTemplate、allowBareDomains、allowSubdomains、allowGlobDomains、allowWildcardCertificates、allowLocalhost(默认 true)、allowAnyName、enforceHostnames(默认 true) - Key parameters:
keyType(rsa/ec/ed25519/mldsa/any,默认rsa)、keyBits(默认2048)、signatureBits(仅 RSA 适用,可选0/256/384/512) - Key usage:
keyUsage(默认['DigitalSignature', 'KeyAgreement', 'KeyEncipherment'])、extKeyUsage、extKeyUsageOids - Policy identifiers:
policyIdentifiers(OID 列表) - SAN Options:
allowIpSans(默认 true)、allowedUriSans、allowUriSansTemplate、allowedOtherSans - Additional subject fields:
allowedUserIds、allowedSerialNumbers(支持 shell 风格 glob,留空则禁止自定义序列号)、requireCn(默认 true)、useCsrCommonName(默认 true)、useCsrSans(默认 true)以及 OU/组织/国家等身份元数据
角色在权限控制上也十分精细:canDelete、canEdit、canRead基于/roles/:id端点;canGenerateCert基于/issue/:id;canSign基于/sign/:id;canSignVerbatim基于/sign-verbatim/:id(ui/app/models/pki/role.js)。
pki/key:密钥管理
pki/key(ui/app/models/pki/key.js)的CREATE有两条路径:
- generate:生成新密钥,
type可选internal(私钥不返回、之后也无法取回)或exported(私钥在响应中返回);keyType可选rsa、ec、ed25519、mldsa - import:通过
pemBundle导入已有密钥
模型同样包含read/list能力,并通过canRead、canEdit、canDelete、canGenerateKey、canImportKey控制 UI 操作入口(ui/app/models/pki/key.js)。校验规则要求type与keyType必选,且keyName不能是保留值default。
引擎内部:路由、组件与序列化器如何协作
除数据层外,ui/lib/pki引擎还提供了完整的页面组件(ui/lib/pki/addon/components),例如:
- 配置向导类:
pki-configure-create、pki-configuration-edit、pki-generate-root、pki-issuer-generate-root、pki-issuer-import、pki-issuer-generate-intermediate、pki-issuer-rotate-root、pki-issuer-cross-sign - 表单组件:
pki-role-form、pki-key-form、pki-tidy-form、pki-sign-intermediate-form、pki-import-pem-bundle、pki-key-usage、pki-key-parameters、pki-not-valid-after-form - 展示组件:
parsed-certificate-info-rows(渲染parsedCertificate的证书信息行)、pki-info-table-rows、pki-overview - 状态页:
pki-tidy-status、pki-tidy-manual、pki-tidy-auto-settings
路由(ui/lib/pki/addon/routes)与模板(ui/lib/pki/addon/templates)按issuers、certificates、keys、roles、tidy、configuration划分,与各模型的 LIST 视图一一对应。
序列化器(ui/app/serializers/pki)则负责两端工作:一方面把表单属性按目标端点筛选后发送(例如pki/action序列化器按actionType决定发送哪些字段,见 ui/app/adapters/pki/action.js 中serializer.serialize(snapshot, actionType)的调用);另一方面把后端返回的证书内容解析为parsedCertificate,供详情页直接渲染。
小结:一张 PKI 前端数据流全景图
综合来看,OpenBao PKI UI 引擎的数据流可以概括为:
- 用户在路由/组件中触发操作(生成根、导入 CA、签发证书、配置 tidy 等);
- 对应模型(
pki/action负责非 CRUD 动作,pki/issuer、pki/role、pki/key负责 CRUD,pki/tidy统一手动/自动清理)通过@lazyCapabilities探测权限,决定 UI 展示哪些入口; - 适配器根据 actionType / tidyType 将请求映射到正确的端点(
ui/app/adapters/pki/action.js、ui/app/adapters/pki/tidy.js); - 序列化器筛选发送字段,后端返回后由
parse-pki-cert.js在浏览器端完成 ASN.1 解码,生成parsedCertificate供详情页与交叉签名等高级功能使用。
这套"模型 + 适配器 + 序列化器 + 引擎组件"的分层设计,正是 OpenBao 在前端优雅地消化 PKI 后端复杂 API 的关键所在——它让安全工程师可以在图形界面中完成从根 CA 建立到证书签发、再到自动清理的全生命周期管理,而无需直接面对底层千变万化的 HTTP 端点。
- 后端
- 认证鉴权
- 密钥管理
- 密码学
【免费下载链接】openbao
OpenBao is a software solution to manage, store, and distribute sensitive data including secrets, certificates, and keys.
相关推荐
Vault PKI 双引擎解读:从 Internal 到 External PKI 的 Ember Engine 界面架构与实现
Vault PKI 双引擎解读:从 Internal 到 External PKI 的 Ember Engine 界面架构与实现 导读 本仓库在 ui/lib/
后端密钥管理认证鉴权身份认证应用安全Dynamic-Datasource数据源适配器:适配器模式深度解析
Dynamic Datasource数据源适配器:适配器模式深度解析 在当今的微服务架构中, 动态数据源切换 已成为企业级应用不可或缺的核心能力。dynamic
后端数据库sqlite-vss与Datasette集成:打造可视化向量搜索平台
sqlite vss与Datasette集成:打造可视化向量搜索平台 sqlite vss是一个基于Faiss的SQLite扩展,为SQLite数据库带来了高效
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考