bitnami-labs/sealed-secrets

GitHub: bitnami-labs/sealed-secrets

这是一个适用于 Kubernetes 的控制器与客户端工具,通过非对称加密将普通 Secret 封装为可安全存储于 Git 仓库的 SealedSecret 对象。

Stars: 9032 | Forks: 767

# 适用于 Kubernetes 的 "Sealed Secrets" [![](https://img.shields.io/badge/install-docs-brightgreen.svg)](#Installation) [![](https://img.shields.io/github/release/bitnami-labs/sealed-secrets.svg)](https://github.com/bitnami-labs/sealed-secrets/releases/latest) [![](https://img.shields.io/homebrew/v/kubeseal)](https://formulae.brew.sh/formula/kubeseal) [![Build Status](https://static.pigsec.cn/wp-content/uploads/repos/2026/04/f1c74598a9063416.svg)](https://github.com/bitnami-labs/sealed-secrets/actions/workflows/ci.yml) [![](https://img.shields.io/github/v/release/bitnami-labs/sealed-secrets?include_prereleases&label=helm&sort=semver)](https://github.com/bitnami-labs/sealed-secrets/releases) [![Download Status](https://img.shields.io/docker/pulls/bitnami/sealed-secrets-controller.svg)](https://hub.docker.com/r/bitnami/sealed-secrets-controller) [![Go Report Card](https://goreportcard.com/badge/github.com/bitnami-labs/sealed-secrets)](https://goreportcard.com/report/github.com/bitnami-labs/sealed-secrets) ![Downloads](https://img.shields.io/github/downloads/bitnami-labs/sealed-secrets/total.svg) **问题:** “我可以在 git 中管理我所有的 K8s 配置,除了 Secrets。” **解决方案:** 将您的 Secret 加密成一个 SealedSecret,这*是*安全 可存储的——甚至可以存储在公共仓库中。SealedSecret 只能被运行在目标集群中的 controller 解密, 其他人(甚至包括原作者)都无法从 SealedSecret 中获取原始 Secret。 - [概述](#overview) - [作为 secret 模板的 SealedSecrets](#sealedsecrets-as-templates-for-secrets) - [公钥 / 证书](#public-key--certificate) - [作用域](#scopes) - [安装说明](#installation) - [在受限环境中的安装(无 RBAC)](#installation-in-restricted-environments-no-rbac) - [Controller](#controller) - [Kustomize](#kustomize) - [Helm Chart](#helm-chart) - [在受限环境中的 Helm Chart](#helm-chart-on-a-restricted-environment) - [Kubeseal](#kubeseal) - [Homebrew](#homebrew) - [MacPorts](#macports) - [Nixpkgs](#nixpkgs) - [Linux](#linux) - [从源代码安装](#installation-from-source) - [升级](#upgrade) - [支持的版本](#supported-versions) - [与 Kubernetes 版本的兼容性](#compatibility-with-kubernetes-versions) - [使用方法](#usage) - [管理现有的 secrets](#managing-existing-secrets) - [修补现有的 secrets](#patching-existing-secrets) - [加密可以跳过设置 owner references 的 secret](#seal-secret-which-can-skip-set-owner-references) - [更新现有的 secrets](#update-existing-secrets) - [原始模式(实验性)](#raw-mode-experimental) - [验证 Sealed Secret](#validate-a-sealed-secret) - [Secret 轮换](#secret-rotation) - [密封密钥更新](#sealing-key-renewal) - [密钥注册表初始化优先级顺序](#key-registry-init-priority-order) - [用户 secret 轮换](#user-secret-rotation) - [提前更新密钥](#early-key-renewal) - [关于密钥更新的常见误解](#common-misconceptions-about-key-renewal) - [手动密钥管理(高级)](#manual-key-management-advanced) - [重新加密(高级)](#re-encryption-advanced) - [详情(高级)](#details-advanced) - [加密算法](#crypto) - [开发](#developing) - [FAQ](#faq) - [我可以一次性加密多个 secrets 吗,在一个 YAML / JSON 文件中?](#can-i-encrypt-multiple-secrets-at-once-in-one-yaml--json-file) - [如果你不再拥有集群的访问权限,你还能解密吗?](#will-you-still-be-able-to-decrypt-if-you-no-longer-have-access-to-your-cluster) - [我该如何备份我的 SealedSecrets?](#how-can-i-do-a-backup-of-my-sealedsecrets) - [我可以使用备份密钥离线解密我的 secrets 吗?](#can-i-decrypt-my-secrets-offline-with-a-backup-key) - [kubeseal 有哪些可用标志?](#what-flags-are-available-for-kubeseal) - [我如何更新使用 sealed secrets 加密的 JSON/YAML/TOML/.. 文件的一部分?](#how-do-i-update-parts-of-jsonyamltoml-file-encrypted-with-sealed-secrets) - [我可以使用自己的(预生成的)证书吗?](#can-i-bring-my-own-pre-generated-certificates) - [如果 controller 没有运行在 `kube-system` namespace 中,如何使用 kubeseal?](#how-to-use-kubeseal-if-the-controller-is-not-running-within-the-kube-system-namespace) - [如何验证镜像?](#how-to-verify-the-images) - [如何使用一个 controller 管理一部分 namespaces](#how-to-use-one-controller-for-a-subset-of-namespaces) - [我可以配置 Controller 的解密重试吗?](#can-i-configure-the-controller-unseal-retries) - [如何在集群范围内或特定的 namespaces 中管理 SealedSecrets?](#how-to-manage-sealedsecrets-across-the-cluster-or-specific-namespaces) - [社区](#community) - [相关项目](#related-projects) ## 概述 Sealed Secrets 由两部分组成: - 集群端的 controller / operator - 客户端实用工具:`kubeseal` `kubeseal` 实用工具使用非对称加密来加密只有 controller 才能解密的 secrets。 这些加密的 secrets 被编码在一个 `SealedSecret` 资源中,你可以将其视为创建 secret 的配方。 它的样子如下所示: ``` apiVersion: bitnami.com/v1alpha1 kind: SealedSecret metadata: name: mysecret namespace: mynamespace spec: encryptedData: foo: AgBy3i4OJSWK+PiTySYZZA9rO43cGDEq..... ``` 一旦解密,这将产生一个等效于以下的 secret: ``` apiVersion: v1 kind: Secret metadata: name: mysecret namespace: mynamespace data: foo: YmFy # <- base64 encoded "bar" ``` 这个普通的 [kubernetes secret](https://kubernetes.io/docs/concepts/configuration/secret/) 将在几秒钟后出现在集群中, 你可以像使用任何直接创建的 secret 一样使用它(例如,从 `Pod` 中引用它)。 跳转到 [安装说明](#installation) 部分以开始运行。 [使用方法](#usage) 部分将更详细地探讨如何制作 `SealedSecret` 资源。 ### 作为 secret 模板的 SealedSecrets 前面的示例仅关注加密的 secret 项本身,但 `SealedSecret` 自定义资源与其解密成的 `Secret` 之间的关系在许多方面(但并非所有方面)类似于熟悉的 `Deployment` 与 `Pod` 之间的关系。 特别是,`SealedSecret` 资源的 annotations 和 labels 与由此生成的 `Secret` 的 annotations 是不同的。 为了捕捉这种区别,`SealedSecret` 对象有一个 `template` 部分,它编码了你希望 controller 放入解密后的 `Secret` 中的所有字段。 除了默认的 Go 文本模板函数外,还可使用 [Sprig 函数库](https://masterminds.github.io/sprig/)(除了 `env`、`expandenv` 和 `getHostByName`)。 `metadata` 块按原样复制(`ownerReference` 字段将被更新,[除非被禁用](#seal-secret-which-can-skip-set-owner-references))。 其他 secret 字段被单独处理。`type` 和 `immutable` 字段被复制,`data` 字段可用于在 `Secret` 上[模板化复杂值](docs/examples/config-template)。目前忽略所有其他字段。 ``` apiVersion: bitnami.com/v1alpha1 kind: SealedSecret metadata: name: mysecret namespace: mynamespace annotations: "kubectl.kubernetes.io/last-applied-configuration": .... spec: encryptedData: .dockerconfigjson: AgBy3i4OJSWK+PiTySYZZA9rO43cGDEq..... template: type: kubernetes.io/dockerconfigjson immutable: true # this is an example of labels and annotations that will be added to the output secret metadata: labels: "jenkins.io/credentials-type": usernamePassword annotations: "jenkins.io/credentials-description": credentials from Kubernetes ``` Controller 会将其解密为类似这样的内容: ``` apiVersion: v1 kind: Secret metadata: name: mysecret namespace: mynamespace labels: "jenkins.io/credentials-type": usernamePassword annotations: "jenkins.io/credentials-description": credentials from Kubernetes ownerReferences: - apiVersion: bitnami.com/v1alpha1 controller: true kind: SealedSecret name: mysecret uid: 5caff6a0-c9ac-11e9-881e-42010aac003e type: kubernetes.io/dockerconfigjson immutable: true data: .dockerconfigjson: ewogICJjcmVk... ``` 如你所见,生成的 `Secret` 资源是 `SealedSecret` 的“依赖对象”,因此 每当 `SealedSecret` 对象被更新或删除时,它也会被更新和删除。 ### 公钥 / 证书 密钥证书(公钥部分)用于加密 secrets, 需要在任何使用 `kubeseal` 的地方都可用。证书不是机密信息,但你需要 确保你使用的是正确的证书。 `kubeseal` 会在运行时从 controller 获取证书 (需要安全访问 Kubernetes API 服务器),这对于交互式使用很方便, 但当用户拥有特殊配置的集群时,已知这很脆弱,例如控制平面和节点之间有防火墙的 [私有 GKE 集群](docs/GKE.md#private-gke-clusters)。 另一种工作流程 是将证书存储在某处(例如本地磁盘),使用 `kubeseal --fetch-cert >mycert.pem`, 并使用 `kubeseal --cert mycert.pem` 离线使用它。 证书也会在启动时打印到 controller 日志中。 从 v0.9.x 开始,证书每 30 天自动更新一次。你和你团队最好 定期更新离线证书。为了帮助你做到这一点,从 v0.9.2 开始,`kubeseal` 也接受 URL。你可以设置内部自动化程序将证书发布到你信任的地方。 ``` kubeseal --cert https://your.intranet.company.com/sealed-secrets/your-cluster.cert ``` 它也能识别 `SEALED_SECRETS_CERT` 环境变量。(提示:另请参阅 [direnv](https://github.com/direnv/direnv))。 ### 作用域 从最终用户的角度来看,SealedSecrets 是一个“只写”设备。 其思想是 SealedSecret 只能由运行在目标集群中的 controller 解密, 其他人(甚至包括原作者)都无法从 SealedSecret 中获取原始 Secret。 用户可能直接访问目标集群,也可能不访问。 更具体地说,用户可能访问也可能无法访问由 controller 解密的 Secret。 在 k8s 上配置 RBAC 有很多种方法,但禁止低权限用户 读取 Secrets 是很常见的。给用户一个或多个具有更高权限的 namespaces 也很常见, 这将允许他们创建和读取 secrets(和/或创建可以引用这些 secrets 的 deployments)。 加密的 `SealedSecret` 资源被设计为即使查看也不会获得关于其隐藏的 secrets 的任何知识。这意味着我们不能允许用户读取针对他们无权访问的 namespace 的 SealedSecret, 并将其副本推送到他们可以读取 secrets 的 namespace 中。 因此,sealed-secrets 的行为*就像*每个 namespace 都有自己的独立加密密钥,因此一旦 你为一个 namespace 加密了一个 secret,它就不能被移动到另一个 namespace 并在那里解密。 我们在技术上并没有为每个 namespace 使用独立的私钥,而是在加密过程中*包含*了 namespace 名称, 从而有效地达到了相同的结果。 此外,namespace 并不是 RBAC 配置决定谁能查看哪个 secret 的唯一级别。事实上,用户可能可以访问给定 namespace 中名为 `foo` 的 secret,但无法访问同一 namespace 中的任何其他 secret。因此,默认情况下,我们不能让用户自由重命名 `SealedSecret` 资源,否则恶意用户只需将其重命名以覆盖他们确实有权访问的那个 secret,就能够解密该 namespace 的任何 SealedSecret。我们使用将 namespace 包含在加密密钥中的相同机制来也包含 secret 名称。 话虽如此,在许多场景中你可能不在乎这种级别的保护。例如,有权访问你集群的人要么是管理员,要么根本无法读取任何 `Secret` 资源。你可能需要将 sealed secret 移动到其他 namespaces(例如,你可能预先不知道 namespace 名称),或者你可能不知道 secret 的名称(例如,它可能包含基于内容哈希的唯一后缀等)。 以下是可能的作用域: - `strict`(默认):secret 必须使用完全相同的*名称*和 *namespace* 进行加密。这些属性成为*加密数据的一部分*,因此更改名称和/或 namespace 将导致“解密错误”。 - `namespace-wide`:你可以在给定的 namespace 内自由*重命名* sealed secret。 - `cluster-wide`:secret 可以在*任何* namespace 中解密,并且可以给定*任何*名称。 与*名称*和 *namespace* 的限制相反,secret *项*(即 JSON 对象键,如 `spec.encryptedData.my-key`)可以随意重命名,而不会失去解密 sealed secret 的能力。 使用 `--scope` 标志选择作用域: ``` kubeseal --scope cluster-wide sealed-secret.json ``` 也可以通过传递给 `kubeseal` 的输入 secret 中的 annotations 来请求作用域: - `sealedsecrets.bitnami.com/namespace-wide: "true"` -> 用于 `namespace-wide` - `sealedsecrets.bitnami.com/cluster-wide: "true"` -> 用于 `cluster-wide` 缺少任何此类 annotations 意味着 `strict` 模式。如果两者都设置了,`cluster-wide` 优先。 ## 安装说明 请参阅 https://github.com/bitnami-labs/sealed-secrets/releases 以获取最新版本和详细的安装说明。 特定于云平台的说明和指南: - [GKE](docs/GKE.md) ### 在受限环境中的安装(无 RBAC) 在缺乏权限创建集群范围 RBAC 资源(如 `ClusterRoles`)的环境中,你可以使用发布页面上提供的 **`controller-norbac.yaml`** 清单。 此版本是一个最小部署,仅包含 **Deployment**、**Service** 和 **CustomResourceDefinition**。它有意省略了 `ServiceAccount`、`ClusterRole` 和 `ClusterRoleBinding`。 **要求:** 1. 集群管理员必须已经安装了 SealedSecret CRDs。 2. 你必须有一个分配的 Service Account 来运行该部署 ### Controller 部署清单后,它将创建 `SealedSecret` 资源 并将 controller 安装到 `kube-system` namespace 中,创建一个 service 帐户和必要的 RBAC 角色。 几分钟后,controller 将启动,生成密钥对, 并准备就绪。如果没有,请检查 controller 日。 #### Kustomize 官方 controller 清单安装机制只是一个 YAML 文件。 在某些情况下,你可能需要应用自己的自定义配置,例如设置自定义 namespace 或设置一些环境变量。 `kubectl` 对此有原生支持,请参阅 [kustomize](https://kustomize.io/)。 #### Helm Chart Sealed Secrets Helm Chart 现已正式支持并托管在此 GitHub 存储库中。 ``` helm repo add sealed-secrets https://bitnami-labs.github.io/sealed-secrets ``` 最初,Helm Chart 由社区维护,采用的第一个版本的主版本为 1,而 sealed secrets 项目本身的主版本仍为 0。 这是可以的,因为 Helm Chart 本身的版本不一定非要与应用本身的版本一致。 然而这很令人困惑,所以我们当前的版本控制规则是: 1. `SealedSecret` controller 版本方案:0.X.Y 2. Helm Chart 版本方案:1.X.Y-rZ 因此,Helm Chart 可能有多个修订版,其中包含仅适用于 Helm Chart 的修复,而不会 影响静态 YAML 清单或 controller 镜像本身。 ``` kubeseal --controller-name sealed-secrets ``` 或者,你可以在安装 Chart 时设置 `fullnameOverride` 来覆盖名称。还要注意,`kubeseal` 默认假定 controller 安装在 `kube-system` namespace 中。因此,如果你希望不必传递预期的 controller 名称和 namespace 就能使用 `kubeseal` CLI,你应该像这样安装 Helm Chart: ``` helm install sealed-secrets -n kube-system --set-string fullnameOverride=sealed-secrets-controller sealed-secrets/sealed-secrets ``` ##### 在受限环境中的 Helm Chart 在某些公司,你可能只能访问单个 namespace,而不是整个集群。 你可能遇到的最受限的环境之一是: - 分配给你一个 `namespace`,其中包含一些 `service account`。 - 你无法访问集群的其余部分,甚至无法访问集群 CRDs。 - 你甚至可能无法在你的 namespace 中创建进一步的 service accounts 或 roles。 - 你被要求在所有部署中包含资源限制。 即使有这些限制,你仍然可以安装 sealed secrets Helm Chart,只有一个先决条件: - *集群必须已经安装了 sealed secrets CRDs*。 一旦你的管理员安装了 CRDs(如果尚未安装),你可以通过准备一个 YAML 配置文件来安装 Chart,如下所示: ``` serviceAccount: create: false name: {allocated-service-account} rbac: create: false clusterRole: false resources: limits: cpu: 150m memory: 256Mi ``` 请注意: - 不创建 service accounts,而是使用分配给你的那个。 - `{allocated-service-account}` 是你在集群上分配的 `service account` 的名称。 - 在 namespace 或集群中都不创建 RBAC 角色。 - 必须指定资源限制。 - 这些限制是可以工作的示例,但你可能希望在你的特定设置中查看它们。 该文件准备好后,如果你将其命名为 `config.yaml`,你现在可以像这样安装 sealed secrets Helm Chart: ``` helm install sealed-secrets -n {allocated-namespace} sealed-secrets/sealed-secrets --skip-crds -f config.yaml ``` 其中 `{allocated-namespace}` 是你在集群中分配的 `namespace` 的名称。 ### Kubeseal #### Homebrew `kubeseal` 客户端也可以在 [homebrew](https://formulae.brew.sh/formula/kubeseal) 上获得: ``` brew install kubeseal ``` #### MacPorts `kubeseal` 客户端也可以在 [MacPorts](https://ports.macports.org/port/kubeseal/summary) 上获得: ``` port install kubeseal ``` #### Nixpkgs `kubeseal` 客户端也可以在 [Nixpkgs](https://search.nixos.org/packages?channel=unstable&show=kubeseal&from=0&size=50&sort=relevance&type=packages&query=kubeseal) 上获得:(**免责声明**:不由 bitnami-labs 维护) ``` nix-env -iA nixpkgs.kubeseal ``` #### Linux 可以使用以下命令在 Linux 上安装 `kubeseal` 客户端: ``` KUBESEAL_VERSION='' # Set this to, for example, KUBESEAL_VERSION='0.23.0' curl -OL "https://github.com/bitnami-labs/sealed-secrets/releases/download/v${KUBESEAL_VERSION:?}/kubeseal-${KUBESEAL_VERSION:?}-linux-amd64.tar.gz" tar -xvzf kubeseal-${KUBESEAL_VERSION:?}-linux-amd64.tar.gz kubeseal sudo install -m 755 kubeseal /usr/local/bin/kubeseal ``` 如果你的机器上安装了 `curl` 和 `jq`,你可以通过这种方式动态获取版本。这对于自动化等使用的环境很有用。 ``` # 使用 GitHub API 获取最新的 sealed-secrets 版本 KUBESEAL_VERSION=$(curl -s https://api.github.com/repos/bitnami-labs/sealed-secrets/tags | jq -r '.[0].name' | cut -c 2-) # 检查版本是否成功获取 if [ -z "$KUBESEAL_VERSION" ]; then echo "Failed to fetch the latest KUBESEAL_VERSION" exit 1 fi curl -OL "https://github.com/bitnami-labs/sealed-secrets/releases/download/v${KUBESEAL_VERSION}/kubeseal-${KUBESEAL_VERSION}-linux-amd64.tar.gz" tar -xvzf kubeseal-${KUBESEAL_VERSION}-linux-amd64.tar.gz kubeseal sudo install -m 755 kubeseal /usr/local/bin/kubeseal ``` 其中 `KUBESEAL_VERSION` 是你要使用的 kubeseal 发布版的[版本标签](https://github.com/bitnami-labs/sealed-secrets/tags)。例如:`v0.18.0`。 #### 从源代码安装 如果你只是想要最新的客户端工具,可以将其安装到 `$GOPATH/bin` 中,方法是: ``` go install github.com/bitnami-labs/sealed-secrets/cmd/kubeseal@main ``` 你可以指定一个发布标签或提交 SHA 来代替 `main`。 `go install` 命令将把 `kubeseal` 二进制文件放在 `$GOPATH/bin`: ``` $(go env GOPATH)/bin/kubeseal ``` ## 升级 不要忘记查看[发布说明](RELEASE-NOTES.md),以获取有关升级客户端工具 和/或 controller 时可能的破坏性更改的指导。 ### 支持的版本 目前,仅支持最新版本的 Sealed Secrets 用于生产环境。 ### 与 Kubernetes 版本的兼容性 Sealed Secrets controller 通过依赖稳定的 Kubernetes API 来确保与不同版本的 Kubernetes 兼容。通常,Kubernetes 1.16 以上的版本被认为是兼容的。但是,我们官方支持 [当前推荐的 Kubernetes 版本](https://kubernetes.io/releases/)。此外,1.24 以上的版本会在我们每次发布时通过 CI 流程进行彻底验证。 ## 使用方法 ``` # 以某种方式创建 json/yaml 编码的 Secret: # (注意使用 `--dry-run` - 这只是一个本地文件!) echo -n bar | kubectl create secret generic mysecret --dry-run=client --from-file=foo=/dev/stdin -o json >mysecret.json # 这是关键部分: kubeseal -f mysecret.json -w mysealedsecret.json # 此时 mysealedsecret.json 可以安全地上传到 Github, # 发布在 Twitter 等平台上。 # 最终: kubectl create -f mysealedsecret.json # 获利! kubectl get secret mysecret ``` 请注意 `SealedSecret` 和 `Secret` 必须具有**相同的 namespace 和 名称**。这是一项功能,用于防止同一集群上的其他用户 重用你的 sealed secrets。有关更多信息,请参阅[作用域](#scopes) 部分。 `kubeseal` 从输入 secret 读取 namespace,接受显式的 `--namespace` 参数,并使用 `kubectl` 默认 namespace(按此顺序)。原始 `Secret` 上的任何 labels、 annotations 等都会被保留,但不会 自动反映在 `SealedSecret` 中。 根据设计,此方案*不验证用户身份*。换句话说, *任何人*都可以创建包含他们喜欢的任何 `Secret` 的 `SealedSecret` (只要 namespace/name 匹配)。这取决于你现有的 配置管理工作流程、集群 RBAC 规则等,以确保 只有预期的 `SealedSecret` 被上传到集群。与现有 Kubernetes 的唯一区别 在于,`Secret` 的*内容*现在在集群外部是隐藏的。 ### 管理现有的 secrets 如果你希望 Sealed Secrets controller 管理现有的 `Secret`,你可以使用 `sealedsecrets.bitnami.com/managed: "true"` annotation 来标注你的 `Secret`。当解密具有相同名称和 namespace 的 `SealedSecret` 时,现有的 `Secret` 将被覆盖,并且 `SealedSecret` 将获得 `Secret` 的所有权(因此当 `SealedSecret` 被删除时,`Secret` 也将被删除)。 ### 修补现有的 secrets 在某些用例中,你不想替换整个 `Secret`,而只是想从现有的 `Secret` 中添加或修改一些键。为此,你可以使用 `sealedsecrets.bitnami.com/patch: "true"` annotation 来标注你的 `Secret`。使用此 annotation 将确保 `Secret` 中不存在于 `SealedSecret` 中的 secret 键、labels 和 annotations 不会被删除,而 `SealedSecret` 中存在的那些将被添加到 `Secret` 中(`Secret` 和 `SealedSecret` 中都存在的 secret 键、labels 和 annotations 将由 `SealedSecret` 修改)。 此 annotation 不会使 `SealedSecret` 获得 `Secret` 的所有权。你可以同时添加 `patch` 和 `managed` annotations,以便在获得 `Secret` 所有权的同时获得修补行为。 ### 加密可以跳过设置 owner references 的 secret 如果你希望 `SealedSecret` 和 `Secret` 是独立的,这意味着当你删除 `SealedSecret` 时 `Secret` 不会随之消失,那么你必须在应用使用步骤之前使用 annotation `sealedsecrets.bitnami.com/skip-set-owner-references: "true"` 标注该 Secret。你仍然可以向你的 `Secret` 添加 `sealedsecrets.bitnami.com/managed: "true"`,以便当 `SealedSecret` 更新时你的 secret 也会更新。 ### 更新现有的 secrets 如果你想添加或更新现有的 sealed secrets 而没有其他项的明文, 你只需复制并粘贴新的加密数据项并将其合并到现有的 sealed secret 中即可。 你必须注意使用兼容的名称和 namespace 来加密更新的项(请参阅上面关于作用域的说明)。 如果你不想复制并粘贴,可以使用 `--merge-into` 命令来更新现有的 sealed secrets: ``` echo -n bar | kubectl create secret generic mysecret --dry-run=client --from-file=foo=/dev/stdin -o json \ | kubeseal > mysealedsecret.json echo -n baz | kubectl create secret generic mysecret --dry-run=client --from-file=bar=/dev/stdin -o json \ | kubeseal --merge-into mysealedsecret.json ``` ### 原始模式(实验性) 使用 `kubectl` 命令创建临时 Secret,只是为了在通过管道传给 `kubeseal` 后将其丢弃, 这是一种非常不友好的用户体验。我们正在努力改进 CLI 体验。在此期间, 我们提供了一种替代模式,其中 kubeseal 只关心将值加密到 stdout,你有责任将其放入 `SealedSecret` 资源中(就像任何其他 k8s 资源一样)。 它也可以作为编辑器/IDE 集成的构建块。 缺点是你必须小心,以确保与加密作用域、namespace 和名称一致。 请参阅[作用域](#scopes) `strict` 作用域(默认): ``` $ echo -n foo | kubeseal --raw --namespace bar --name mysecret AgBChHUWLMx... ``` `namespace-wide` 作用域: ``` $ echo -n foo | kubeseal --raw --namespace bar --scope namespace-wide AgAbbFNkM54... ``` 在 `SealedSecret` 中包含 `sealedsecrets.bitnami.com/namespace-wide` annotation ``` metadata: annotations: sealedsecrets.bitnami.com/namespace-wide: "true" ``` `cluster-wide` 作用域: ``` $ echo -n foo | kubeseal --raw --scope cluster-wide AgAjLKpIYV+... ``` 在 `SealedSecret` 中包含 `sealedsecrets.bitnami.com/cluster-wide` annotation ``` metadata: annotations: sealedsecrets.bitnami.com/cluster-wide: "true" ``` ### 验证 Sealed Secret 如果你想验证现有的 sealed secret,`kubeseal` 有一个标志 `--validate` 可以帮助你。 给定一个名为 `sealed-secrets.yaml` 的文件,其中包含以下 sealed secret: ``` apiVersion: bitnami.com/v1alpha1 kind: SealedSecret metadata: name: mysecret namespace: mynamespace spec: encryptedData: foo: AgBy3i4OJSWK+PiTySYZZA9rO43cGDEq..... ``` 你可以验证 sealed secret 是否正确创建: ``` $ cat sealed-secrets.yaml | kubeseal --validate ``` 如果是无效的 sealed secret,`kubeseal` 将显示: ``` $ cat sealed-secrets.yaml | kubeseal --validate error: unable to decrypt sealed secret ``` ## Secret 轮换 你应该始终轮换你的 secrets。但由于你的 secrets 是用另一个 secret 加密的, 你需要了解这两层之间的关系才能做出正确的决定。 简而言之: ### 密封密钥更新 密封密钥每 30 天自动更新一次。这意味着创建一个新的密封密钥并将其附加到 controller 可用于解密 `SealedSecret` 资源的现有密封密钥集中。 最近创建的密封密钥是当你使用 `kubeseal` 时用于加密新 secrets 的密钥,也是当你使用 `kubeseal --fetch-cert` 时下载的证书。 30 天的更新时间是一个合理的默认值,但可以根据需要通过 `SealedSecret` controller 的 pod 模板中命令的 `--key-renew-period=` 标志进行调整。`value` 字段可以作为 golang 时长标志给出(例如:`720h30m`)。假设你已将 Sealed Secrets 安装到 `kube-system` namespace 中,使用以下命令编辑 Deployment controller,并添加 `--key-renew-period` 参数。一旦你关闭文本编辑器,并且 Deployment controller 已被修改,将自动创建一个新的 Pod 来替换旧的 Pod。 ``` kubectl edit deployment/sealed-secrets-controller --namespace=kube-system ``` 值为 `0` 将停用自动密钥更新。当然,你可能有一个停用自动密封密钥更新的正当用例,但经验表明,新用户往往在完全理解 sealed secrets 如何工作之前就急于得出结论,认为他们想要控制密钥更新。在下面的[常见误解](#common-misconceptions-about-key-renewal)部分阅读更多相关信息。 一个常见的误解是,密钥更新通常被认为是一种密钥轮换的形式,其中旧密钥不仅过时了,而且实际上是坏的,因此你想要摆脱它。 历史上此功能被称为“密钥轮换”,这一事实并没有帮助,这可能会增加混淆。 Sealed secrets 不会自动轮换,旧密钥在生成新密钥时也不会被删除。 旧的 `SealedSecret` 资源仍然可以解密(这是因为旧的密封密钥没有被删除)。 ### 密钥注册表初始化优先级顺序 当 controller 启动时,它将初始化密钥注册表。最近的密钥用于加密 secrets。默认情况下,此证书是根据证书的 NotBefore 属性选择的。如果你想更改注册表中密钥的优先级顺序,可以使用 `--key-order-priority` 标志。 `--key-order-priority` 标志接受以下值: - `CertNotBefore`:(默认)密钥注册表将根据密钥证书的 NotBefore 属性进行排序。 - `SecretCreationTimestamp`:密钥注册表将根据 secret 的创建时间戳进行排序。 此标志会影响用于加密 secrets 的公钥以及通过 `kubeseal --fetch-cert` 检索的证书。 ### 用户 secret 轮换 *密封密钥*更新和 SealedSecret 轮换**不能代替**轮换你的实际 secrets。 此工具的一个核心价值主张是: 如果你将任何内容存储在版本控制存储中,尤其是在公共存储中,你必须假设你永远无法删除该信息。 *如果*密封密钥以某种方式从集群中泄露,你必须将所有使用该密钥加密的 `SealedSecret` 资源 视为已受损。无论在集群中进行多少密封密钥轮换,甚至重新加密现有的 SealedSecrets 文件,都无法改变这一点。 最佳做法是定期轮换所有实际 secrets(例如,更改密码)**并**使用这些新 secrets 制作新的 `SealedSecret` 资源。 但是,如果 `SealedSecret` controller 没有更新*密封密钥*,那么这种轮换将毫无意义, 因为攻击者也可以解密新的 secrets。因此,你需要同时做这两件事:定期更新密封密钥并轮换你的实际 secrets! ### 提前更新密钥 如果你知道或怀疑*密封密钥*已受损,你应该在开始密封新的轮换 secrets 之前尽快更新密钥,否则你给攻击者访问你的新 secrets 的权限。 可以通过将当前时间戳传递给 controller 中的名为 `--key-cutoff-time` 的标志或名为 `SEALED_SECRETS_KEY_CUTOFF_TIME` 的环境变量来提前生成密钥。预期的格式是 RFC1123,你可以使用 `date -R` unix 命令生成它。 ### 关于密钥更新的常见误解 Sealed secrets 密封密钥不是访问控制密钥(例如密码)。它们更像你可能用来阅读发送给你的加密邮件的 GPG 密钥。让我们继续用电子邮件类比一下: 想象一下,你有理由相信你的私有 GPG 密钥可能已经受损。如果你做的第一件事只是删除你的私有密钥,那么你将得不偿失。之前使用该密钥发送的所有电子邮件你都无法再访问(除非你有这些电子邮件的解密副本),你的朋友尚未被通知使用新密钥而发送的新电子邮件也是如此。 当然,这些加密电子邮件的内容是不安全的,因为攻击者现在可能能够解密它们,但木已成舟。你突然失去阅读这些电子邮件的能力肯定不会消除损害。如果有的话,情况更糟,因为你不再确定攻击者知道了什么 secret。你真正想做的是确保你的朋友停止使用你的旧密钥,并且从现在开始所有进一步的通信都使用新的密钥对加密(即你的朋友必须知道那个新密钥)。 同样的逻辑也适用于 SealedSecrets。最终目标是保护你实际的“用户”secrets。“密封”secrets 只是一种机制,一个“信封”。如果 secret 泄露了,就无法回头,木已成舟。 你首先需要确保新 secrets 不会使用旧的受损密钥加密(在上面的电子邮件类比中:创建一个新的密钥对并将你的新公钥给你所有的朋友)。 第二步是在逻辑上消除损害,这取决于 secret 的性质。一个简单的例子是数据库密码:如果你意外泄露了数据库密码,你应该做的就是简单地更改你的数据库密码(在数据库上;并撤销旧的!)*并*使用新密码更新 `SealedSecret` 资源(即再次运行 `kubeseal`)。 这两个步骤在前面的章节中已经描述过,尽管不那么冗长。既然你对基本原理有了更深入的了解,再读一遍也不为过。 ### 手动密钥管理(高级) `SealedSecret` controller 和相关工作流程旨在保留旧的密封密钥并定期添加新的密钥。除非你知道自己在做什么,否则不应删除旧密钥。 话虽如此,如果你愿意,你可以手动管理(创建、移动、删除)*密封密钥*。它们只是普通的 k8s secrets,位于 `SealedSecret` controller 所在的同一 namespace 中(通常是 `kube-system`,但是可配置的)。 你可以通过创造性地管理密封密钥来解决一些高级用例。 例如,你可以在几个集群之间共享同一个密封密钥,以便你可以在多个集群中应用完全相同的 sealed secret。 由于密封密钥只是普通的 k8s secrets,你甚至可以使用 sealed secrets 本身并使用 GitOps 工作流程来管理你的密封密钥(当你想在不同集群之间共享同一个密钥时很有用)! 使用除 `active` 以外的任何内容标记*密封密钥* secret 实际上会从 `SealedSecret` controller 中删除该密钥,但如果需要,它仍然可以在 k8s 中用于 手动加密/解密。 **注意** `SealedSecret` controller 目前不会自动获取手动创建、删除或重新标记的密封密钥。管理员必须在效果生效之前重启 controller。 ### 重新加密(高级) 在你可以摆脱一些旧的密封密钥之前,你需要使用最新的私钥重新加密你的 SealedSecrets。 ``` kubeseal --re-encrypt tmp.json \ && mv tmp.json my_sealed_secret.json ``` 上面的调用将产生一个使用最新密钥新近加密的新 sealed secret 文件,而无需将 secrets 离开集群发送到客户端。然后你可以将该文件保存在你的版本控制系统中(`kubeseal --re-encrypt` 不会更新集群内的对象)。 目前,旧密钥不会被自动垃圾回收。 定期重新加密你的 SealedSecrets 是一个好主意。但如上所述,不要让自己陷入虚假的安全感:你必须假设 `SealedSecret` 资源的旧版本(即用你认为已失效的密钥加密的版本)仍然可能存在并且可以被攻击者访问。也就是说,重新加密不能代替定期轮换你的实际 secrets。 ## 详情(高级) 此 controller 添加了一个新的 `SealedSecret` 自定义资源。`SealedSecret` 最有趣的部分是 base64 编码的 非对称加密 `Secret`。 controller 维护一组私钥/公钥对作为 kubernetes secrets。密钥用 `sealedsecrets.bitnami.com/sealed-secrets-key` 标记, 并在标签中标识为 `active` 或 `compromised`。在启动时, sealed secrets controller 将... 1. 搜索这些密钥,如果它们被标记为 active,则将它们添加到其本地存储中。 2. 创建一个新密钥 3. 启动密钥轮换周期 ### 加密算法 有关加密的更多详细信息可以在[这里](docs/developer/crypto.md)找到。 ## 开发 开发指南可以在[开发者指南](docs/developer/README.md)中找到。 ## FAQ ### 我可以一次性加密多个 secrets 吗,在一个 YAML / JSON 文件中? 是的,你可以!在一个文件中放入尽可能多的 secrets。确保通过 `---` 分隔它们,对于 YAML 作为额外的单个对象,对于 JSON 也是如此。 ### 如果你不再拥有集群的访问权限,你还能解密吗? 不,私钥仅存储在由 controller 管理的 Secret 中(除非你有其他 k8s 对象的备份)。没有后门 - 如果没有用于加密给定 SealedSecrets 的私钥,你就无法解密它。如果你无法获取带有加密密钥的 Secrets,并且你也无法获取集群中实时存在的 Secrets 的解密版本,那么你需要为所有内容重新生成新密码,使用新的密封密钥再次密封它们,等等。 ### 我该如何备份我的 SealedSecrets? 如果你确实想备份加密私钥,可以从具有适当访问权限的帐户轻松完成: ``` kubectl get secret -n kube-system -l sealedsecrets.bitnami.com/sealed-secrets-key -o yaml >main.key echo "---" >> main.key kubectl get secret -n kube-system sealed-secrets-key -o yaml >>main.key ``` 如果在发生灾难后从备份恢复,只需在启动 controller 之前将该 secret 放回 - 或者如果 controller 已经启动,则替换新创建的 secrets 并重启 controller: * 对于 Helm 部署: kubectl apply -f main.key kubectl delete pod -n kube-system -l app.kubernetes.io/name=sealed-secrets * 通过 `controller.yaml` 清单进行部署 kubectl apply -f main.key kubectl delete pod -n kube-system -l name=sealed-secrets-controller ### 我可以使用备份密钥离线解密我的 secrets 吗? 虽然将 sealed-secrets 视为 secrets 的长期存储系统并不是推荐的用例,但有些人 确实有一个合理的要求,即当 k8s 集群关闭时能够恢复 secrets,并且将备份恢复到新的 `SealedSecret` controller 部署中是不切实际的。 如果你备份了一个或多个私钥(请参阅上一个问题),你可以使用 `kubeseal --recovery-unseal --recovery-private-key file1.key,file2.key,...` 命令来解密 sealed secrets 文件。 ### kubeseal 有哪些可用标志? 你可以使用 `kubeseal --help` 检查可用的标志。 ### 我如何更新使用 sealed secrets 加密的 JSON/YAML/TOML/.. 文件的一部分? kubernetes `Secret` 资源包含多个项,基本上是键/值对的平面映射。 SealedSecrets 在该级别操作,并不关心你在值中放了什么。换句话说 它无法理解你可能在 secret 中放置的任何结构化配置文件,因此 无法帮助你更新其中的单个字段。 由于这是一个常见问题,尤其是在处理遗留应用程序时,我们确实提供了可能的[示例](docs/examples/config-template) 解决方法。 ### 我可以使用自己的(预生成的)证书吗? 是的,你可以向 controller 提供你自己的证书,它将使用它们。 请查看[这里](docs/bring-your-own-certificates.md)以获取解决方法。 ### 如果 controller 没有运行在 `kube-system` namespace 中,如何使用 kubeseal? 如果你将 controller 安装在默认 `kube-system` 以外的 namespace 中,你需要将此 namespace 提供给 `kubeseal` 命令行工具。有两个选项: 1. 你可以通过命令行选项 `--controller-namespace ` 指定 namespace: ``` kubeseal --controller-namespace sealed-secrets mysealedsecret.json ``` 2. 通过环境变量 `SEALED_SECRETS_CONTROLLER_NAMESPACE`: ``` export SEALED_SECRETS_CONTROLLER_NAMESPACE=sealed-secrets kubeseal mysealedsecret.json ``` ### 如何验证镜像? 我们的镜像使用 [cosign](https://github.com/sigstore/cosign) 进行签名。签名已保存在我们的 [GitHub Container Registry](https://ghcr.io/bitnami-labs/sealed-secrets-controller/signs) 中。 验证镜像非常简单: ``` # export COSIGN_VARIABLE 设置 GitHub 容器注册表 signs 路径 export COSIGN_REPOSITORY=ghcr.io/bitnami-labs/sealed-secrets-controller/signs # 验证上传到 GHCR 的镜像 cosign verify --key .github/workflows/cosign.pub ghcr.io/bitnami-labs/sealed-secrets-controller:latest # 验证上传到 Dockerhub 的镜像 cosign verify --key .github/workflows/cosign.pub docker.io/bitnami/sealed-secrets-controller:latest ``` ### 如何使用一个 controller 管理一部分 namespaces 如果你想为多个 namespace(但不是所有 namespaces)使用一个 controller,你可以使用命令行标志 `--additional-namespaces=,,<...>` 提供额外的 namespaces。确保在目标 namespaces 中提供适当的 roles 和 rolebindings,以便 controller 可以在那里管理 secrets。 ### 我可以配置 Controller 的解密重试吗? 是的,你可以使用标志 `--max-unseal-retries` 在你的 controller 中配置重试次数。此标志允许你配置解密 Sealed Secrets 的最大重试次数。 ### 如何在集群范围内或特定的 namespaces 中管理 SealedSecrets? 默认情况下,controller 使用 `--all-namespaces` 标志(默认为 `true`)监视**所有 namespaces** 中的 `SealedSecret` 资源。 如果你需要限制 controller 的范围,你有两个选项: - **监视一部分 namespaces:** 使用 `--additional-namespaces=,` 标志为 controller 提供一个用逗号分隔的 namespace 列表以进行管理。 - **仅监视本地 namespace:** 设置 `--all-namespaces=false`(或环境变量 `SEALED_SECRETS_ALL_NAMESPACES=false`)。这对于多租户集群很有用,你希望在每个 namespace 中拥有独立的 controller 和独立的密封密钥。 ## 社区 - [Kubernetes Slack 上的 #sealed-secrets](https://kubernetes.slack.com/messages/sealed-secrets) 点击[此处](http://slack.k8s.io)注册 Kubernetes Slack 组织。 ### 相关项目 - `kseal` Kubeseal 的伴侣:[https://github.com/eznix86/kseal](https://github.com/eznix86/kseal) - `kubeseal-convert`:[https://github.com/EladLeev/kubeseal-convert](https://github.com/EladLeev/kubeseal-convert) - Visual Studio Code 扩展:[https://marketplace.visualstudio.com/items?itemName=codecontemplator.kubeseal](https://marketplace.visualstudio.com/items?itemName=codecontemplator.kubeseal) - WebSeal:在浏览器中生成 secrets:[https://socialgouv.github.io/webseal](https://socialgouv.github.io/webseal) - HybridEncrypt TypeScript 实现:[https://github.com/SocialGouv/aes-gcm-rsa-oaep](https://github.com/SocialGouv/aes-gcm-rsa-oaep) - [已弃用] Sealed Secrets Operator:[https://github.com/disposab1e/sealed-secrets-operator-helm](https://github.com/disposab1e/sealed-secrets-operator-helm)
标签:Bitnami, DevSecOps, EVTX分析, GitOps, Go, Helm, RBAC, Ruby工具, Secrets, 上游代理, 公钥加密, 加密, 单向加密, 子域名突变, 容器编排, 控制器, 日志审计, 漏洞扫描器, 证书管理, 请求拦截