Secret 的处理与静态加密
目标
按类型创建 Secret,确认不同注入方式会产生怎样的暴露路径,使用 RBAC 收窄读取范围,并亲自编写静态加密配置文件。
为什么重要
Secret 清单中的值采用 base64,看起来仿佛经过加密,但 base64 只是编码,不是加密。它没有密钥,只需一条命令即可还原。在默认配置下,Secret 以明文保存在 etcd 中,因此任何取得 etcd 备份文件的人都能读取所有 Secret。只有启用 EncryptionConfiguration 后,存储的值才会真正加密;此时 identity Provider 必须位于列表最后。如果它在前面,就会退回明文存储。
注入方式也很重要。环境变量可通过 /proc/PID/environ 读取,会被子进程继承,还可能完整出现在崩溃报告或调试页面中。卷挂载更加安全,通常还会使用 defaultMode 收紧文件权限。
最后是 RBAC。在某个命名空间拥有 get secrets 权限的人,可以读取该命名空间中的所有 Secret。可以通过 resourceNames 只允许访问特定 Secret,这一点出乎意料地鲜为人知。
步骤
- 创建命名空间
cks-secrets,并在其中创建 Opaque Secretdb-cred。 包含两个键:username(值为app)和password(值为pr0d-Db-Pass)。 - 在
cks-secrets中创建类型为kubernetes.io/dockerconfigjson的 Secretregistry-cred。 服务器为registry.cks.local,用户名为ci,密码为ci-token。 - 在
cks-secrets中创建类型为kubernetes.io/tls的 Secretshop-tls。tls.crt和tls.key均不得为空。 - 在
cks-secrets中创建 Podenv-app(容器名称app,镜像nginx:1.27-alpine)。 容器的envFrom[0].secretRef.name为db-cred。 - 在
cks-secrets中创建 Podvol-app(容器名称app)。卷名称为db,secret.secretName为db-cred,secret.defaultMode为0400(十进制 256), 容器将该卷挂载到/etc/db,并设置readOnly: true。 - 在
cks-secrets中创建 Opaque Secretapp-config。唯一的键为mode(值为strict),immutable为true。 - 在
cks-secrets中创建 ServiceAccountapp和 Roledb-cred-reader。 apiGroups 为核心组,resources 为secrets,resourceNames仅为db-cred, verbs 仅为get。通过 RoleBindingapp-db-cred绑定给 SAapp。 然后按顺序将两个问题的答案(yes/no)分两行保存到/root/cks-secrets/can-i.txt。第一行:SAapp能否 getsecret/db-cred; 第二行:能否 getsecret/app-config。 - 在
/root/cks-secrets/encryption-config.yaml中编写静态加密配置。apiVersion: apiserver.config.k8s.io/v1,kind: EncryptionConfiguration,resources[0].resources只有secrets;providers有两个,第一个为aescbc(键名key1,secret 为 32 字节值的 base64 编码),最后一个为identity。
参考
kubectl create secret generic db-cred --from-literal=username=app --from-literal=password=pr0d-Db-Pass -n cks-secrets- 生成 32 字节密钥:
head -c 32 /dev/urandom | base64 kubectl auth can-i get secret/db-cred -n cks-secrets --as=system:serviceaccount:cks-secrets:app- 常见错误 1:用引号包裹
defaultMode: 0400会使其变成字符串并被拒绝。请使用数字。 - 常见错误 2:在 EncryptionConfiguration 中把
identity放在第一位,会导致新写入的 Secret 以明文存储。 - 常见错误 3:即使启用配置,现有 Secret 在重新写入前仍为明文。需要使用
kubectl get secrets -A -o json | kubectl replace -f -全部刷新。
创建 Opaque Secret
创建命名空间 cks-secrets,并在其中创建 Opaque Secret db-cred。
包含两个键:username(值为 app)和 password(值为 pr0d-Db-Pass)。
使用 kubectl create secret generic --from-literal= 最快捷。不指定类型时默认为 Opaque。
Registry 凭据 Secret
在 cks-secrets 中创建类型为 kubernetes.io/dockerconfigjson 的 Secret registry-cred。
服务器为 registry.cks.local,用户名为 ci,密码为 ci-token。
类型固定的 Secret,其数据键名也固定。dockerconfigjson 类型只使用 .dockerconfigjson 这一个键。
TLS Secret
在 cks-secrets 中创建类型为 kubernetes.io/tls 的 Secret shop-tls。tls.crt 和
tls.key 均不得为空。
必须同时包含 tls.crt 和 tls.key 两个键,API 服务器才会接受。
以环境变量注入
在 cks-secrets 中创建 Pod env-app(容器名称 app,镜像 nginx:1.27-alpine)。
容器的 envFrom[0].secretRef.name 为 db-cred。
envFrom 的 secretRef 会将 Secret 的所有键展开为环境变量。虽然方便,但要记住这些值会原样留在进程环境中。
以卷挂载并收紧权限
在 cks-secrets 中创建 Pod vol-app(容器名称 app)。卷名称为 db,
secret.secretName 为 db-cred,secret.defaultMode 为 0400(十进制 256),
容器将该卷挂载到 /etc/db,并设置 readOnly: true。
defaultMode 在 YAML 中使用八进制编写,保存时会转为十进制。0400 表示仅所有者可读。
不可变 Secret
在 cks-secrets 中创建 Opaque Secret app-config。唯一的键为 mode(值为 strict),
immutable 为 true。
启用 immutable: true 后无法修改数据,只能删除并重新创建,同时也能降低 kubelet 的 watch 负载。
限制为只能读取特定 Secret
在 cks-secrets 中创建 ServiceAccount app 和 Role db-cred-reader。
apiGroups 为核心组,resources 为 secrets,resourceNames 仅为 db-cred,
verbs 仅为 get。通过 RoleBinding app-db-cred 绑定给 SA app。
然后按顺序将两个问题的答案(yes/no)分两行保存到
/root/cks-secrets/can-i.txt。第一行:SA app 能否 get secret/db-cred;
第二行:能否 get secret/app-config。
在 Role 中使用 resourceNames 固定名称后,只能读取该 Secret。使用 auth can-i 时以 TYPE/NAME 格式询问并确认结果。
静态加密配置文件
在 /root/cks-secrets/encryption-config.yaml 中编写静态加密配置。
apiVersion: apiserver.config.k8s.io/v1,kind: EncryptionConfiguration,
resources[0].resources 只有 secrets;providers 有两个,第一个为 aescbc
(键名 key1,secret 为 32 字节值的 base64 编码),最后一个为 identity。
Provider 列表的顺序具有意义:写入使用第一个 Provider,读取则按顺序尝试。错误放置 identity 会退回明文存储。