kubeseal

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Kubeseal: 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 against
kubeseal
v0.38.4. Flag names are stable across recent releases, but run
kubeseal --help
if a command behaves unexpectedly.
Key insight: the public certificate (
tls.crt
) is not secret — it is a public key, safe to share and even commit. Only the controller's private key (
tls.key
) is sensitive. Sealing is a purely local, offline operation once you have the cert; no cluster access is needed to seal.
将Kubernetes Secrets密封为Bitnami SealedSecrets,这类Secrets可以安全地存储在Git中。SealedSecrets采用非对称加密——任何人都可以加密(密封),但只有集群中的sealed-secrets控制器才能解密(解封)。
已针对
kubeseal
v0.38.4验证。近期版本的标志名称保持稳定,但如果命令行为不符合预期,请运行
kubeseal --help
核心要点:公证书(
tls.crt
)并非机密——它是公钥,可安全共享甚至提交到仓库。只有控制器的私钥(
tls.key
)是敏感信息。一旦获取到证书,密封操作完全是本地离线操作,无需访问集群即可完成密封。

CRITICAL SECURITY RULES

关键安全规则

NEVER LEAK CREDENTIALS

绝对不能泄露凭证

  1. NEVER print plaintext credentials to stdout, conversation output, or any readable medium.
  2. NEVER write plaintext secrets to files in the repository or any version-controlled location.
  3. NEVER echo or log secret values — use variables and pipe directly.
  4. NEVER include plaintext secrets in commit messages, PR descriptions, or comments.
  5. ALWAYS overwrite or zero-out temp files containing plaintext secrets immediately after use.
  6. NEVER leak seal key material — the private key (
    tls.key
    ) must never be printed, committed, or shared. Only the public key (
    tls.crt
    ) is needed for sealing.
  1. 绝对不能将明文凭证输出到标准输出、对话内容或任何可读介质中。
  2. 绝对不能将明文密钥写入仓库或任何版本控制位置的文件中。
  3. 绝对不能回显或记录密钥值——使用变量并直接通过管道传输。
  4. 绝对不能在提交信息、PR描述或评论中包含明文密钥
  5. 使用后立即覆盖或清零包含明文密钥的临时文件。
  6. 绝对不能泄露密封密钥材料——私钥(
    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 (
--raw
)", both below. When a temp file is unavoidable:
  • Write temp secret files to
    /tmp/
    with descriptive names.
  • After sealing, overwrite the temp file contents with empty or garbage data.
  • Never
    rm
    temp files (may be blocked by permission rules) — overwrite instead:
    bash
    echo "" > /tmp/secret-temp.yaml
  • Never commit temp files to the repository.
Note: the public cert (
tls.crt
/
.pem
) 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.
优先选择从不将明文写入磁盘上命名文件的方法——下文“1. 新建SealedSecret”中的标准输入HereDoc,或“加密单个值(
--raw
)”方法。当必须使用临时文件时:
  • 将临时密钥文件写入
    /tmp/
    目录,并使用描述性名称。
  • 密封完成后,覆盖临时文件内容为空白或无用数据。
  • 不要使用
    rm
    删除临时文件(可能会被权限规则阻止)——而是覆盖:
    bash
    echo "" > /tmp/secret-temp.yaml
  • 绝对不要将临时文件提交到仓库。
注意:公证书(
tls.crt
/
.pem
不敏感无需清理——将其密封起来只会导致需要重新获取。仅覆盖包含明文密钥值私钥的文件。

Prerequisites

前提条件

  • kubeseal
    CLI installed and available
  • Public key certificate (
    tls.crt
    ) — obtained only as described in "Obtaining the Certificate" below
  • Access to the Kubernetes cluster via the Kubernetes MCP (for the cert fallback, and for retrieving existing secret values)
  • 已安装并可使用
    kubeseal
    CLI
  • 公钥证书(
    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

来源1(首选)——项目的
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
undefined

From 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.crt
If
certs/
holds more than one candidate, validate each and prefer the one that verifies against the repo's existing SealedSecrets.
find . -type d -name certs -not -path '/.git/'

将`CERT`设置为找到的文件,然后**使用前验证它**(参见“始终验证证书”):

```bash
CERT=certs/tls.crt
如果
certs/
中有多个候选证书,验证每个证书并优先选择可与仓库中现有SealedSecrets匹配的证书。

Source 2 (fallback) — the cluster, via the Kubernetes MCP

来源2(回退)——集群,通过Kubernetes MCP

Only if
certs/
has no valid cert. Use the Kubernetes MCP tools to read the controller's active key Secret:
Step 1. Call
mcp__kubernetes__resources_list
with:
  • apiVersion: v1
    ,
    kind: Secret
  • labelSelector: sealedsecrets.bitnami.com/sealed-secrets-key=active
  • namespace
    : wherever the controller runs (commonly
    kube-system
    or
    sealed-secrets
    ); omit to search all namespaces
If several
active
key Secrets come back, pick the one with the most recent
metadata.creationTimestamp
— that is the key the controller currently seals with.
Step 2. Take
data["tls.crt"]
from that Secret and base64-decode it into a local file:
bash
printf '%s' '<tls.crt base64 from the MCP response>' | base64 -d > /tmp/sealed-secrets-cert.pem
CERT=/tmp/sealed-secrets-cert.pem
Step 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
tls.key
, the controller's private key.
Read and use only the
tls.crt
field. Never decode, print, echo, write, or commit
tls.key
. Do not paste the raw MCP response anywhere.
仅当
certs/
中没有有效证书时使用。使用Kubernetes MCP工具读取控制器的活动密钥Secret:
步骤1. 调用
mcp__kubernetes__resources_list
,参数如下:
  • apiVersion: v1
    kind: Secret
  • labelSelector: sealedsecrets.bitnami.com/sealed-secrets-key=active
  • namespace
    :控制器运行的命名空间(通常为
    kube-system
    sealed-secrets
    );省略则搜索所有命名空间
如果返回多个
active
密钥Secret,选择
metadata.creationTimestamp
最新的那个——这是控制器当前使用的密封密钥。
步骤2. 从该Secret中取出
data["tls.crt"]
并进行base64解码,保存到本地文件:
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
字段。绝对不要解码、打印、回显、写入或提交
tls.key
。不要将原始MCP响应粘贴到任何地方。

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
illegal base64 data at input byte N
or
no key could decrypt secret
— long after the plaintext is gone.
Run 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
certs/
file fails validation, report it to the user and move to Source 2. Never "fix" it by guessing.
**不能仅因为文件存在就认为它是证书。**重定向操作可能会将错误文本捕获成类似证书的文件,使用此类无效文件进行密封会生成SealedSecret,之后解封时会失败,报错为
illegal base64 data at input byte N
no key could decrypt secret
——此时明文可能已经丢失。
每次密封前都要运行此命令。如果失败,请停止——使用下一个来源获取证书,不要继续密封:
bash
openssl x509 -in "$CERT" -noout -subject -dates || echo "无效证书——请勿使用此文件进行密封"
如果
certs/
中的文件验证失败,告知用户并切换到来源2。不要通过猜测来“修复”它。

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"
EOF
Alternative — 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
undefined

Write 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
undefined
echo "" > /tmp/my-secret.yaml
undefined

2. 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
,
no key could decrypt secret
) or because a credential changed. Resealing rewrites
spec.encryptedData
in place; keep
metadata
,
spec.template
(labels, annotations,
type
), name, and namespace byte-identical to the original unless the user asks otherwise, and match the surrounding files' conventions.
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
    --raw
    (paste into
    encryptedData
    ) or
    --merge-into
    — you only need the values of the keys you're changing.
  • 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__kubernetes__resources_get
MCP tool to read the decrypted Secret — the SealedSecret controller auto-decrypts into a regular Secret in the cluster (
apiVersion: v1
,
kind: Secret
, plus the name and namespace).
Or via kubectl:
bash
kubectl get secret <name> -n <namespace> -o jsonpath='{.data}'
优先使用
mcp__kubernetes__resources_get
MCP工具读取解密后的Secret——SealedSecrets控制器会自动将其解密为集群中的常规Secret(
apiVersion: v1
kind: 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
"
,
:
,
\n
, leading spaces, or
$
will break the quoting and either corrupt the secret or make
kubeseal
fail with
error: no secrets found
. This is common with generated passwords and S3 keys. Never build YAML like
password: "$EXISTING_PASS"
.
Safe approach — seal each key individually with
--raw
, then merge.
--raw
reads the value from stdin as raw bytes (no YAML quoting involved), so special characters are handled correctly. Under the default
strict
scope you must pass
--name
and
--namespace
, and they must match the target SealedSecret.
bash
NS=my-namespace
NAME=my-secret
**危险——不要将密钥值插入YAML中。**包含
"
:
\n
、前导空格或
$
的值会破坏引号,导致密钥损坏或
kubeseal
报错
error: no secrets found
。这种情况在生成密码和S3密钥时很常见。绝对不要
password: "$EXISTING_PASS"
这样构建YAML。
安全方法——使用
--raw
单独密封每个密钥,然后合并。
--raw
从标准输入读取原始字节值(不涉及YAML引号),因此可以正确处理特殊字符。在默认的
strict
作用域下,必须传递
--name
--namespace
,且它们必须与目标SealedSecret匹配。
bash
NS=my-namespace
NAME=my-secret

Decode 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
printf '%s' "$EXISTING_PASS" | kubeseal --cert "$CERT"
--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
printf '%s' 'new-value' | kubeseal --cert "$CERT"
--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.yaml

SANITIZE the plaintext temp file

清理明文临时文件

echo "" > /tmp/updated-secret.yaml
undefined
echo "" > /tmp/updated-secret.yaml
undefined

3. Obtain the Public Key

3. 获取公钥

See "Obtaining the Certificate" above —
certs/
first, then the Kubernetes MCP. No other source is permitted, and the cert must be validated with
openssl x509
before sealing.
参见上文**“获取证书”**——优先从
certs/
获取,其次是Kubernetes MCP。不允许使用其他来源,且密封前必须使用
openssl x509
验证证书。

kubeseal Command Reference

kubeseal命令参考

Common Flags

常用标志

FlagPurpose
--cert <file>
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
-o, --format yaml|json
Output format (default: json)
-f, --secret-file <file>
Input Secret YAML file (use
/dev/stdin
to pipe)
-n, --namespace <ns>
Namespace scope for the secret being sealed (not the controller's location)
--scope strict|namespace-wide|cluster-wide
Scoping of the sealed secret (default:
strict
)
--merge-into <file>
Merge sealed keys into existing SealedSecret file (in-place)
--raw
Encrypt a single raw value from
--from-file
; requires
--scope
, plus
--name
+
--namespace
for strict scope
--from-file <file>
(with
--raw
) Source the value from a file; use
/dev/stdin
for a pipe
--name <name>
Name of the sealed secret (required with
--raw
under strict scope)
--re-encrypt
Re-encrypt an existing SealedSecret with the controller's latest key (needs cluster)
--validate
Verify the sealed secret decrypts — contacts the controller; requires cluster access
--fetch-cert
FORBIDDEN — do not use. Get the cert from
certs/
or the Kubernetes MCP instead (see "Obtaining the Certificate"). On failure it emits an error message to stdout that a redirect turns into a bogus cert file
--controller-namespace <ns>
Namespace where the controller runs (default:
kube-system
). Applies to
--validate
/
--re-encrypt
only — never for fetching the cert
--controller-name <name>
Controller name (default:
sealed-secrets-controller
). Same restriction as above
--recovery-unseal
Disaster-recovery decrypt using
--recovery-private-key
(handles the private key — use with extreme caution)
标志用途
--cert <file>
用于加密的公钥文件。始终显式传递此标志——省略它会让kubeseal自动检测控制器。仅使用本地文件,绝不要使用URL
-o, --format yaml|json
输出格式(默认:json)
-f, --secret-file <file>
输入Secret YAML文件(使用
/dev/stdin
进行管道传输)
-n, --namespace <ns>
要密封的密钥的命名空间作用域(不是控制器的位置)
--scope strict|namespace-wide|cluster-wide
密封密钥的作用域(默认:
strict
--merge-into <file>
将密封的密钥合并到现有SealedSecret文件中(原地修改)
--raw
--from-file
加密单个原始值;需要
--scope
,在strict作用域下还需要
--name
+
--namespace
--from-file <file>
(与
--raw
一起使用)从文件获取值;使用
/dev/stdin
进行管道传输
--name <name>
密封密钥的名称(在strict作用域下与
--raw
一起使用时必填)
--re-encrypt
使用控制器的最新密钥重新加密现有SealedSecret(需要访问集群)
--validate
验证密封的密钥是否可解密——会联系控制器;需要访问集群
--fetch-cert
**禁止使用——请勿使用。**请从
certs/
或Kubernetes MCP获取证书(参见“获取证书”)。失败时它会向标准输出发送错误信息,重定向后会生成虚假的证书文件
--controller-namespace <ns>
控制器运行的命名空间(默认:
kube-system
)。仅适用于
--validate
/
--re-encrypt
——绝不要用于获取证书
--controller-name <name>
控制器名称(默认:
sealed-secrets-controller
)。限制同上
--recovery-unseal
使用
--recovery-private-key
进行灾难恢复解密(涉及私钥——需极度谨慎使用

Scope Modes

作用域模式

  • strict
    (default): Can only be unsealed with the exact namespace and name.
  • namespace-wide
    : Can be unsealed in the specified namespace with any name.
  • cluster-wide
    : Can be unsealed in any namespace with any name.
  • 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
undefined

Seal 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
encrypts one value and prints the ciphertext string — you paste it under
spec.encryptedData.<key>
yourself. This is the safest way to handle values with special characters (it reads raw bytes from stdin, bypassing YAML quoting entirely) and it never writes plaintext to a file.
bash
undefined
--raw
加密一个值并打印密文字符串——您需要自行将其粘贴到
spec.encryptedData.<key>
下。这是处理包含特殊字符的值的最安全方式(它从标准输入读取原始字节,完全绕过YAML引号),且绝不会将明文写入文件。
bash
undefined

strict 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
printf '%s' 'p@ss"word:with$pecial' | kubeseal --cert "$CERT"
--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
printf '%s' 'value' | kubeseal --cert "$CERT"
--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

- 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

- 使用`printf '%s'`(而非`echo`)避免向密钥值添加尾随换行符。
- `--raw`生成的密文与您传递的作用域/名称/命名空间绑定。如果它们与粘贴到的SealedSecret不匹配,控制器会拒绝解封。

Rotate to a New Controller Key (
--re-encrypt
)

轮换到新的控制器密钥(
--re-encrypt

After 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
never exposes plaintext — the re-encryption happens inside the controller.
控制器的密钥对轮换后,现有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-encrypt
绝不会暴露明文——重新加密操作在控制器内部完成。

SealedSecret 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: Opaque
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: Opaque

Common 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

陷阱与注意事项

  1. Each key is encrypted independently — you can't mix encrypted values from different sealing operations into the same
    encryptedData
    block without re-sealing. Use
    --merge-into
    or re-seal the entire secret.
  2. 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 to
    kubeseal --re-encrypt
    existing SealedSecrets onto the latest key. If old keys are ever purged, un-re-encrypted SealedSecrets become undecryptable.
  3. stringData
    vs
    data
    — use
    stringData
    for plaintext values (kubeseal handles encoding). Use
    data
    for pre-base64-encoded values.
  4. Scope matters — a
    strict
    -scoped SealedSecret can only be unsealed with the exact name and namespace. If you rename the secret, it won't unseal. Use
    namespace-wide
    or
    cluster-wide
    if name changes are expected.
  5. Temp file hygiene — always overwrite temp files after sealing. Never leave plaintext secrets on disk.
  6. Git history — if a secret was accidentally committed, it remains in git history. Use
    git filter-repo
    to remove it, then rotate the credential.
  7. A cert-shaped file may not be a cert (seen in the wild).
    kubeseal --fetch-cert > certs/sealed-secrets-cert.pem
    against a cluster with no controller writes this into the file:
    error: 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 as
    illegal base64 data at input byte N
    or
    no key could decrypt secret
    — by which point the plaintext may be gone. This is exactly why
    --fetch-cert
    is forbidden and why
    openssl x509
    validation is mandatory before every seal. A cert file whose size is a few hundred bytes with no
    -----BEGIN CERTIFICATE-----
    line is this failure.
  8. A private key in
    certs/
    is a security incident, not a convenience.
    If
    certs/
    contains
    tls.key
    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 read
    tls.crt
    for sealing.
  1. 每个密钥独立加密——不能将不同密封操作生成的加密值混合到同一个
    encryptedData
    块中,除非重新密封。使用
    --merge-into
    或重新密封整个密钥。
  2. SealedSecrets与控制器的密钥对绑定——控制器轮换密钥后会保留旧密钥,因此现有SealedSecrets仍可解密。但新的密封操作需要当前的公证书,最佳实践是使用
    kubeseal --re-encrypt
    将现有SealedSecrets重新加密为最新密钥。如果旧密钥被清除,未重新加密的SealedSecrets将无法解密。
  3. stringData
    vs
    data
    ——对明文值使用
    stringData
    (kubeseal会处理编码)。对已base64编码的值使用
    data
  4. 作用域很重要——
    strict
    作用域的SealedSecrets只能使用完全匹配的名称和命名空间解封。如果重命名密钥,它将无法解封。如果预期会更改名称,请使用
    namespace-wide
    cluster-wide
    作用域。
  5. 临时文件卫生——密封后始终覆盖临时文件。绝不要在磁盘上留下明文密钥。
  6. Git历史——如果密钥被意外提交,它仍会保留在Git历史中。使用
    git filter-repo
    将其移除,然后轮换凭证。
  7. **形似证书的文件可能不是证书(实际场景中已出现)。**在没有控制器的集群上运行
    kubeseal --fetch-cert > certs/sealed-secrets-cert.pem
    会将以下内容写入文件:
    error: 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-----
    行,就是这种失败情况。
  8. **
    certs/
    中的私钥是安全事件,而非便利。**如果
    certs/
    中同时包含
    tls.key
    和证书,任何拥有仓库访问权限的人都可以解密其中的所有SealedSecrets。报告此问题:应轮换控制器密钥并从Git历史中清除该文件。密封时仅读取
    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:

```bash
kubeseal --validate -f path/to/repo/sealedsecret.yaml

**部署后**,验证它是否已正确解封:

```bash

Check 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
    certs/
    or the Kubernetes MCP — never
    --fetch-cert
    ,
    kubectl
    , or a URL
  • Cert passed
    openssl x509
    validation before sealing
  • Temp files overwritten (not just deleted)
  • No plaintext credentials in shell history (use
    set +o history
    before sensitive operations)
  • No credentials in git diff output
  • SealedSecret YAML committed to the correct branch
  • No seal private keys (
    tls.key
    ) in the repository or output
  • Verified the sealed secret unseals correctly on the cluster
  • 证书来自
    certs/
    或Kubernetes MCP——绝不是
    --fetch-cert
    、kubectl或URL
  • 密封前证书通过了
    openssl x509
    验证
  • 临时文件已被覆盖(不只是删除)
  • Shell历史中没有明文凭证(敏感操作前使用
    set +o history
  • Git diff输出中没有凭证
  • SealedSecret YAML已提交到正确的分支
  • 仓库或输出中没有密封私钥(
    tls.key
  • 已验证密封的密钥在集群上可正确解封