bitnami-labs/sealed-secrets
GitHub: bitnami-labs/sealed-secrets
这是一个适用于 Kubernetes 的控制器与客户端工具,通过非对称加密将普通 Secret 封装为可安全存储于 Git 仓库的 SealedSecret 对象。
Stars: 9032 | Forks: 767
# 适用于 Kubernetes 的 "Sealed Secrets"
[](#Installation)
[](https://github.com/bitnami-labs/sealed-secrets/releases/latest)
[](https://formulae.brew.sh/formula/kubeseal)
[](https://github.com/bitnami-labs/sealed-secrets/actions/workflows/ci.yml)
[](https://github.com/bitnami-labs/sealed-secrets/releases)
[](https://hub.docker.com/r/bitnami/sealed-secrets-controller)
[](https://goreportcard.com/report/github.com/bitnami-labs/sealed-secrets)

**问题:** “我可以在 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, 上游代理, 公钥加密, 加密, 单向加密, 子域名突变, 容器编排, 控制器, 日志审计, 漏洞扫描器, 证书管理, 请求拦截