关于我们

质量为本、客户为根、勇于拼搏、务实创新

< 返回新闻公共列表

2026 服务器租用集群密钥怎么进 Git:Sealed Secrets 加密边界与轮换 实测对比 + 避坑全解

发布时间:2026-10-22

2026 服务器租用集群密钥怎么进 Git:Sealed Secrets 加密边界与轮换 实测对比 + 避坑全解

GitOps 的纪律其实就一句话:集群里跑的每一样东西,都要能在仓库里找到出处。Deployment、Service、Ingress、ConfigMap 都能乖乖进 Git,唯独 Secret 卡在那儿——它装着数据库口令、对象存储 AK、第三方回调 Token。明文进仓库是事故,不进仓库又等于亲手拆掉"单一事实来源"这块地基。于是很多小团队走到一个很尴尬的中间态:配置全部走 Argo CD 或 Flux 同步,唯独十几个 Secret 是某人当年在跳板机上 kubectl apply 出来的,没人记得当时填了什么,也不敢动。

Sealed Secrets 就是冲着这个缝来的。它不追求做一个完整的密钥保管库,它只回答一个问题:怎么让密文能堂而皇之地躺在 Git 里,同时让 Git 的读权限不等于密钥的读权限。这个定位决定了它的优点和它的天花板,也决定了它什么时候够用、什么时候该换人。下面我按"边界—机制—取舍—轮换—落地—替代方案—红线"这条线讲,重点放在那些真正会咬人的地方:scope 选错、密钥备份忘了、把 renewal 当成 rotation。

得先说清楚本文口径。Sealed Secrets 的行为细节我以官方仓库 README 为准;社区教程里对个别参数名的写法口径不一(比如有些文章写 --renew,官方仓库里实际是控制器的 --key-renew-period 与客户端的 kubeseal --re-encrypt),这类地方我在正文里点出来。涉及"实测"的部分,指的是可由任何人复现的操作性验证——比如把 strict scope 的密文挪到另一个命名空间,控制器会直接报解不开——而不是我这边跑出来的性能数字,性能与报价相关的东西一律以官网实时信息或咨询确认为准。

把 Secret 提交进仓库之前,先看清 base64 到底解决了什么

第一件事必须掰扯明白:Kubernetes 的 Secret 不是加密,是 base64 编码。这两者的区别不是程度问题,是性质问题。编码不需要任何密钥,任何拿到 manifest 的人,一条 base64 -d 就能看到口令原文。kubectl get secret db-cred -o yaml 出来的那一串,跟明文是等价的。很多团队默认"反正它是 Secret,应该安全",这个误解几乎是所有后续事故的起点。

再往下看一层。Secret 落到 etcd 里默认也是明文的(base64 只是客户端呈现层的编码)。也就是说,谁拿到一份 etcd 的冷备份,谁就拿到了整个集群的凭据,连 API Server 都不用碰。Kubernetes 官方提供了一套 EncryptionConfiguration,可以挂到 kube-apiserver 上,用 AES-CBC、AES-GCM 或者外部 KMS 把 secrets 资源在写盘之前包一层;托管集群上更常见的做法是交给云厂商 KMS 做信封加密。静态加密确实该开,它是任何集群的基础控制项。

静态加密解决的不是"能不能进 Git"这件事

但请把边界画清楚:静态加密解决的是"落盘和备份泄露",它完全不解决"这个 manifest 能不能提交到仓库",也不解决"谁有权限 kubectl get secret"。一个开了 etcd 静态加密的集群,你把明文 Secret 提交进 Git,泄露程度一点没减少——Git 历史是永久的,重写历史也不能把已经泄露的东西收回去,克隆过仓库的人、跑过那个分支的 CI runner 都已经有了一份。所以正确的分层是三件事,各管一个洞:静态加密管磁盘,RBAC 管 API 访问,而"明文根本不进 Git"这件事得靠 Sealed Secrets、SOPS 或外部保管库这类工具来管。

还有个容易忽略的点:RBAC 粒度。给某个 Deployment 的 ServiceAccount 授予 get secrets 时,如果用通配符匹配全部 Secret,等于把这个命名空间里所有凭据都给了它。用 resourceNames 逐个列出该读的 Secret 名字,才是真的按最小权限来。这一层跟用不用 Sealed Secrets 无关,都得做。

kubeseal 加密、控制器解密:一次密封的加密边界在哪

Sealed Secrets 由两部分组成:跑在集群里的控制器(controller / operator),和跑在你本机或 CI 里的客户端命令行工具 kubeseal。官方仓库对机制的描述很直接——kubeseal 用非对称加密把 Secret 加密成只有控制器能解开的形态,产物是一个 SealedSecret 自定义资源,API 组是 bitnami.com/v1alpha1,密文放在 spec.encryptedData 下面,每个键一段独立的密文块。把这个资源 apply 进集群,控制器watch 到之后用自己手里的私钥解开,在同命名空间生成一个普通的 Secret,Pod 照常挂载或注入环境变量,应用代码一行都不用改。

关键在于"非对称"三个字带来的分工。用来加密的是密钥证书的公钥部分,官方明确写了它不是机密信息(not secret information),可以随 kubeseal 分发到任何需要加密的地方。所以 kubeseal 支持离线密封:先用 kubeseal --fetch-cert 把公钥证书拉下来存成 pem,之后用 --cert 指定这个文件就能加密,全程不需要连集群、也不需要任何高权限。但要确保用的是正确集群的正确证书——证书拿错了,加密会成功,解密会失败,而且失败发生在几天后别人 apply 的时候。

加密是单向的:你不能"解密看一眼"

官方 README 有一句很硬的表述:SealedSecret 只能由目标集群里的控制器解密,其他任何人都无法从 SealedSecret 还原出原始 Secret,连原作者也不行。这句话是 Sealed Secrets 全部安全性的来源,也是它Workflow 上最别扭的地方——你没法把提交过的密文解回来看一眼确认填对了没有。想改其中一个值,正确做法是重新拿明文再封一次;明文在你手上的密码管理器里,不在仓库里。这也意味着"仓库里有密文"不等于"仓库是明文来源",明文必须另有保管处,否则哪天真要重建集群,你会发现自己既没有私钥备份、也没有明文,两条路都断了。

两个实践细节。一是 kubeseal 提供 --raw 模式,可以直接封单个值而不是整个 Secret 对象,改一个字段时很好用,省得把整份重建一遍。二是版本对齐:老版本 kubeseal 对着新版本控制器(或反过来)可能报出很难懂的错,写进脚本和 CI 镜像时把两边版本钉死在同一个号上,能省掉一次半夜排障。另外,官方 FAQ 里确实给了离线解密的后门:kubeseal --recovery-unseal --recovery-private-key 配合备份下来的私钥文件,可以离线解开 SealedSecret。这条命令的存在本身就是个提醒——谁拿着私钥备份,谁就能读全部密文。

strict、namespace-wide、cluster-wide 三种 scope 的真实取舍

scope 是 Sealed Secrets 里最容易踩、也最容易被低估的一块。三种取值:strict(默认)要求解封时 Secret 的名字和命名空间都与加密时完全一致;namespace-wide 只绑定命名空间,同一个命名空间里可以自由改名;cluster-wide 不绑任何东西,可以在任意命名空间、以任意名字解封。设置方式有两种,命令行 kubeseal --scope namespace-wide,或者在输入 Secret 上打注解 sealedsecrets.bitnami.com/namespace-wide: "true" / sealedsecrets.bitnami.com/cluster-wide: "true";两个注解都打时 cluster-wide 优先。官方还提到后续版本会收敛成单一的 sealedsecrets.bitnami.com/scope 注解,写自动化脚本时留个心。

为什么改名就废了:名字是织进密文里的

很多人以为 strict 是控制器在做一次校验,其实不是。官方的说法是 name 和 namespace 会成为加密数据的一部分(part of the encrypted data),换任何一个都会导致解密错误。实际表现就是:把一份 strict 密文改掉 namespace 再 apply,资源会被接受,然后控制器解不开,status 里给出 no key could decrypt secret 之类的信息,并产生 ErrUnsealFailed 事件,Secret 根本不会生成。这是刻意的防重放设计——它保证了"能在 Git 里读到这份密文"不等于"能把这段密文变成你自己的凭据"。

我的选择建议是这样的:默认 strict,别动。namespace-wide 只在一种情况下用——同一个命名空间里有几个不同名字的 Secret 需要共享同一个值(比如同一个数据库口令被两个 Deployment 各引用一份)。cluster-wide 我几乎不给真密钥用,它把防重放的保护整个拆掉了,任何能在任意命名空间创建 Secret 的人,都能把你的密文挂到他自己控制的名下读出来;只适合那些其实不算秘密的东西。另外有个常被忽略的例外:encryptedData 下面的键名(item key)是可以随便改的,不受 scope 影响,重命名 data 里的字段不会破坏解密。

环境 promotion 怎么办?跨环境不要靠放宽 scope 来省事。正确姿势是 Kustomize overlay 或 Helm values 按环境分目录,每个环境各自 seal 一次——多一次 kubeseal 调用而已,不是瓶颈。真正麻烦的场景是明文已经丢了、只有密文,这时候要挪命名空间就只能找回明文重封。所以"明文另存一份"这件事我再强调一次。

密钥 30 天自动轮换,旧密钥为什么不删

控制器在首次启动时生成密钥对,之后按周期自动续期。官方 README 写得很明确:密封密钥每 30 天自动续期一次,所谓续期是生成一把新密钥并追加进控制器可用于解封的活跃密钥集合里;最近生成的那一把是 kubeseal 密封新密钥时用的,也是 kubeseal --fetch-cert 拉下来那一把。周期可以用控制器的 --key-renew-period 参数调整,取值是 Go 的 duration 格式(比如 720h30m,注意不能用 d 表示天);设成 0 会关掉自动续期,官方对新手顺手关掉这事是有保留意见的。

renewal 不是 rotation,这俩别混

这里有个历史包袱值得单独拎出来讲。这个功能历史上被叫做 key rotation,于是很多人以为旧密钥会作废、应该清掉。官方专门写了"关于密钥续期的常见误解"来纠正:SealedSecret 不会自动轮换,生成新密钥时旧密钥也不会被删除,老的 SealedSecret 资源照样能解开。这不是疏漏——如果新密钥一生成旧密钥就失效,仓库里所有历史密文会同时变砖,整个方案就没法用了。所以严格讲这是 renewal(续期/追加),不是 rotation(轮换/作废)。

那么轮换后要不要重新密封?不需要立刻做,旧密钥还在,老密文照常工作。但长期不做等于一直让老密钥保护着仓库里的密文,而老密钥目前不会自动垃圾回收。要把已有密文搬到新密钥上,用 kubeseal --re-encrypt,它会把密文回传控制器、用最新密钥重新加密后吐出一个新文件,明文不出集群,也不会更新集群里的对象。我一般建议:每次续期后对活跃环境的密文跑一遍 re-encrypt,尤其是怀疑密钥出过问题的时候。

怀疑泄露时的操作顺序不能反

提前续期用控制器的 --key-cutoff-time 参数或环境变量 SEALED_SECRETS_KEY_CUTOFF_TIME,值用 RFC1123 格式,一条 date -R 就能生成。官方文档强调了顺序:如果你知道或怀疑密封私钥泄露了,必须在开始轮换真实凭据之前先把密钥续掉,否则你新改的口令还是会被封进那把已经泄露的密钥里,等于白改。续完之后才是改真密码、重新密封。而且要说清楚:续期和 re-encrypt 都不替代真实凭据的定期轮换——官方的原话是 SealedSecret 的密钥续期与重新加密功能不是真实密钥定期轮换的替代品。泄露就是泄露,旧密文永远在攻击者手上。

顺便纠正一处口径差异。有些教程提到一个 --renew 参数,官方仓库 README 里没有这个独立 flag;与续期相关的实际是控制器侧的 --key-renew-period 和 --key-cutoff-time,以及与重封相关的 kubeseal --re-encrypt。这属于公开资料口径不一,以官方仓库文档为准。

私钥备份、控制器重建与节点重装的恢复路径

先把结论摆在最前面:控制器私钥就是全部秘密,丢了它,仓库里所有 SealedSecret 永久变砖——不是"很难恢复",是真的解不开,没有任何后门。而私钥在集群里的存在形式,只是一个普通的 Kubernetes Secret,住在控制器所在的命名空间(默认 kube-system),带标签 sealedsecrets.bitnami.com/sealed-secrets-key,值为 active 或 compromised。它本身没有被密封,它是信任根。

官方 FAQ 给的备份方式很朴素:kubectl get secret -n kube-system -l sealedsecrets.bitnami.com/sealed-secrets-key -o yaml 导出成文件;如果你在这个集群上装过 0.9.x 之前的版本,还要再补一条 kubectl get secret -n kube-system sealed-secrets-key。官方专门加了三条提示:这个文件里含公私钥对,务必妥善保管;密钥续期之后要重新做一次备份,否则旧备份解不开新密文;恢复时把文件 apply 回去,再删掉控制器 Pod 让它重新加载。恢复可以在启动控制器之前做,也可以之后替换掉新建的密钥再重启控制器。

恢复演练才是分水岭

我见过太多团队"备份做了"但从没验过。备份这件事的价值只有在恢复成功那一刻才成立,而恢复失败往往发生在最糟的时间点——集群已经没了,人正在慌。建议每个季度做一次低成本演练:起一个临时命名空间或临时集群,把备份 apply 进去,装控制器,随便找一份仓库里的 SealedSecret apply 上去,确认 Secret 真的生成了。整个过程十几分钟,能把"我们备份过"变成"我们知道它能恢复"。

还有两个细节。一是控制器对手动创建、删除或改标签的密钥的感知行为,不同版本表现不一致,历史上需要管理员重启控制器才生效,较新的提交中加入了对密钥 Secret 增删改的动态检测;这块属于版本相关、公开资料口径不一,落地时以你所用版本的官方仓库文档为准。二是标签值改成 active 以外的值,等于把这把密钥从控制器视野里摘掉,但它在集群里还在,可以留作离线恢复用——这正好是"标记 compromised 但仍保留可解密能力"的操作空间。

CI 流水线里怎么接 kubeseal,多环境多集群要不要共用一套密钥

CI 接入这件事比想象中简单,因为 kubeseal 在 CI 里只需要公钥,不需要任何集群凭据。典型部署场景是这样:把 kubeseal 二进制或用官方容器镜像放进流水线镜像,公钥证书 pem 作为一个受控产物随仓库或配置分发(它不是机密,可以放仓库里,但要保证来源正确),流水线从密码管理器或 CI 的 masked variable 里取出明文值,走 kubectl create secret --dry-run=client -o yaml 管道喂给 kubeseal,产出的 SealedSecret 提交回仓库。明文只在流水线内存里过一遍,不落盘、不进仓库、不进构建日志。

三个必须加的护栏。第一,pre-commit 钩子里拦截 kind: Secret 且不是 SealedSecret 的文件——这是唯一一个能一击致命的失误,值得用工具挡住。第二,kubeseal 与控制器版本对齐,钉死版本号。第三,流水线里禁止 kubectl get secret -o yaml 之后直接入库,这个反射动作会绕过整条链路;同样,helm install --set password=xxx 会把明文写进 Helm release 历史,要加密就加密 values 文件本身。

多集群:一钥多用还是一集群一钥

共用一套密封密钥的好处很实在:一份密文可以在多个集群解开,迁移和灾备都省事,新集群恢复时不用重新封全部密钥。代价同样直接——一把钥匙开所有的门,测试集群的密钥泄露会顺带把生产密文交给对方。我的默认建议是:生产独立一把,非生产(开发、测试、预发)可以共用一把,生产之间如果有主备或同城双活,视灾备要求决定是否共用。判断标准就一条:你能不能接受"最弱的那个集群被攻破时,最强的那个集群的密文同时暴露"。

多环境也不要靠共用密文来省事。即使共用密钥,环境之间的差异(数据库地址、账号、配额)本来就应该分开存储、分别密封,能不能解开是能力问题,该不该解开是管理问题。按环境分目录、每环境独立 seal,仓库结构清晰,出问题时候的回滚边界也清楚。

密文评审看不到明文:改密流程与协作摩擦怎么消化

这是 Sealed Secrets 在团队协作层面最真实的代价,很多人上手前没想到。PR 里能看到的东西只有加密后的 encryptedData,评审者无法判断这次改的是哪个环境的哪个字段、值填对了没有、有没有把测试库地址写进生产。于是密文 PR 的评审会退化成"点头合并",这在安全上其实是个洞——你失去了对凭据变更的第二双眼睛,而凭据变更恰恰是最需要复核的变更。

能做的补偿有几种。把明文放在密码管理器里作为真正的来源,仓库里只放密文,PR 描述里写清楚"本次变更哪个键、取值来自哪条记录、生效窗口、回滚方式",评审者核对的是描述与流程而不是值本身。涉生产的关键凭据走双人复核:一人提交密文,另一人核对描述与密码管理器记录后合并。这个流程听起来重,实际一年也就十几次。

改密的正确顺序

常规改密:先在源头(数据库、第三方平台)改掉真实口令并确认新值可用 → 用新明文重新密封 → 提交 PR → 合并后观察连接错误 → 留好回滚预案。注意顺序里"先改源头再改仓库"的原因:如果先改仓库再改源头,中间窗口里集群用的是新口令、源头上还是旧口令,服务会直接断。

怀疑密封私钥泄露时的顺序不一样,前面提过但值得重复:先 --key-cutoff-time 续密钥 → 再改真口令 → 用新密钥重新密封。顺序反了,新口令会被封进泄露的那把密钥里。谁需要掌握明文?执行改密的人需要,其他人不必——这本身是一种权限收缩,也是 Sealed Secrets 相对"所有人都能 kubectl get secret"的进步。代价是人员离职或交接时,明文清单要跟着交接。

和 External Secrets Operator、SOPS、Vault、etcd 静态加密怎么分工

把四个方案的差别讲清楚,判断就好做了。External Secrets Operator(ESO)的思路是"仓库里只有引用":ExternalSecret 资源描述去哪个后端取哪个键,真正的明文住在 AWS Secrets Manager、GCP Secret Manager、Azure Key Vault、HashiCorp Vault 等外部保管库里,ESO 按 refreshInterval 轮询同步成集群内的原生 Secret。它的杀手锏是审计与自动轮换——改一次外部保管库里的值,所有集群在刷新周期内跟着变,而且外部保管库自带访问审计。代价是运行时依赖:保管库不可达,新 Pod 拿不到还没缓存的密钥就会起不来。

SOPS 走的是另一条路:值级加密,文件结构保留。一份 SOPS 加密后的 manifest,apiVersion、kind、metadata、键名都是明文的,只有值变成 ENC[...] 块,秘钥可以是 age 也可以是云 KMS。Flux 对 SOPS 有原生支持,Argo CD 需要插件。它的好处是 diff 友好(能看出改了哪个键)和不绑定 Kubernetes(非 K8s 的配置文件也能用),代价是要把解密密钥交给 CD 工具、且每个要用的人都要装 sops 二进制。

Vault 那条线分两种形态:Vault Agent Sidecar Injector 通过 mutating webhook 注入 sidecar,把密钥写成共享内存卷里的文件,应用读文件而不读环境变量;Secrets Store CSI Driver 更进一步,密钥以卷的形式挂载,压根不落 Kubernetes Secret 对象、不进 etcd。Vault 还能发动态凭据——按需生成、带租约、自动过期。能力最强,运维成本也最高:Vault 集群本身的高可用、存储后端、unseal 流程、策略体系,都得有人扛。

etcd 静态加密跟上面三者不是竞争关系,是正交的。上面三种方案最终都在集群里产出一个 base64 编码的原生 Secret(CSI 除外),静态加密管的是这个 Secret 落盘后安不安全。所以完整形态通常是"静态加密 + RBAC + 三者之一"。

方案 密钥明文存放位置 仓库里放什么 轮换与吊销方式 灾备恢复依赖 适用规模
Sealed Secrets 加密后进 Git,明文另存密码管理器 密文(SealedSecret),可进公开仓库 手动改密重封;密钥 30 天自动续期,re-encrypt 可换到新密钥;无集中吊销 控制器私钥备份 + 明文备份,缺一不可 单集群或少量集群、小团队、离线/私有环境
SOPS(age / KMS) 加密后进 Git,密钥由团队或云 KMS 持有 值级密文,键名与结构可见 手动改密重加密;KMS 可集中吊销,age 需改 recipient 列表并重加密 age 私钥或多个 recipient 中的应急密钥 Flux 用户、需要非 K8s 配置共用一套加密、中小团队
External Secrets Operator 外部保管库(Vault、云厂商 Secret Manager 等) 仅引用(ExternalSecret),不含任何密文 源端改值后按 refreshInterval 自动同步;后端可集中吊销与审计 保管库自身的灾备 + 集群到保管库的认证凭据 多集群共享、有合规审计要求、已有 Vault 或云 KMS 投入
Vault Agent / CSI Driver Vault,可发动态凭据 注解或 SecretProviderClass,不含值 租约自动续期与回收,动态凭据到期即失效 Vault 集群高可用与 unseal 流程 对审计、动态凭据、短生命周期令牌有硬要求的场景
etcd 静态加密 仍在集群内,仅落盘加密 不涉及,不管仓库 不涉及密钥轮换,只管存储层 EncryptionConfiguration 的 KEK 与 etcd 快照 所有集群的基础项,与上面任一方案叠加

这张表怎么读?我建议按"两个时刻"去看,而不是按功能多少去看。第一个时刻是轮换:谁的轮换是人驱动的、谁是后端驱动的。Sealed Secrets 和 SOPS 都是人驱动——改一次要人工重封一遍,频率高了就没人愿意改,于是轮换实际会停摆;ESO 和 Vault 是后端驱动,改一次源头全部集群跟着走,轮换才可能真的按周期发生。第二个时刻是重建:集群整个没了之后,你要拿什么才能把服务拉起来。Sealed Secrets 依赖私钥备份和明文备份两份东西,SOPS 依赖 age 私钥(建议配一个应急 recipient),ESO 依赖保管库本身和集群到保管库的认证。把这两列看明白,选型的八成争议就没了。

还有一条经验:同一个集群里同一类凭据只选一种方案。混用会制造"这个密钥到底住哪儿"的困惑,而这种困惑在半夜排障时会变成实打实的停机时间。允许的特例是分层——比如 TLS 证书交给 cert-manager 自己管(它本来就会生成 Secret,而且按自己的节奏轮换,别去加密或外置它),业务凭据统一走一种方案。

私钥就是全部秘密:RBAC、审计与几条不能踩的红线

说完机制,红线得单独拎出来说清楚,但这些是防御边界,不是吓唬人。

第一,密封私钥等于全部历史密文。官方的表述很重:如果密封密钥以任何形式流出集群,你必须认为所有用它加密过的 SealedSecret 都已经泄露,集群里再怎么轮换密钥、再怎么 re-encrypt 都改变不了这一点。所以谁能读 kube-system 下带那个标签的 Secret,谁就等价于能读全部密文。这条 RBAC 必须收紧到最小集合,而且要定期审一遍 RoleBinding / ClusterRoleBinding,看有没有人无意间拿到了通配的 secrets 读权限。

第二,私钥备份不能进同一个 Git 仓库。密文在仓库里、密钥也在仓库里,加密就白做了。备份应该进密码管理器或离线保管库,与仓库物理分离。同理,别把备份贴进工单、聊天记录、截图。

第三,别把 SealedSecret 当成"已脱敏数据"到处贴。它只对持有那把私钥的那个集群脱敏。贴到公开仓库、技术论坛求助帖、第三方 SaaS 工单里,如果对方同时能拿到你的私钥备份(比如你为了排查问题把备份传给了外包),密文就不再是密文。日常沟通里涉及凭据,一律只描述键名和用途。

第四,控制器密钥 Secret 本身不要被 GitOps 一起管进去。有些团队把整个 kube-system 的清单同步进仓库,很容易把私钥一起同步了。同步策略要么显式排除带 sealedsecrets.bitnami.com/sealed-secrets-key 标签的资源,要么干脆不同步这个命名空间。

第五,审计是 Sealed Secrets 的短板,要提前承认。它没有"谁在什么时候读了哪个密钥"这类集中审计能力,控制器解封动作留下的痕迹有限。如果你的合规要求里明确写了凭据访问审计,那这个方案大概率不满足,该上 ESO 或 Vault。把这条写在选型文档里,比上线半年后被审计问住要好得多。

租用服务器上的控制面怎么放:磁盘、快照与密钥备份分离

换到服务器租用的视角,这套东西落到物理机上就变成几个很具体的运维决策。自建 K8s 集群的控制面(etcd、API Server、Sealed Secrets 控制器、GitOps 控制器)通常跑在几台独立的物理机或裸金属上,这些机器的选型直接决定了你的恢复能力。

etcd 那几台,磁盘是第一个要较真的地方。etcd 对写延迟极度敏感,fsync 慢一点就会触发集群不稳定甚至频繁选主,所以 etcd 的数据目录应该放在独立的 NVMe 或企业级 SSD 上,不要和日志、容器镜像、数据库共用一块盘抢 IO。控制面节点数量按奇数来(三台起步),跨机架或跨可用区摆放,避免一台交换机带走整个控制面。这些机型、带宽与报价在不同地区差异很大,一律以官网实时信息或咨询确认为准,别照着别人家的配置单抄。

快照与密钥备份必须分开存

这是我最想强调的一条:快照不等于密钥备份。很多团队做了整机快照或云盘快照就觉得高枕无忧,但快照里同时包含了 etcd 数据和控制器私钥,一旦快照泄露,攻击者同时拿到了密文(通过 etcd 里的 Secret)和解密能力(通过私钥),整套防护在同一份文件里被打包送走。正确做法是两条独立的恢复链:etcd 快照与控制面整机快照走一套存储与权限;控制器私钥导出文件加密后走另一套(密码管理器或离线保管库),访问人名单单独维护,且明显小于能碰快照的人。节点重装时的恢复顺序也随之确定:先把私钥 apply 进新集群,再装 Sealed Secrets 控制器,最后才让 GitOps 控制器把仓库里的密文同步下来解封——顺序反了会出现一批解不开的 SealedSecret,场面很难看。

一万网络深耕 IDC 行业 19 年(成立于 2007 年),在华南、华东、华北及中国香港、海外多地整合自营节点与 BGP 多线资源,提供服务器租用、机柜托管与带宽接入的一体化交付;具体机型、带宽与报价以官网实时信息或咨询确认为准。这类控制面集群放在租用物理机上时,我一般会建议把 etcd 节点的独立盘、控制面的整机备份策略、以及离线密钥保管这三件事在合同阶段就谈清楚,比事后补方案省事得多。另外,控制面机器的带外管理口、IPMI 权限同样属于"能读全部密钥"的能力范畴,权限清单里别忘了它。

什么时候 Sealed Secrets 够用,什么时候该换方案

够用的场景我判断得比较宽:单一集群或两三个集群、团队规模不大、没有强制的集中审计要求、网络是私有或离线的(这一点很关键,ESO 依赖外部保管库可达,私有化环境里这个依赖本身就是负担)、以及团队刚开始做 GitOps、需要一个小时内能把密钥管理跑起来的情况。这些条件下 Sealed Secrets 的性价比非常高——装一个控制器,改一下仓库结构,事情就结束了,没有额外的运行时依赖。

不够用的信号也很好认:集群数量上去了、同一份凭据要跨集群共享、轮换频率高到人工跟不动、合规要求里出现"凭据访问审计""集中吊销"这类字眼、或者你们已经在用 Vault 或云厂商的 Secret Manager。这些情况下继续用 Sealed Secrets,你会不断用人力去补方案本身缺的能力,最后算下来比直接上 ESO 贵。

真要迁移,别一刀切。选一个非关键命名空间做试点,把 SealedSecret 和新的方案并行跑一两周,确认恢复路径和轮换流程都顺了再铺开;试点期间保持可逆,别急着删旧密文。一万网络这边接触到的典型部署场景里,最常见的稳妥路径是先在一个边缘或内部业务集群上验证,再动核心集群——密钥管理这类改造,出问题时的影响面和它的 silent 特性(平时看不出问题,出事就是全量)决定了它必须慢慢来。

最后一句立场:不要因为"没想好"就什么都不做。明文 Secret 躺在仓库里,或者十几个 Secret 靠人工在跳板机上维护,比选错加密方案要危险得多。先选一个,把它用对,比继续犹豫强。

Sealed Secrets 落地前最该问清楚的八个问题

SealedSecret 提交到公开仓库,真的就安全了吗?

有前提的"安全"。官方的说法是只有目标集群里的控制器能解密,连原作者都不能从密文还原明文,所以理论上密文放公开仓库也没问题。但这个前提建立在三件事上:控制器私钥没泄露、你用的是 strict 或至少不是 cluster-wide 的 scope、以及仓库里没有同时存在私钥备份。私钥一旦泄露,所有用它加密过的密文回溯性暴露,再怎么轮换密钥都改变不了这一点。所以准确的说法是"密文可以进 Git",而不是"密文可以到处贴"。

改了 Secret 的名字或挪了命名空间,为什么控制器解不开了?

因为默认 strict scope 下,name 和 namespace 是织进加密数据本身的,不是控制器后加的一道校验。改任何一个,解密就会在数学层面失败,典型表现是资源被接受但状态里出现解不开的提示,并产生解封失败事件,目标 Secret 不会生成。解决方式只有重新密封:拿明文再跑一次 kubeseal。想让同一命名空间内可自由改名就用 --scope namespace-wide,想跨命名空间用 --scope cluster-wide,但后者等于放弃防重放保护,我不建议给真凭据用。这类失败还有个迷惑之处:资源会被 API Server 正常接受,只是 Secret 迟迟不出现,得去看 SealedSecret 的 status 和控制器事件才知道原因。把"改名即重封"写进团队 Wiki,能省掉不少排查时间。

密封密钥 30 天自动续期,我是不是不用管了?

恰恰相反,续期自动、但两件别的事是自动的:一是备份不会自动更新,官方明确提示密钥续期后要重新做一次私钥备份,否则旧备份解不开新密文;二是已提交的密文不会自动搬到新密钥上,要搬得手动跑 kubeseal --re-encrypt。更重要的是,续期不等于轮换——旧密钥不会被删,也不会被自动回收,这是为了让历史密文继续可用。真实口令的定期轮换跟密钥续期是两件事,官方明确说了续期和重封都不能替代改真密码。我的习惯是给活跃环境定一个季度一次的重封窗口,顺带把私钥备份重做一遍,两件事放在同一个操作里,不容易漏。

控制器私钥真的丢了,还有救吗?

没有。控制器私钥是唯一能解开仓库里全部 SealedSecret 的东西,丢了就是永久解不开,不存在恢复后门。唯一的补救是回到明文源头:从密码管理器里把值取出来重新密封一遍。这就是为什么我一再强调"明文另存一份"和"私钥备份与仓库分离"——这两份东西缺一份,重建集群时就会卡死。建议每季度做一次恢复演练,起个临时集群把备份导进去、装控制器、apply 一份仓库里的密文,确认 Secret 真的生成了。

多个集群共用一套密封密钥,到底是好是坏?

是权衡,不是对错。共用的好处是密文可跨集群解开、新集群恢复不用重封全部密钥、主备切换简单;代价是一把钥匙开所有门,最弱的那个集群被攻破,最强的那个集群的历史密文同时暴露。我的默认建议是生产独立一把、非生产可以共用一把,判断标准就是你能不能接受"测试环境失守导致生产密文暴露"。即使共用密钥,各环境的凭据也应该分开存储和分别密封,环境之间的账号、地址、配额本来就不该相同。另一种折中是主备集群共用一把、其余独立,这样灾备切换不用重封,同时把暴露面控制在同等级的两个集群之间。

只想改 Secret 里的一个字段,必须整份重做吗?

不用。kubeseal 提供 --raw 模式,可以把单个值直接封成密文,适合改一个字段的场景,避免重建整份 Secret。另外 encryptedData 下面的键名是可以自由重命名的,不受 scope 约束,改 data 里的字段名不会破坏解密,这一点跟改 Secret 名字完全不是一回事,别搞混。但要注意改的是"值"这件事本身——你仍然需要持有明文,无法从现有密文里把值读出来再改,官方 FAQ 里那条 --recovery-unseal 离线解密是给持有私钥备份的人应急用的,不是日常工具。这也是为什么明文清单必须和密码管理器同步维护。

Sealed Secrets 和 External Secrets Operator 能一起用吗?

技术上能,但我强烈建议不要对同一类凭据混用。混用会造成"这个密钥到底住在哪里"的认知混乱,而这种混乱在半夜排障时会直接变成停机时间。可以接受的是按凭据类型分层:比如业务数据库口令统一走一种方案,TLS 证书交给 cert-manager 自己管(它生成的 Secret 按自己的节奏轮换,不要去加密或外置),CI 用的机器人 Token 走另一种。分层可以,同一个凭据同时进两套系统不行。真要从 Sealed Secrets 迁到 ESO,建议按命名空间切:先迁一个非关键业务,两套并行跑一两周,确认恢复路径和轮换流程都通了再铺开,试点期间旧密文别急着删,保持可逆。

已经开了 etcd 静态加密,还需要 Sealed Secrets 吗?

需要,两者管的根本不是同一个洞。静态加密解决的是 Secret 落盘后、以及 etcd 快照泄露时的暴露问题,它不改变 kubectl get secret 就能拿到明文这件事,也完全不管你的 manifest 能不能提交进仓库。Git 历史是永久的,明文一旦提交,重写历史也收不回来,克隆过仓库的人和跑过那个分支的 CI runner 都已经有了一份。完整的分层应该是:静态加密管磁盘、RBAC 管 API 访问、Sealed Secrets 或同类工具管明文不进仓库,三层各管各的,缺任何一层都会留下一个具体可指认的洞。开了静态加密就以为万事大吉,是最常见的一种自我安慰。

数据来源

1. bitnami-labs/sealed-secrets 官方仓库 README(Overview 与 Scopes 章节):控制器与 kubeseal 两组件架构、非对称加密机制、strict / namespace-wide / cluster-wide 三种 scope 的定义与 --scope 及注解设置方式、公钥证书非机密可用于离线密封。

2. bitnami-labs/sealed-secrets 官方仓库 README(Secret Rotation、Early key renewal、Re-encryption、Common misconceptions about key renewal 章节):默认 30 天自动续期、--key-renew-period 参数、--key-cutoff-time 与 SEALED_SECRETS_KEY_CUTOFF_TIME 提前续期、旧密钥不删除、kubeseal --re-encrypt、续期与真实密钥轮换的关系。

3. bitnami-labs/sealed-secrets 官方仓库 README(FAQ 与 Details 章节):私钥备份命令与"续期后需重建备份"提示、离线解密的 kubeseal --recovery-unseal 用法、密钥 Secret 的标签 sealedsecrets.bitnami.com/sealed-secrets-key 与 active / compromised 取值。

4. Kubernetes 官方文档 Secrets 与 Encrypting Secret Data at Rest 章节:Secret 为 base64 编码而非加密、默认以明文形式存于 etcd、EncryptionConfiguration 与静态加密的作用边界。

5. External Secrets Operator 官方文档 external-secrets.io:ExternalSecret / SecretStore / ClusterSecretStore 三类自定义资源、从外部保管库同步为原生 Secret 的工作方式。

6. Mozilla SOPS 官方仓库与 HashiCorp Vault 官方文档:SOPS 基于 age / 云 KMS 的值级加密模型;Vault Agent Sidecar Injector 与 Secrets Store CSI Driver 的注入方式、动态凭据与租约机制。

7. 公开技术社区中关于 Sealed Secrets 与 SOPS、ESO、Vault 对比的讨论与操作演示(含 scope 行为复现、私钥备份与恢复流程、CI 集成方式):多方口径基本一致,但在"--renew"这一参数名上存在不一致表述,本文以官方仓库 README 为准;未标注具体数值的性能与成本项,以官方实时报价或咨询确认为准。


上一篇:每次发布总有用户报错:连接排空和优雅停机的几秒差在哪

下一篇:机柜还剩一半 U 位却装不进新机器:托管扩容先卡电力还是先卡空间