LabHub
学习 学习路径 课程

CKS — Kubernetes 安全专家

Secret 的处理与静态加密

在 LabHub 中继续学习

目标

按类型创建 Secret,确认不同注入方式会产生怎样的暴露路径,使用 RBAC 收窄读取范围,并亲自编写静态加密配置文件。

为什么重要

Secret 清单中的值采用 base64,看起来仿佛经过加密,但 base64 只是编码,不是加密。它没有密钥,只需一条命令即可还原。在默认配置下,Secret 以明文保存在 etcd 中,因此任何取得 etcd 备份文件的人都能读取所有 Secret。只有启用 EncryptionConfiguration 后,存储的值才会真正加密;此时 identity Provider 必须位于列表最后。如果它在前面,就会退回明文存储。

注入方式也很重要。环境变量可通过 /proc/PID/environ 读取,会被子进程继承,还可能完整出现在崩溃报告或调试页面中。卷挂载更加安全,通常还会使用 defaultMode 收紧文件权限。

最后是 RBAC。在某个命名空间拥有 get secrets 权限的人,可以读取该命名空间中的所有 Secret。可以通过 resourceNames 只允许访问特定 Secret,这一点出乎意料地鲜为人知。

步骤

  1. 创建命名空间 cks-secrets,并在其中创建 Opaque Secret db-cred。 包含两个键:username(值为 app)和 password(值为 pr0d-Db-Pass)。
  2. cks-secrets 中创建类型为 kubernetes.io/dockerconfigjson 的 Secret registry-cred。 服务器为 registry.cks.local,用户名为 ci,密码为 ci-token
  3. cks-secrets 中创建类型为 kubernetes.io/tls 的 Secret shop-tlstls.crttls.key 均不得为空。
  4. cks-secrets 中创建 Pod env-app(容器名称 app,镜像 nginx:1.27-alpine)。 容器的 envFrom[0].secretRef.namedb-cred
  5. cks-secrets 中创建 Pod vol-app(容器名称 app)。卷名称为 dbsecret.secretNamedb-credsecret.defaultMode0400(十进制 256), 容器将该卷挂载到 /etc/db,并设置 readOnly: true
  6. cks-secrets 中创建 Opaque Secret app-config。唯一的键为 mode(值为 strict), immutabletrue
  7. cks-secrets 中创建 ServiceAccount app 和 Role db-cred-reader。 apiGroups 为核心组,resources 为 secretsresourceNames 仅为 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
  8. /root/cks-secrets/encryption-config.yaml 中编写静态加密配置。 apiVersion: apiserver.config.k8s.io/v1kind: EncryptionConfigurationresources[0].resources 只有 secretsproviders 有两个,第一个为 aescbc (键名 key1,secret 为 32 字节值的 base64 编码),最后一个为 identity

参考

创建 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-tlstls.crttls.key 均不得为空。

必须同时包含 tls.crttls.key 两个键,API 服务器才会接受。

以环境变量注入

cks-secrets 中创建 Pod env-app(容器名称 app,镜像 nginx:1.27-alpine)。 容器的 envFrom[0].secretRef.namedb-cred

envFromsecretRef 会将 Secret 的所有键展开为环境变量。虽然方便,但要记住这些值会原样留在进程环境中。

以卷挂载并收紧权限

cks-secrets 中创建 Pod vol-app(容器名称 app)。卷名称为 dbsecret.secretNamedb-credsecret.defaultMode0400(十进制 256), 容器将该卷挂载到 /etc/db,并设置 readOnly: true

defaultMode 在 YAML 中使用八进制编写,保存时会转为十进制。0400 表示仅所有者可读。

不可变 Secret

cks-secrets 中创建 Opaque Secret app-config。唯一的键为 mode(值为 strict), immutabletrue

启用 immutable: true 后无法修改数据,只能删除并重新创建,同时也能降低 kubelet 的 watch 负载。

限制为只能读取特定 Secret

cks-secrets 中创建 ServiceAccount app 和 Role db-cred-reader。 apiGroups 为核心组,resources 为 secretsresourceNames 仅为 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/v1kind: EncryptionConfigurationresources[0].resources 只有 secretsproviders 有两个,第一个为 aescbc (键名 key1,secret 为 32 字节值的 base64 编码),最后一个为 identity

Provider 列表的顺序具有意义:写入使用第一个 Provider,读取则按顺序尝试。错误放置 identity 会退回明文存储。