kubeseal
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseKubeseal: Seal and Reseal Kubernetes Secrets
Kubeseal:密封与重新密封Kubernetes Secrets
Overview
概述
Seal Kubernetes Secrets into Bitnami SealedSecrets that are safe to store in Git. SealedSecrets are asymmetrically encrypted — anyone can encrypt (seal), but only the sealed-secrets controller in the cluster can decrypt (unseal).
Validated againstv0.38.4. Flag names are stable across recent releases, but runkubesealif a command behaves unexpectedly.kubeseal --help
Key insight: the public certificate () is not secret — it is a public key, safe to share and even commit. Only the controller's private key () is sensitive. Sealing is a purely local, offline operation once you have the cert; no cluster access is needed to seal.
tls.crttls.key将Kubernetes Secrets密封为Bitnami SealedSecrets,这类Secrets可以安全地存储在Git中。SealedSecrets采用非对称加密——任何人都可以加密(密封),但只有集群中的sealed-secrets控制器才能解密(解封)。
已针对v0.38.4验证。近期版本的标志名称保持稳定,但如果命令行为不符合预期,请运行kubeseal。kubeseal --help
核心要点:公证书()并非机密——它是公钥,可安全共享甚至提交到仓库。只有控制器的私钥()是敏感信息。一旦获取到证书,密封操作完全是本地离线操作,无需访问集群即可完成密封。
tls.crttls.keyCRITICAL SECURITY RULES
关键安全规则
NEVER LEAK CREDENTIALS
绝对不能泄露凭证
- NEVER print plaintext credentials to stdout, conversation output, or any readable medium.
- NEVER write plaintext secrets to files in the repository or any version-controlled location.
- NEVER echo or log secret values — use variables and pipe directly.
- NEVER include plaintext secrets in commit messages, PR descriptions, or comments.
- ALWAYS overwrite or zero-out temp files containing plaintext secrets immediately after use.
- NEVER leak seal key material — the private key () must never be printed, committed, or shared. Only the public key (
tls.key) is needed for sealing.tls.crt
- 绝对不能将明文凭证输出到标准输出、对话内容或任何可读介质中。
- 绝对不能将明文密钥写入仓库或任何版本控制位置的文件中。
- 绝对不能回显或记录密钥值——使用变量并直接通过管道传输。
- 绝对不能在提交信息、PR描述或评论中包含明文密钥。
- 使用后立即覆盖或清零包含明文密钥的临时文件。
- 绝对不能泄露密封密钥材料——私钥()绝对不能被打印、提交或共享。密封仅需公钥(
tls.key)。tls.crt
Temp File Handling
临时文件处理
Prefer approaches that never write plaintext to a named file on disk — the stdin heredoc under "1. New SealedSecret", or "Encrypt a Single Value ()", both below. When a temp file is unavoidable:
--raw- Write temp secret files to with descriptive names.
/tmp/ - After sealing, overwrite the temp file contents with empty or garbage data.
- Never temp files (may be blocked by permission rules) — overwrite instead:
rmbashecho "" > /tmp/secret-temp.yaml - Never commit temp files to the repository.
Note: the public cert (/tls.crt) is not sensitive and does not need sanitizing — sealing it away only forces a re-fetch. Only overwrite files that contain plaintext secret values or the private key..pem
优先选择从不将明文写入磁盘上命名文件的方法——下文“1. 新建SealedSecret”中的标准输入HereDoc,或“加密单个值()”方法。当必须使用临时文件时:
--raw- 将临时密钥文件写入目录,并使用描述性名称。
/tmp/ - 密封完成后,覆盖临时文件内容为空白或无用数据。
- 不要使用删除临时文件(可能会被权限规则阻止)——而是覆盖:
rmbashecho "" > /tmp/secret-temp.yaml - 绝对不要将临时文件提交到仓库。
注意:公证书(/tls.crt)不敏感,无需清理——将其密封起来只会导致需要重新获取。仅覆盖包含明文密钥值或私钥的文件。.pem
Prerequisites
前提条件
- CLI installed and available
kubeseal - Public key certificate () — obtained only as described in "Obtaining the Certificate" below
tls.crt - Access to the Kubernetes cluster via the Kubernetes MCP (for the cert fallback, and for retrieving existing secret values)
- 已安装并可使用CLI
kubeseal - 公钥证书()——仅可通过下文“获取证书”中描述的方式获取
tls.crt - 通过Kubernetes MCP访问Kubernetes集群(用于证书回退,以及检索现有密钥值)
Obtaining the Certificate
获取证书
This section is authoritative. The certificate MUST come from one of exactly two sources, tried in this order. Do not improvise a third.
**本节内容为权威说明。证书必须且仅能来自以下两个来源,按顺序尝试。**不得自行使用第三种方法。
Source 1 (preferred) — the project's certs/
folder
certs/来源1(首选)——项目的certs/
文件夹
certs/Look for the cert in the repository first. Sealing is offline; if the repo ships the cert, no cluster access is needed at all.
bash
undefined首先在仓库中查找证书。密封操作是离线的;如果仓库提供了证书,则完全无需访问集群。
bash
undefinedFrom the project root; typical names: tls.crt, sealed-secrets-cert.pem, sealed-secrets.crt
从项目根目录查找;常见名称:tls.crt、sealed-secrets-cert.pem、sealed-secrets.crt
ls certs/
ls certs/
If certs/ is not at the root, locate it:
如果certs/不在根目录,查找它:
find . -type d -name certs -not -path '/.git/'
Set `CERT` to the file you found, then **validate it before use** (see "Always Validate the Certificate"):
```bash
CERT=certs/tls.crtIf holds more than one candidate, validate each and prefer the one that verifies against the repo's existing SealedSecrets.
certs/find . -type d -name certs -not -path '/.git/'
将`CERT`设置为找到的文件,然后**使用前验证它**(参见“始终验证证书”):
```bash
CERT=certs/tls.crt如果中有多个候选证书,验证每个证书并优先选择可与仓库中现有SealedSecrets匹配的证书。
certs/Source 2 (fallback) — the cluster, via the Kubernetes MCP
来源2(回退)——集群,通过Kubernetes MCP
Only if has no valid cert. Use the Kubernetes MCP tools to read the controller's active key Secret:
certs/Step 1. Call with:
mcp__kubernetes__resources_list- ,
apiVersion: v1kind: Secret labelSelector: sealedsecrets.bitnami.com/sealed-secrets-key=active- : wherever the controller runs (commonly
namespaceorkube-system); omit to search all namespacessealed-secrets
If several key Secrets come back, pick the one with the most recent — that is the key the controller currently seals with.
activemetadata.creationTimestampStep 2. Take from that Secret and base64-decode it into a local file:
data["tls.crt"]bash
printf '%s' '<tls.crt base64 from the MCP response>' | base64 -d > /tmp/sealed-secrets-cert.pem
CERT=/tmp/sealed-secrets-cert.pemStep 3. Validate it before use (next section).
If the MCP returns no such Secret, the controller is not installed on the cluster the MCP points at. Stop and tell the user — do not fall back to another method.
DANGER — that Secret also contains, the controller's private key. Read and use only thetls.keyfield. Never decode, print, echo, write, or committls.crt. Do not paste the raw MCP response anywhere.tls.key
仅当中没有有效证书时使用。使用Kubernetes MCP工具读取控制器的活动密钥Secret:
certs/步骤1. 调用,参数如下:
mcp__kubernetes__resources_list- ,
apiVersion: v1kind: Secret labelSelector: sealedsecrets.bitnami.com/sealed-secrets-key=active- :控制器运行的命名空间(通常为
namespace或kube-system);省略则搜索所有命名空间sealed-secrets
如果返回多个密钥Secret,选择最新的那个——这是控制器当前使用的密封密钥。
activemetadata.creationTimestamp步骤2. 从该Secret中取出并进行base64解码,保存到本地文件:
data["tls.crt"]bash
printf '%s' '<来自MCP响应的tls.crt base64值>' | base64 -d > /tmp/sealed-secrets-cert.pem
CERT=/tmp/sealed-secrets-cert.pem步骤3. 使用前验证它(下一节)。
如果MCP未返回此类Secret,说明MCP指向的集群上未安装控制器。立即告知用户——不要使用其他方法回退。
**危险——该Secret还包含,即控制器的私钥。**仅读取和使用tls.key字段。绝对不要解码、打印、回显、写入或提交tls.crt。不要将原始MCP响应粘贴到任何地方。tls.key
Always Validate the Certificate
始终验证证书
A file is not a certificate just because it exists. Redirects capture error text into cert-shaped files, and sealing against that garbage produces a SealedSecret that fails later with or — long after the plaintext is gone.
illegal base64 data at input byte Nno key could decrypt secretRun this before every seal. If it fails, STOP — fall back to the next source rather than sealing:
bash
openssl x509 -in "$CERT" -noout -subject -dates || echo "NOT A VALID CERT — do not seal with this file"If a file fails validation, report it to the user and move to Source 2. Never "fix" it by guessing.
certs/**不能仅因为文件存在就认为它是证书。**重定向操作可能会将错误文本捕获成类似证书的文件,使用此类无效文件进行密封会生成SealedSecret,之后解封时会失败,报错为或——此时明文可能已经丢失。
illegal base64 data at input byte Nno key could decrypt secret每次密封前都要运行此命令。如果失败,请停止——使用下一个来源获取证书,不要继续密封:
bash
openssl x509 -in "$CERT" -noout -subject -dates || echo "无效证书——请勿使用此文件进行密封"如果中的文件验证失败,告知用户并切换到来源2。不要通过猜测来“修复”它。
certs/Confirm It Is the Right Key (recommended)
确认是正确的密钥(推荐)
A valid cert may still be the wrong or a rotated key. When the repo already contains working SealedSecrets, confirm the cert matches the one they were sealed with by comparing public keys against a known-good pair, or by checking that a newly sealed test value round-trips. If the repo also contains the private key, note that as a security problem (see Pitfalls) rather than relying on it.
有效的证书可能仍然是错误或已轮换的密钥。当仓库中已包含可用的SealedSecrets时,可通过将公钥与已知有效的密钥对进行比较,或检查新密封的测试值是否能正常往返解密,来确认证书是否与密封现有SealedSecrets所用的证书匹配。如果仓库中还包含私钥,应将其视为安全问题(参见陷阱部分),而不是依赖它。
Workflow
工作流程
1. New SealedSecret
1. 新建SealedSecret
Preferred — seal via stdin (no plaintext file on disk):
The sealed output is safe to write directly into the repo. Only the input is sensitive, and piping it via a heredoc keeps it off the filesystem entirely.
bash
kubeseal --cert "$CERT" -o yaml -f /dev/stdin > path/to/repo/sealedsecret.yaml <<'EOF'
apiVersion: v1
kind: Secret
metadata:
name: my-secret
namespace: my-namespace
type: Opaque
stringData:
key1: "value1"
key2: "value2"
EOFAlternative — temp file (when you must inspect/edit the plaintext first):
bash
undefined首选方式——通过标准输入进行密封(磁盘上无明文文件):
密封后的输出可直接写入仓库。只有输入是敏感的,通过HereDoc管道传输可完全避免其写入文件系统。
bash
kubeseal --cert "$CERT" -o yaml -f /dev/stdin > path/to/repo/sealedsecret.yaml <<'EOF'
apiVersion: v1
kind: Secret
metadata:
name: my-secret
namespace: my-namespace
type: Opaque
stringData:
key1: "value1"
key2: "value2"
EOF替代方式——临时文件(必须先检查/编辑明文时使用):
bash
undefinedWrite secret to temp file (NEVER to repo)
将密钥写入临时文件(绝对不要写入仓库)
cat > /tmp/my-secret.yaml <<'EOF'
apiVersion: v1
kind: Secret
metadata:
name: my-secret
namespace: my-namespace
type: Opaque
stringData:
key1: "value1"
key2: "value2"
EOF
cat > /tmp/my-secret.yaml <<'EOF'
apiVersion: v1
kind: Secret
metadata:
name: my-secret
namespace: my-namespace
type: Opaque
stringData:
key1: "value1"
key2: "value2"
EOF
Seal and output YAML (sealed output is safe to write straight to the repo)
密封并输出YAML(密封后的输出可直接写入仓库)
kubeseal --cert "$CERT" --format yaml -f /tmp/my-secret.yaml > path/to/repo/sealedsecret.yaml
kubeseal --cert "$CERT" --format yaml -f /tmp/my-secret.yaml > path/to/repo/sealedsecret.yaml
SANITIZE the plaintext temp file immediately (the sealed output is not sensitive)
立即清理明文临时文件(密封后的输出不敏感)
echo "" > /tmp/my-secret.yaml
undefinedecho "" > /tmp/my-secret.yaml
undefined2. Reseal / Update an Existing SealedSecret (Preserving Fields)
2. 重新密封/更新现有SealedSecret(保留字段)
This is the path for a reseal request — the user asks to reseal an existing SealedSecret, usually because it fails to unseal (,illegal base64 data at input byte N) or because a credential changed. Resealing rewritesno key could decrypt secretin place; keepspec.encryptedData,metadata(labels, annotations,spec.template), name, and namespace byte-identical to the original unless the user asks otherwise, and match the surrounding files' conventions.type
When updating some keys in a SealedSecret (e.g., changing S3 credentials but keeping a database password), you have two options:
- Update individual keys with (paste into
--raw) orencryptedData— you only need the values of the keys you're changing.--merge-into - Re-seal the whole secret — you need the plaintext of ALL keys, retrieved from the cluster.
这是处理重新密封请求的流程——用户要求重新密封现有SealedSecrets,通常是因为它无法解封(报错或illegal base64 data at input byte N)或凭证已更改。重新密封会原地重写no key could decrypt secret;除非用户另有要求,否则保持spec.encryptedData、metadata(标签、注解、spec.template)、名称和命名空间与原始文件完全一致,并匹配周围文件的约定。type
当更新SealedSecret中的部分密钥时(例如,更改S3凭证但保留数据库密码),有两种选择:
- 更新单个密钥:使用(粘贴到
--raw中)或encryptedData——仅需更改的密钥的值。--merge-into - 重新密封整个密钥:需要所有密钥的明文,从集群中检索。
Step 1: Retrieve existing secret from the cluster
步骤1:从集群中检索现有密钥
Prefer the MCP tool to read the decrypted Secret — the SealedSecret controller auto-decrypts into a regular Secret in the cluster (, , plus the name and namespace).
mcp__kubernetes__resources_getapiVersion: v1kind: SecretOr via kubectl:
bash
kubectl get secret <name> -n <namespace> -o jsonpath='{.data}'优先使用 MCP工具读取解密后的Secret——SealedSecrets控制器会自动将其解密为集群中的常规Secret(,,加上名称和命名空间)。
mcp__kubernetes__resources_getapiVersion: v1kind: Secret或通过kubectl:
bash
kubectl get secret <name> -n <namespace> -o jsonpath='{.data}'Step 2: Decode and re-seal with updated values
步骤2:解码并使用更新后的值重新密封
DANGER — do not interpolate secret values into YAML. A value containing,",:, leading spaces, or\nwill break the quoting and either corrupt the secret or make$fail withkubeseal. This is common with generated passwords and S3 keys. Never build YAML likeerror: no secrets found.password: "$EXISTING_PASS"
Safe approach — seal each key individually with , then merge. reads the value from stdin as raw bytes (no YAML quoting involved), so special characters are handled correctly. Under the default scope you must pass and , and they must match the target SealedSecret.
--raw--rawstrict--name--namespacebash
NS=my-namespace
NAME=my-secret**危险——不要将密钥值插入YAML中。**包含、"、:、前导空格或\n的值会破坏引号,导致密钥损坏或$报错kubeseal。这种情况在生成密码和S3密钥时很常见。绝对不要像error: no secrets found这样构建YAML。password: "$EXISTING_PASS"
安全方法——使用单独密封每个密钥,然后合并。从标准输入读取原始字节值(不涉及YAML引号),因此可以正确处理特殊字符。在默认的作用域下,必须传递和,且它们必须与目标SealedSecret匹配。
--raw--rawstrict--name--namespacebash
NS=my-namespace
NAME=my-secretDecode a value you want to PRESERVE (capture in variable, NEVER echo)
解码要保留的值(保存到变量中,绝对不要回显)
EXISTING_PASS=$(printf '%s' '<base64value>' | base64 -d)
EXISTING_PASS=$(printf '%s' '<base64值>' | base64 -d)
Re-seal the preserved value and the updated value straight into the repo file.
将保留的值和更新后的值直接密封到仓库文件中。
printf '%s' avoids adding a trailing newline to the secret.
使用printf '%s'避免向密钥值添加尾随换行符。
printf '%s' "$EXISTING_PASS" | kubeseal --cert "$CERT"
--raw --namespace "$NS" --name "$NAME" --from-file=/dev/stdin
--raw --namespace "$NS" --name "$NAME" --from-file=/dev/stdin
printf '%s' "$EXISTING_PASS" | kubeseal --cert "$CERT"
--raw --namespace "$NS" --name "$NAME" --from-file=/dev/stdin
--raw --namespace "$NS" --name "$NAME" --from-file=/dev/stdin
-> paste the output under spec.encryptedData.password in path/to/repo/sealedsecret.yaml
-> 将输出粘贴到path/to/repo/sealedsecret.yaml的spec.encryptedData.password下
printf '%s' 'new-value' | kubeseal --cert "$CERT"
--raw --namespace "$NS" --name "$NAME" --from-file=/dev/stdin
--raw --namespace "$NS" --name "$NAME" --from-file=/dev/stdin
printf '%s' 'new-value' | kubeseal --cert "$CERT"
--raw --namespace "$NS" --name "$NAME" --from-file=/dev/stdin
--raw --namespace "$NS" --name "$NAME" --from-file=/dev/stdin
-> paste the output under spec.encryptedData.access-key-id
-> 将输出粘贴到path/to/repo/sealedsecret.yaml的spec.encryptedData.access-key-id下
Or use `--merge-into` (see below) to update individual keys in place without touching the others.
**If you must build a full Secret object** (e.g. a fresh SealedSecret from scratch), avoid quoting pitfalls by base64-encoding values into the `data:` field instead of `stringData:`:
```bash
cat > /tmp/updated-secret.yaml <<EOF
apiVersion: v1
kind: Secret
metadata:
name: $NAME
namespace: $NS
type: Opaque
data:
access-key-id: $(printf '%s' 'new-value' | base64 -w0)
password: $(printf '%s' "$EXISTING_PASS" | base64 -w0)
EOF
kubeseal --cert "$CERT" --format yaml -f /tmp/updated-secret.yaml > path/to/repo/sealedsecret.yaml
或者使用`--merge-into`(见下文)原地更新单个密钥,不影响其他密钥。
**如果必须构建完整的Secret对象**(例如,从头开始创建新的SealedSecret),为避免引号问题,可将值base64编码后放入`data:`字段,而非`stringData:`:
```bash
cat > /tmp/updated-secret.yaml <<EOF
apiVersion: v1
kind: Secret
metadata:
name: $NAME
namespace: $NS
type: Opaque
data:
access-key-id: $(printf '%s' 'new-value' | base64 -w0)
password: $(printf '%s' "$EXISTING_PASS" | base64 -w0)
EOF
kubeseal --cert "$CERT" --format yaml -f /tmp/updated-secret.yaml > path/to/repo/sealedsecret.yamlSANITIZE the plaintext temp file
清理明文临时文件
echo "" > /tmp/updated-secret.yaml
undefinedecho "" > /tmp/updated-secret.yaml
undefined3. Obtain the Public Key
3. 获取公钥
See "Obtaining the Certificate" above — first, then the Kubernetes MCP. No other source is permitted, and the cert must be validated with before sealing.
certs/openssl x509参见上文**“获取证书”**——优先从获取,其次是Kubernetes MCP。不允许使用其他来源,且密封前必须使用验证证书。
certs/openssl x509kubeseal Command Reference
kubeseal命令参考
Common Flags
常用标志
| Flag | Purpose |
|---|---|
| Public key file for encryption. Always pass this explicitly — omitting it makes kubeseal auto-detect the controller. Use a local file only, never a URL |
| Output format (default: json) |
| Input Secret YAML file (use |
| Namespace scope for the secret being sealed (not the controller's location) |
| Scoping of the sealed secret (default: |
| Merge sealed keys into existing SealedSecret file (in-place) |
| Encrypt a single raw value from |
| (with |
| Name of the sealed secret (required with |
| Re-encrypt an existing SealedSecret with the controller's latest key (needs cluster) |
| Verify the sealed secret decrypts — contacts the controller; requires cluster access |
| FORBIDDEN — do not use. Get the cert from |
| Namespace where the controller runs (default: |
| Controller name (default: |
| Disaster-recovery decrypt using |
| 标志 | 用途 |
|---|---|
| 用于加密的公钥文件。始终显式传递此标志——省略它会让kubeseal自动检测控制器。仅使用本地文件,绝不要使用URL |
| 输出格式(默认:json) |
| 输入Secret YAML文件(使用 |
| 要密封的密钥的命名空间作用域(不是控制器的位置) |
| 密封密钥的作用域(默认: |
| 将密封的密钥合并到现有SealedSecret文件中(原地修改) |
| 从 |
| (与 |
| 密封密钥的名称(在strict作用域下与 |
| 使用控制器的最新密钥重新加密现有SealedSecret(需要访问集群) |
| 验证密封的密钥是否可解密——会联系控制器;需要访问集群 |
| **禁止使用——请勿使用。**请从 |
| 控制器运行的命名空间(默认: |
| 控制器名称(默认: |
| 使用 |
Scope Modes
作用域模式
- (default): Can only be unsealed with the exact namespace and name.
strict - : Can be unsealed in the specified namespace with any name.
namespace-wide - : Can be unsealed in any namespace with any name.
cluster-wide
- (默认):只能使用完全匹配的命名空间和名称解封。
strict - :可在指定命名空间中使用任意名称解封。
namespace-wide - :可在任意命名空间中使用任意名称解封。
cluster-wide
Merge Into Existing
合并到现有文件
To update individual keys without re-sealing the entire secret (useful when you can't retrieve all plaintext values):
bash
undefined要更新单个密钥而无需重新密封整个密钥(当无法检索所有明文值时很有用):
bash
undefinedSeal only the keys you want to update
仅密封要更新的密钥
cat > /tmp/delta-secret.yaml <<'EOF'
apiVersion: v1
kind: Secret
metadata:
name: my-secret
namespace: my-namespace
type: Opaque
stringData:
new-key: "new-value"
EOF
cat > /tmp/delta-secret.yaml <<'EOF'
apiVersion: v1
kind: Secret
metadata:
name: my-secret
namespace: my-namespace
type: Opaque
stringData:
new-key: "new-value"
EOF
Merge into existing sealed secret
合并到现有密封密钥中
kubeseal --cert "$CERT" --merge-into path/to/repo/sealedsecret.yaml -f /tmp/delta-secret.yaml
kubeseal --cert "$CERT" --merge-into path/to/repo/sealedsecret.yaml -f /tmp/delta-secret.yaml
SANITIZE
清理临时文件
echo "" > /tmp/delta-secret.yaml
**WARNING**: `--merge-into` adds or updates keys but does NOT remove keys. To remove a key, you must re-seal the entire secret from scratch.echo "" > /tmp/delta-secret.yaml
**警告**:`--merge-into`会添加或更新密钥,但**不会删除密钥**。要删除密钥,必须从头开始重新密封整个密钥。Encrypt a Single Value (--raw
)
--raw加密单个值(--raw
)
--raw--rawspec.encryptedData.<key>bash
undefined--rawspec.encryptedData.<key>bash
undefinedstrict scope (default): --name AND --namespace are REQUIRED and must match the target SealedSecret
strict作用域(默认):必须指定--name和--namespace,且必须与目标SealedSecret匹配
printf '%s' 'p@ss"word:with$pecial' | kubeseal --cert "$CERT"
--raw --namespace my-namespace --name my-secret --from-file=/dev/stdin
--raw --namespace my-namespace --name my-secret --from-file=/dev/stdin
printf '%s' 'p@ss"word:with$pecial' | kubeseal --cert "$CERT"
--raw --namespace my-namespace --name my-secret --from-file=/dev/stdin
--raw --namespace my-namespace --name my-secret --from-file=/dev/stdin
namespace-wide: only --namespace required
namespace-wide作用域:仅需指定--namespace
printf '%s' 'value' | kubeseal --cert "$CERT"
--raw --scope namespace-wide --namespace my-namespace --from-file=/dev/stdin
--raw --scope namespace-wide --namespace my-namespace --from-file=/dev/stdin
printf '%s' 'value' | kubeseal --cert "$CERT"
--raw --scope namespace-wide --namespace my-namespace --from-file=/dev/stdin
--raw --scope namespace-wide --namespace my-namespace --from-file=/dev/stdin
cluster-wide: neither required
cluster-wide作用域:无需指定任何参数
printf '%s' 'value' | kubeseal --cert "$CERT"
--raw --scope cluster-wide --from-file=/dev/stdin
--raw --scope cluster-wide --from-file=/dev/stdin
- Use `printf '%s'` (not `echo`) to avoid appending a trailing newline to the secret value.
- The `--raw` ciphertext is bound to the scope/name/namespace you pass. If they don't match the SealedSecret it's pasted into, the controller will refuse to unseal it.printf '%s' 'value' | kubeseal --cert "$CERT"
--raw --scope cluster-wide --from-file=/dev/stdin
--raw --scope cluster-wide --from-file=/dev/stdin
- 使用`printf '%s'`(而非`echo`)避免向密钥值添加尾随换行符。
- `--raw`生成的密文与您传递的作用域/名称/命名空间绑定。如果它们与粘贴到的SealedSecret不匹配,控制器会拒绝解封。Rotate to a New Controller Key (--re-encrypt
)
--re-encrypt轮换到新的控制器密钥(--re-encrypt
)
--re-encryptAfter the controller's key pair is rotated, existing SealedSecrets still decrypt (the controller keeps old keys) but should be re-encrypted to the latest key. This requires cluster access:
bash
kubeseal --re-encrypt -o yaml -f path/to/repo/sealedsecret.yaml > /tmp/reencrypted.yaml
cp /tmp/reencrypted.yaml path/to/repo/sealedsecret.yaml--re-encrypt控制器的密钥对轮换后,现有SealedSecrets仍然可以解密(控制器会保留旧密钥),但应重新加密为最新密钥。这需要访问集群:
bash
kubeseal --re-encrypt -o yaml -f path/to/repo/sealedsecret.yaml > /tmp/reencrypted.yaml
cp /tmp/reencrypted.yaml path/to/repo/sealedsecret.yaml--re-encryptSealedSecret YAML Structure
SealedSecret YAML结构
yaml
apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
name: my-secret
namespace: my-namespace
spec:
encryptedData:
key1: AgC...base64encrypteddata...==
key2: AgC...base64encrypteddata...==
template:
metadata:
name: my-secret
namespace: my-namespace
type: Opaqueyaml
apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
name: my-secret
namespace: my-namespace
spec:
encryptedData:
key1: AgC...base64encrypteddata...==
key2: AgC...base64encrypteddata...==
template:
metadata:
name: my-secret
namespace: my-namespace
type: OpaqueCommon Patterns
常见模式
S3-Compatible Object Store Credentials
S3兼容对象存储凭证
yaml
apiVersion: v1
kind: Secret
metadata:
name: s3-credentials
namespace: my-namespace
type: Opaque
stringData:
access-key-id: "<access-key>"
secret-access-key: "<secret-key>"yaml
apiVersion: v1
kind: Secret
metadata:
name: s3-credentials
namespace: my-namespace
type: Opaque
stringData:
access-key-id: "<access-key>"
secret-access-key: "<secret-key>"Database Credentials
数据库凭证
yaml
apiVersion: v1
kind: Secret
metadata:
name: db-credentials
namespace: my-namespace
type: Opaque
stringData:
username: "admin"
password: "<password>"yaml
apiVersion: v1
kind: Secret
metadata:
name: db-credentials
namespace: my-namespace
type: Opaque
stringData:
username: "admin"
password: "<password>"TLS Certificate
TLS证书
yaml
apiVersion: v1
kind: Secret
metadata:
name: tls-cert
namespace: my-namespace
type: kubernetes.io/tls
stringData:
tls.crt: |
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
tls.key: |
-----BEGIN PRIVATE KEY-----
...
-----END PRIVATE KEY-----yaml
apiVersion: v1
kind: Secret
metadata:
name: tls-cert
namespace: my-namespace
type: kubernetes.io/tls
stringData:
tls.crt: |
-----BEGIN CERTIFICATE-----
...
-----END CERTIFICATE-----
tls.key: |
-----BEGIN PRIVATE KEY-----
...
-----END PRIVATE KEY-----Pitfalls and Gotchas
陷阱与注意事项
-
Each key is encrypted independently — you can't mix encrypted values from different sealing operations into the sameblock without re-sealing. Use
encryptedDataor re-seal the entire secret.--merge-into -
SealedSecrets are bound to the controller's key pair — the controller retains old keys after rotation, so existing SealedSecrets keep decrypting. But new seals need the current public cert, and best practice is toexisting SealedSecrets onto the latest key. If old keys are ever purged, un-re-encrypted SealedSecrets become undecryptable.
kubeseal --re-encrypt -
vs
stringData— usedatafor plaintext values (kubeseal handles encoding). UsestringDatafor pre-base64-encoded values.data -
Scope matters — a-scoped SealedSecret can only be unsealed with the exact name and namespace. If you rename the secret, it won't unseal. Use
strictornamespace-wideif name changes are expected.cluster-wide -
Temp file hygiene — always overwrite temp files after sealing. Never leave plaintext secrets on disk.
-
Git history — if a secret was accidentally committed, it remains in git history. Useto remove it, then rotate the credential.
git filter-repo -
A cert-shaped file may not be a cert (seen in the wild).against a cluster with no controller writes this into the file:
kubeseal --fetch-cert > certs/sealed-secrets-cert.pemerror: cannot get sealed secret service: services "sealed-secrets-controller" not found.Sealing against it appears to succeed, and the corruption only surfaces at unseal time asorillegal base64 data at input byte N— by which point the plaintext may be gone. This is exactly whyno key could decrypt secretis forbidden and why--fetch-certvalidation is mandatory before every seal. A cert file whose size is a few hundred bytes with noopenssl x509line is this failure.-----BEGIN CERTIFICATE----- -
A private key inis a security incident, not a convenience. If
certs/containscerts/alongside the cert, anyone with repo access can decrypt every SealedSecret in it. Report it: the controller key should be rotated and the file purged from git history. Only ever readtls.keyfor sealing.tls.crt
-
每个密钥独立加密——不能将不同密封操作生成的加密值混合到同一个块中,除非重新密封。使用
encryptedData或重新密封整个密钥。--merge-into -
SealedSecrets与控制器的密钥对绑定——控制器轮换密钥后会保留旧密钥,因此现有SealedSecrets仍可解密。但新的密封操作需要当前的公证书,最佳实践是使用将现有SealedSecrets重新加密为最新密钥。如果旧密钥被清除,未重新加密的SealedSecrets将无法解密。
kubeseal --re-encrypt -
vs
stringData——对明文值使用data(kubeseal会处理编码)。对已base64编码的值使用stringData。data -
作用域很重要——作用域的SealedSecrets只能使用完全匹配的名称和命名空间解封。如果重命名密钥,它将无法解封。如果预期会更改名称,请使用
strict或namespace-wide作用域。cluster-wide -
临时文件卫生——密封后始终覆盖临时文件。绝不要在磁盘上留下明文密钥。
-
Git历史——如果密钥被意外提交,它仍会保留在Git历史中。使用将其移除,然后轮换凭证。
git filter-repo -
**形似证书的文件可能不是证书(实际场景中已出现)。**在没有控制器的集群上运行会将以下内容写入文件:
kubeseal --fetch-cert > certs/sealed-secrets-cert.pemerror: cannot get sealed secret service: services "sealed-secrets-controller" not found.使用它进行密封看似成功,但损坏只会在解封时显现,报错为或illegal base64 data at input byte N——此时明文可能已经丢失。这正是禁止使用no key could decrypt secret以及每次密封前必须进行--fetch-cert验证的原因。如果证书文件大小只有几百字节且没有openssl x509行,就是这种失败情况。-----BEGIN CERTIFICATE----- -
**中的私钥是安全事件,而非便利。**如果
certs/中同时包含certs/和证书,任何拥有仓库访问权限的人都可以解密其中的所有SealedSecrets。报告此问题:应轮换控制器密钥并从Git历史中清除该文件。密封时仅读取tls.key。tls.crt
Verification
验证
Before deploying, validate the sealed secret against the live controller (this contacts the cluster — it does not work offline):
bash
undefined部署前,针对在线控制器验证密封的密钥(这会联系集群——无法离线工作):
bash
undefined--validate talks to the controller (no --cert needed); add --controller-namespace/--controller-name if non-default
--validate会与控制器通信(无需--cert);如果控制器不是默认配置,添加--controller-namespace/--controller-name
kubeseal --validate -f path/to/repo/sealedsecret.yaml
**After deploying**, verify it unsealed correctly:
```bashkubeseal --validate -f path/to/repo/sealedsecret.yaml
**部署后**,验证它是否已正确解封:
```bashCheck the Secret exists and has the expected keys
检查Secret是否存在并包含预期的密钥
kubectl get secret <name> -n <namespace> -o jsonpath='{.data}' | python3 -c "import sys,json; [print(k) for k in json.load(sys.stdin).keys()]"
Or use the `mcp__kubernetes__resources_get` MCP tool to inspect the decrypted Secret.kubectl get secret <name> -n <namespace> -o jsonpath='{.data}' | python3 -c "import sys,json; [print(k) for k in json.load(sys.stdin).keys()]"
或使用`mcp__kubernetes__resources_get` MCP工具检查解密后的Secret。Post-Sealing Checklist
密封后检查清单
- Cert came from or the Kubernetes MCP — never
certs/,--fetch-cert, or a URLkubectl - Cert passed validation before sealing
openssl x509 - Temp files overwritten (not just deleted)
- No plaintext credentials in shell history (use before sensitive operations)
set +o history - No credentials in git diff output
- SealedSecret YAML committed to the correct branch
- No seal private keys () in the repository or output
tls.key - Verified the sealed secret unseals correctly on the cluster
- 证书来自或Kubernetes MCP——绝不是
certs/、kubectl或URL--fetch-cert - 密封前证书通过了验证
openssl x509 - 临时文件已被覆盖(不只是删除)
- Shell历史中没有明文凭证(敏感操作前使用)
set +o history - Git diff输出中没有凭证
- SealedSecret YAML已提交到正确的分支
- 仓库或输出中没有密封私钥()
tls.key - 已验证密封的密钥在集群上可正确解封