- 后端
- 认证鉴权
- 密钥管理
- 密码学
【免费下载链接】openbao
OpenBao is a software solution to manage, store, and distribute sensitive data including secrets, certificates, and keys.
导读
本文围绕 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_host | string | 环境变量推导 | Kubernetes API 地址。未设置时,若KUBERNETES_SERVICE_HOST与KUBERNETES_SERVICE_PORT_HTTPS环境变量存在,则自动推导为https://$KUBERNETES_SERVICE_HOST:KUBERNETES_SERVICE_PORT_HTTPS(getK8sURLFromEnv) |
kubernetes_ca_cert | string | 本地 Pod CA | PEM 编码的 CA 证书,用于校验 Kubernetes API Server 证书;缺省时回退到本地 Pod 的ca.crt |
service_account_jwt | string | 本地 Pod JWT | OpenBao 调用 Kubernetes API 所用 Service Account 的 JWT;缺省时回退到本地 Pod 的token文件 |
disable_local_ca_jwt | bool | false | 置为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_selector | string | 命名空间的标签选择器(JSON/YAML 格式 LabelSelector);与上面的列表同时设置时取"与"关系 |
token_max_ttl | duration(秒) | 生成 Token 的最大 TTL;0 或未设置时使用系统默认 |
token_default_ttl | duration(秒) | 生成 Token 的默认 TTL;0 或未设置时使用系统默认 |
token_default_audiences | 逗号分隔字符串列表 | 生成 Token 的默认 audience;未设置时使用集群默认 |
service_account_name | string | 为已存在的Service Account 生成 Token(仅生成 Token,不创建对象) |
kubernetes_role_name | string | 为已存在的 Role/ClusterRole 绑定生成的 Service Account(创建 Service Account + RoleBinding + Token) |
kubernetes_role_type | string | 上述角色类型:Role或ClusterRole,默认Role |
generated_role_rules | string | 由插件生成Role/ClusterRole 的规则(JSON/YAML 格式rbac/v1/Policy对象;将创建 Role + RoleBinding + ServiceAccount + Token 整条链路) |
name_template | string | 生成对象名的模板;未设置时使用默认模板 |
extra_labels/extra_annotations | KV 对 | 附加到所有生成的 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)可以看到三种互斥模式:
- 仅生成 Token:设置
service_account_name指向集群中已存在的 Service Account,插件只调用CreateToken签发令牌; - 绑定已有角色:设置
kubernetes_role_name(配合kubernetes_role_type),插件创建新的 Service Account 和 RoleBinding/ClusterRoleBinding,再签发 Token; - 全链路生成:设置
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-testMakefile 中的实现细节(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.
相关推荐
Meshery与Vault集成:密钥管理与动态凭证
Meshery与Vault集成:密钥管理与动态凭证 在云原生环境中,密钥和凭证的安全管理是保障系统安全的核心环节。手动管理静态密钥不仅效率低下,还存在密钥泄露、
云原生微服务运维DevOpsBoundary凭证管理终极指南:静态凭证与动态Vault集成实战
Boundary凭证管理终极指南:静态凭证与动态Vault集成实战 在现代IT架构中,安全高效的凭证管理是保护动态基础设施的核心环节。Boundary作为Has
Kubernetes ExternalJWT 全解析:Service Account 外部签名与密钥管理(proto 契约 + kube-apiserver 集成原理)
Kubernetes ExternalJWT 全解析:Service Account 外部签名与密钥管理(proto 契约 + kube apiserver 集
云原生容器编排集群管理微服务
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考