news 2026/9/28 20:29:13

OpenBao Kubernetes 密钥引擎实战指南:Service Account 凭证的动态生成与管理

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
OpenBao Kubernetes 密钥引擎实战指南:Service Account 凭证的动态生成与管理
  • 后端
  • 认证鉴权
  • 密钥管理
  • 密码学

【免费下载链接】openbao

OpenBao is a software solution to manage, store, and distribute sensitive data including secrets, certificates, and keys.

项目地址:https://gitcode.com/GitHub_Trending/op/openbao
点击查看免费下载

导读

本文围绕 OpenBao 仓库中 internal/builtin/logical/kubernetes/README.md 所描述的 Kubernetes 密钥后端插件展开,讲解如何让 OpenBao 在 Kubernetes 集群内动态生成 Service Account 及其令牌(Token),并自动创建或绑定 RBAC 角色与绑定关系。读完本文,你将掌握该插件的安全模型、启用与配置方式、角色(Role)与凭证(Creds)管理流程,以及本地开发编译与单元/集成测试的完整方法。

插件定位:动态生成 Kubernetes Service Account 凭证

该插件是一个独立的 OpenBao(兼容 Vault 架构)后端插件,核心能力是生成 Kubernetes Service Account 令牌。与静态地把 Token 存入 OpenBao 不同,它属于动态密钥(Dynamic Secrets):每次调用凭证路径都会向 Kubernetes API 发起 TokenRequest,产出有时效、可吊销的短期令牌,并在租约过期时自动清理 OpenBao 在集群中创建的全部 Kubernetes 对象。

从 backend.go 的newBackend()可以看出,该后端注册了如下路径(Paths):

  • config:配置与 Kubernetes API 的连接参数;
  • creds/{name}:按角色生成动态凭证;
  • check:校验配置所需的环境变量是否齐备;
  • roles/:管理角色定义(含列表、读写、删除)。

后端类型为logical.TypeLogical,并声明了SealWrapStorage: ["config"],即连接配置会经过 OpenBao 的 Seal 包装加密后落盘,避免明文暴露 Service Account JWT。同时挂载了WALRollback与WALRollbackMinAge(默认10m,见 backend.go),用于在创建 Kubernetes 对象中途失败时回滚,保证数据一致性。

安全模型:为何需要 Kubernetes 签名的 Token

插件的认证模型要求在config中提供 Kubernetes 的 Service Account Token(service_account_jwt),OpenBao 用它作为 Bearer Token 调用 Kubernetes API。README 特别强调:

该 Token 通常不应被共享,但为了让 Kubernetes 成为可信第三方,OpenBao 必须校验一个由 Kubernetes 加密签名、且能传达 Token 持有者身份的凭证。

这意味着 OpenBao 信任的是"由 Kubernetes 密码学签名并可验证身份"的机制(如 Service Account JWT 签名校验),而非简单的口令。README 也说明,未来 Kubernetes 若支持更轻量的认证机制,插件会随之更新适配。

从 client.go 可以看到,newClient()使用rest.Config组装 k8s client-go 客户端:Host为 API 地址,BearerToken为配置中的 Service Account JWT,若配置了CACert则作为CAData用于校验 API Server 证书。因此,即便集群启用了双向 TLS,插件也能完成服务端证书验证。

运行在 Pod 内的默认值机制

path_config.go中定义了两个关键常量(path_config.go):

  • 本地 CA 路径:/var/run/secrets/kubernetes.io/serviceaccount/ca.crt
  • 本地 JWT 路径:/var/run/secrets/kubernetes.io/serviceaccount/token

当 OpenBao 自身也以 Pod 方式运行在集群中时,只要disable_local_ca_jwt为false(默认),且未显式配置kubernetes_ca_cert/service_account_jwt,插件会自动读取上述本地文件作为默认值(见configWithDynamicValues(),path_config.go)。为支持 Token 的轮换/续期,backend.go 使用带缓存的文件读取器:本地 JWT 每 1 分钟重读一次(jwtReloadPeriod,依据 Kubernetes 1.21 变更日志建议),本地 CA 每 1 小时重读一次(caReloadPeriod)。

启用插件:内置后端的一键启用

README 说明该插件已内置到 OpenBao/Vault 中,默认挂载路径为kubernetes。在运行中的 OpenBao 服务上执行:

$ vault secrets enable kubernetes Successfully enabled 'kubernetes' at 'kubernetes'!

启用后即可通过vault write kubernetes/config、vault write kubernetes/roles/xxx、vault read kubernetes/creds/xxx等路径操作。所有受支持路径都由pathConfig、pathRoles、pathCredentials、pathCheck四个路径定义提供(backend.go)。

配置连接:config 路径参数详解

config路径支持的参数在 path_config.go 中定义,完整清单如下:

参数类型默认值说明
kubernetes_hoststring环境变量推导Kubernetes API 地址。未设置时,若KUBERNETES_SERVICE_HOST与KUBERNETES_SERVICE_PORT_HTTPS环境变量存在,则自动推导为https://$KUBERNETES_SERVICE_HOST:KUBERNETES_SERVICE_PORT_HTTPS(getK8sURLFromEnv)
kubernetes_ca_certstring本地 Pod CAPEM 编码的 CA 证书,用于校验 Kubernetes API Server 证书;缺省时回退到本地 Pod 的ca.crt
service_account_jwtstring本地 Pod JWTOpenBao 调用 Kubernetes API 所用 Service Account 的 JWT;缺省时回退到本地 Pod 的token文件
disable_local_ca_jwtboolfalse置为true时禁用本地 CA 与本地 JWT 的自动回退

写入示例:

vault write kubernetes/config \ kubernetes_host=https://kubernetes.default.svc:443 \ kubernetes_ca_cert=@ca.crt \ service_account_jwt=@token

值得注意的实现细节:

  • 写入时若未显式给出kubernetes_host,且环境变量也无法推导,会直接报错kubernetes_host was unset and could not be determined from environment variables(path_config.go);
  • 读路径(pathConfigRead)不会回显service_account_jwt,因为它属于敏感数据(path_config.go);
  • 配置写入或删除后,会调用b.reset()置空已缓存的 client,使下一次请求基于新配置重建客户端;同时通过Invalidate钩子,当存储中的config被外部变更时也会自动reset()(backend.go)。

check路径则用于快速校验:它仅检查KUBERNETES_SERVICE_HOST与KUBERNETES_SERVICE_PORT_HTTPS两个环境变量是否已设置(path_check.go)。若齐全则返回204 No Content,否则返回缺失变量列表。注意:它只检查环境变量,并不校验 config 中已写入的连接参数。

角色管理:roles 路径与三种凭证模式

roles/{name}路径用于定义"如何生成凭证"。角色字段定义于 path_roles.go,核心参数如下:

参数类型说明
allowed_kubernetes_namespaces逗号分隔字符串列表允许生成凭证的命名空间;*表示全部允许
allowed_kubernetes_namespace_selectorstring命名空间的标签选择器(JSON/YAML 格式 LabelSelector);与上面的列表同时设置时取"与"关系
token_max_ttlduration(秒)生成 Token 的最大 TTL;0 或未设置时使用系统默认
token_default_ttlduration(秒)生成 Token 的默认 TTL;0 或未设置时使用系统默认
token_default_audiences逗号分隔字符串列表生成 Token 的默认 audience;未设置时使用集群默认
service_account_namestring为已存在的Service Account 生成 Token(仅生成 Token,不创建对象)
kubernetes_role_namestring为已存在的 Role/ClusterRole 绑定生成的 Service Account(创建 Service Account + RoleBinding + Token)
kubernetes_role_typestring上述角色类型:Role或ClusterRole,默认Role
generated_role_rulesstring由插件生成Role/ClusterRole 的规则(JSON/YAML 格式rbac/v1/Policy对象;将创建 Role + RoleBinding + ServiceAccount + Token 整条链路)
name_templatestring生成对象名的模板;未设置时使用默认模板
extra_labels/extra_annotationsKV 对附加到所有生成的 Kubernetes 对象上的标签/注解

角色创建示例:

vault write kubernetes/roles/my-role \ allowed_kubernetes_namespaces="default,dev" \ generated_role_rules='{"rules":[{"apiGroups":[""],"resources":["pods"],"verbs":["list"]}]}' \ token_default_ttl=3600 \ token_max_ttl=7200

三种凭证生成模式(核心用法)

从createCreds的switch分支(path_creds.go)可以看到三种互斥模式:

  1. 仅生成 Token:设置service_account_name指向集群中已存在的 Service Account,插件只调用CreateToken签发令牌;
  2. 绑定已有角色:设置kubernetes_role_name(配合kubernetes_role_type),插件创建新的 Service Account 和 RoleBinding/ClusterRoleBinding,再签发 Token;
  3. 全链路生成:设置generated_role_rules,插件依次创建 Role/ClusterRole → RoleBinding → ServiceAccount → Token。

写入时的校验逻辑(path_roles.go)包括:

  • allowed_kubernetes_namespaces与allowed_kubernetes_namespace_selector至少设置一个;
  • service_account_name、kubernetes_role_name、generated_role_rules三者中有且仅有一个被设置(onlyOneSet检查);
  • token_default_ttl不能大于token_max_ttl;
  • kubernetes_role_type必须是Role或ClusterRole(大小写不敏感,内部归一化);
  • 命名空间选择器必须是合法的LabelSelector,角色规则必须是合法的rbac/v1/Policy;
  • name_template必须能通过模板编译。

对象命名模板

默认命名模板定义在 path_roles.go:

{{ printf "v-%s-%s-%s-%s" (.DisplayName | truncate 8) (.RoleName | truncate 8) (unix_time) (random 24) | truncate 62 | lowercase }}

即形如v-<显示名前8位>-<角色名前8位>-<时间戳>-<24位随机串>,整体截断到 62 字符并转小写(以适配 Kubernetes 对象命名规范)。自定义时可通过name_template字段覆盖。

动态凭证获取:creds 路径与租约生命周期

凭证请求路径为creds/{role_name},请求参数(path_creds.go):

  • kubernetes_namespace(必填):目标命名空间;若角色只允许单一命名空间则可省略(自动填充);
  • cluster_role_binding(可选 bool):置为true时创建 ClusterRoleBinding 跨集群授权,此时角色必须为ClusterRole(否则报错a ClusterRoleBinding cannot ref a Role);
  • ttl(可选):请求级 TTL;
  • audiences(可选):请求级 audience。

调用示例:

vault read kubernetes/creds/my-role \ kubernetes_namespace=default \ ttl=3000 \ audiences=my-app

命名空间合法性校验

请求目标命名空间必须命中角色的白名单或标签选择器(isValidKubernetesNamespace,path_creds.go):

  • 若角色allowed_kubernetes_namespaces含*或包含该命名空间,直接通过;
  • 否则若配置了allowed_kubernetes_namespace_selector,插件会调用 Kubernetes API 获取该命名空间的真实标签(getNamespaceLabelSet,client.go),再用LabelSelectorAsSelector(...).Matches(...)做匹配;
  • 都不满足则返回错误响应。

TTL 计算与令牌签发

最终 TTL 的优先级为:请求ttl> 角色token_default_ttl> 系统默认租约 TTL;随后依次与角色token_max_ttl、OpenBao 系统最大租约 TTL 比较并封顶(超出时在响应Warnings中提示,path_creds.go)。签发通过 k8sauthentication/v1的TokenRequestAPI 完成(createToken,client.go),指定ExpirationSeconds与Audiences。

签发完成后,插件会解析令牌的exp/iat声明(getTokenTTL,path_creds.go),核对实际 Token TTL 与 OpenBao 租约 TTL 是否一致,若不一致会给出警告并将租约 TTL 调整到令牌实际 TTL。

响应结构与自动清理

成功响应返回service_account_namespace、service_account_name、service_account_token三个字段(kube_service_account.go中的 Secret 定义,kube_service_account.go)。

租约过期或被主动吊销时,kubeTokenRevoke(kube_service_account.go)会读取 Secret 内部数据,按创建时记录的created_role、created_role_binding、created_service_account依次调用deleteRole/deleteRoleBinding/deleteServiceAccount,将本次生成的全部 Kubernetes 对象清理干净(使用 go-multierror 聚合各步错误)。

中途失败的 WAL 回滚保障

创建多对象链路(如"Role + RoleBinding + ServiceAccount + Token")时,插件会先写入 WAL(Write-Ahead Log)条目再创建对象:createRoleWithWAL与createRoleBindingWithWAL(path_creds.go)。若后续步骤失败,WAL 回滚机制会在达到WALRollbackMinAge后清理孤儿对象;全部成功后会删除对应 WAL 条目(path_creds.go)。这就是 backend.go 中WALRollbackMinAge = "10m"的用途,集成测试目录中的wal_rollback_test.go专门验证了该回滚路径。

此外,创建的对象通过 OwnerReference 建立从属关系(如 RoleBinding 作为 ServiceAccount 的 owner),Kubernetes 自身的 GC 也能兜底清理孤儿资源。

源码目录速览

插件的完整实现集中在 internal/builtin/logical/kubernetes/ 目录:

  • backend.go:后端工厂、路径注册、缓存刷新与 WAL 配置;
  • path_config.go:连接配置的读写删;
  • path_roles.go:角色 CRUD 与校验;
  • path_creds.go:凭证生成主流程、TTL 计算与 WAL;
  • client.go:k8s client-go 封装(Token/ServiceAccount/Role/RoleBinding 增删);
  • kube_service_account.go:Secret 类型定义与吊销回调;
  • path_check.go:环境变量自检;
  • cmd/kubernetes/main.go:插件二进制入口,通过plugin.ServeMultiplex提供 gRPC 服务。

本地开发与编译

若要在本地编译该插件,需要先安装 Go。仓库内提供两套目标(对应 README 的make/make dev,定义于 Makefile):

make make dev

编译产物为bin/vault-plugin-secrets-kubernetes。dev目标只为本机平台生成二进制,速度更快;它还通过-ldflags将WALRollbackMinAge覆盖为10s(而非默认 10 分钟),以加快集成测试中的回滚验证。

注册为外部插件

把编译好的二进制放到指定目录,并在 OpenBao 服务配置中声明该目录为plugin_directory:

plugin_directory = "path/to/plugin/directory"

然后启动服务:

$ vault server -config=path/to/config.hcl ...

接着在插件目录(plugin catalog)中注册:

$ vault plugin register \ -sha256=<expected SHA256 Hex value of the plugin binary> \ -command="vault-plugin-secrets-kubernetes" \ secret kubernetes ... Success! Data written to: sys/plugins/catalog/kubernetes

注意:每次修改插件代码后都需要重新计算 sha256 校验和,例如用 openssl:

openssl dgst -sha256 $GOPATH/vault-plugin-secrets-kubernetes ... SHA256(.../go/bin/vault-plugin-secrets-kubernetes)= 896c13c0f5305daed381952a128322e02bc28a57d0c862a78cbc2ea66e8c6fa1

最后启用该 secrets 插件后端:

$ vault secrets enable kubernetes ... Successfully enabled 'plugin' at 'kubernetes'!

注意:启用外部插件时,kubernetes_host、kubernetes_ca_cert、service_account_jwt均需在config中显式配置——因为外部插件进程不运行在 Pod 内,本地文件自动回退机制不适用。

测试:单元测试与 kind 集成测试

单元测试

执行全部测试:

make test

该目标先运行fmtcheck,再执行go test ./... -timeout=20m。也可以使用TESTARGS过滤:

make test TESTARGS='--run=TestConfig'

仓库自带 backend_test.go、path_config_test.go、path_roles_test.go、path_creds_test.go、client_test.go 等测试文件,分别覆盖配置读写、角色校验、凭证生成与 k8s 客户端行为。

集成测试(基于 kind)

集成测试需要本地安装 kind(README 指明),完整流程为:

# 1. 创建用于测试的 Kubernetes 集群 make setup-kind # 2. 构建插件并注册到集群内运行的 OpenBao 实例 make setup-integration-test # 3. 针对集群内的 OpenBao 运行集成测试 make integration-test

Makefile 中的实现细节(Makefile):

  • setup-kind用kindest/node:v1.26.2创建名为vault-plugin-secrets-kubernetes的单控制面集群,配置见 integrationtest/kind/config.yaml(含 8200→38300 的端口映射,便于本地访问集群内 OpenBao);
  • vault-image先以GOOS=linux交叉编译插件,再用integrationtest/vault/Dockerfile构建hashicorp/vault:dev镜像;
  • setup-integration-test-common通过 Helm(vault chart 0.24.1,dev 模式)在test命名空间部署 OpenBao,注入-dev-plugin-dir=/vault/plugin_directory,并应用 integrationtest/vault/testRoles.yaml、testServiceAccounts.yaml、testBindings.yaml 等预置 RBAC 资源;
  • integration-test以INTEGRATION_TESTS=true运行 integrationtest 下的creds_integration_test.go、integration_test.go、wal_rollback_test.go,超时 40 分钟。

其中testRoles.yaml展示了插件运行所需的典型 RBAC 权限:对serviceaccounts/token的create、对namespaces的get、对serviceaccounts与rolebindings/roles/clusterrolebindings/clusterroles的create/delete。集成测试通过test-capabilitiesClusterRole 反向验证对象确实创建成功,并通过名为k8s-secrets-abilities-broken的残缺权限角色来测试 WAL 回滚与 ownerRef GC 兜底。

小结

OpenBao 的 Kubernetes 密钥引擎把"短期、动态、可吊销"的 Kubernetes 凭证管理能力带给了 OpenBao:运维只需定义角色与命名空间约束,应用每次读取creds路径即可获得有时效的 Service Account 令牌,租约到期后集群内生成的对象会被自动清理。结合源码可以看到,其可靠性依赖 TTL 三层封顶、TokenRequest 签名解析核对、WAL 回滚与 OwnerReference GC 多重机制。无论是直接启用内置后端,还是按 Makefile 编译注册外部插件,均可参照上文流程在本地复现完整的开发、测试与使用闭环。

  • 后端
  • 认证鉴权
  • 密钥管理
  • 密码学

【免费下载链接】openbao

OpenBao is a software solution to manage, store, and distribute sensitive data including secrets, certificates, and keys.

项目地址:https://gitcode.com/GitHub_Trending/op/openbao
点击查看免费下载

相关推荐

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/28 20:28:55

千问 8R 立减券申领通道,外卖打车都能用

安装千问这个软件&#xff08;未用过&#xff09;&#xff0c;然后打开对话输入字符口令&#xff08;9月实测稳定&#xff09;&#xff1a;新用户福利100012&#xff0c;操作方法如下&#xff1a;即可轻松领取&#xff01;小伙伴们可以抓紧去试一试吧~~

作者头像 李华
网站建设 2026/9/28 20:28:13

GPUStack DSpark:一行配置让大模型JSON输出提速3.8倍

最近给团队搭大模型推理服务的时候&#xff0c;发现很多人都在为“让模型输出合法 JSON”这件事头疼。我这次拿到一台 8 卡推理机&#xff0c;用 GPUStack 把 DeepSeek-V4.1 的 DSpark 模式跑通了。所谓 DSpark&#xff0c;就是 GPUStack 针对结构化 JSON 输出做的并行解码优化…

作者头像 李华
网站建设 2026/9/28 20:27:21

我的开源项目:Piral——为微前端而生的框架

这是我"我的开源项目"系列的第三篇文章&#xff0c;我会回顾一些我发起或维护的开源项目&#xff0c;讲讲它们背后的故事。第一篇是 AngleSharp&#xff0c;然后是 MAGES。这一次&#xff1a;Piral——一个用于微前端的框架&#xff0c;也是这个系列里第一个并非来自…

作者头像 李华
网站建设 2026/9/28 20:26:48

Keil MDK中printf重定向到串口的三种方法详解

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华