加密静止状态下的机密数据
所有 Kubernetes 中允许您写入持久性 API 资源数据的 API 都支持静止加密。例如,您可以为 Secrets 启用静止加密。这种静止加密是 etcd 集群或运行 kube-apiserver 的主机上的文件系统(s)的任何系统级加密的补充。
此页面显示如何启用和配置 API 数据的静止加密。
注意
此任务涵盖使用 Kubernetes API 存储的资源数据的加密。例如,您可以加密 Secret 对象,包括它们包含的键值数据。
如果您想加密挂载到容器中的文件系统中的数据,您需要:
- 使用提供加密 volumes 的存储集成
- 在您自己的应用程序中加密数据
开始之前
您需要一个 Kubernetes 集群,并且 kubectl 命令行工具必须配置为与您的集群通信。建议在至少有两个节点的集群上运行本教程,这些节点不充当控制平面主机。如果您还没有集群,可以使用 minikube 创建一个,或者可以使用以下 Kubernetes 游乐场之一
此任务假设您在每个控制平面节点上将 Kubernetes API 服务器作为 static pod 运行。
您的集群的控制平面必须使用 etcd v3.x(主版本 3,任何次版本)。
要加密自定义资源,您的集群必须运行 Kubernetes v1.26 或更高版本。
要使用通配符匹配资源,您的集群必须运行 Kubernetes v1.27 或更高版本。
kubectl version
。确定是否已启用静止加密
默认情况下,API 服务器将资源的纯文本表示形式存储到 etcd 中,没有静止加密。
kube-apiserver
进程接受一个参数 --encryption-provider-config
,该参数指定配置文件的路径。该文件的内容(如果您指定了它)控制 Kubernetes API 数据在 etcd 中的加密方式。如果您在没有 --encryption-provider-config
命令行参数的情况下运行 kube-apiserver,则您没有启用静止加密。如果您在使用 --encryption-provider-config
命令行参数的情况下运行 kube-apiserver,并且它引用的文件将 identity
提供程序指定为列表中的第一个加密提供程序,那么您没有启用静止加密(默认的 identity
提供程序不提供任何机密性保护。)
如果您在使用 --encryption-provider-config
命令行参数的情况下运行 kube-apiserver,并且它引用的文件将 identity
以外的提供程序指定为列表中的第一个加密提供程序,那么您已经启用了静止加密。但是,该检查并不能告诉您以前的迁移到加密存储是否已成功。如果您不确定,请参阅 确保所有相关数据都已加密。
了解静止加密配置
---
#
# CAUTION: this is an example configuration.
# Do not use this for your own cluster!
#
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
- resources:
- secrets
- configmaps
- pandas.awesome.bears.example # a custom resource API
providers:
# This configuration does not provide data confidentiality. The first
# configured provider is specifying the "identity" mechanism, which
# stores resources as plain text.
#
- identity: {} # plain text, in other words NO encryption
- aesgcm:
keys:
- name: key1
secret: c2VjcmV0IGlzIHNlY3VyZQ==
- name: key2
secret: dGhpcyBpcyBwYXNzd29yZA==
- aescbc:
keys:
- name: key1
secret: c2VjcmV0IGlzIHNlY3VyZQ==
- name: key2
secret: dGhpcyBpcyBwYXNzd29yZA==
- secretbox:
keys:
- name: key1
secret: YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoxMjM0NTY=
- resources:
- events
providers:
- identity: {} # do not encrypt Events even though *.* is specified below
- resources:
- '*.apps' # wildcard match requires Kubernetes 1.27 or later
providers:
- aescbc:
keys:
- name: key2
secret: c2VjcmV0IGlzIHNlY3VyZSwgb3IgaXMgaXQ/Cg==
- resources:
- '*.*' # wildcard match requires Kubernetes 1.27 or later
providers:
- aescbc:
keys:
- name: key3
secret: c2VjcmV0IGlzIHNlY3VyZSwgSSB0aGluaw==
每个 resources
数组项都是一个单独的配置,包含完整的配置。resources.resources
字段是一个 Kubernetes 资源名称(resource
或 resource.group
)数组,这些名称应该像 Secrets、ConfigMaps 或其他资源一样被加密。
如果将自定义资源添加到 EncryptionConfiguration
并且集群版本为 1.26 或更高版本,则 EncryptionConfiguration
中提到的任何新创建的自定义资源都将被加密。在该版本和配置之前存在于 etcd 中的任何自定义资源都将保持未加密状态,直到它们下次写入存储为止。这与内置资源的行为相同。请参阅 确保所有 Secrets 都已加密 部分。
providers
数组是您列出的 API 使用的可能的加密提供程序的有序列表。每个提供程序都支持多个密钥 - 密钥按顺序尝试解密,如果提供程序是第一个提供程序,则第一个密钥用于加密。
每个条目只能指定一种提供程序类型(可以提供 identity
或 aescbc
,但不能在同一项中同时提供两者)。列表中的第一个提供程序用于将写入存储的资源加密。从存储中读取资源时,每个与存储数据匹配的提供程序按顺序尝试解密数据。如果由于格式或密钥不匹配,没有提供程序可以读取存储的数据,则会返回错误,这会阻止客户端访问该资源。
EncryptionConfiguration
支持使用通配符来指定应加密的资源。使用 '*.<group>
' 加密组内的所有资源(例如上述示例中的 '*.apps
')或 '*.*
' 加密所有资源。'*.
' 可用于加密核心组中的所有资源。'*.*
' 将加密所有资源,即使是 API 服务器启动后添加的自定义资源。
注意
不允许在同一个资源列表内或跨多个条目使用重叠的通配符,因为配置的一部分将无效。resources
列表的处理顺序和优先级由其在配置中的列出顺序决定。如果您有覆盖资源的通配符,并且想要选择不为特定类型的资源进行静止加密,可以通过添加一个单独的 resources
数组项,其中包含您要豁免的资源的名称,然后添加一个 providers
数组项,其中指定了 identity
提供程序来实现这一点。您将此项添加到列表中,以便它出现在您指定加密(不是 identity
的提供程序)的配置之前。
例如,如果启用了 '*.*
' 并且您想要选择不为 Events 和 ConfigMaps 加密,请在 resources
中添加一个新的更早的项,然后添加一个使用 identity
作为提供程序的 providers 数组项。更具体的条目必须出现在通配符条目之前。
新条目将类似于
...
- resources:
- configmaps. # specifically from the core API group,
# because of trailing "."
- events
providers:
- identity: {}
# and then other entries in resources
确保豁免在 resources 数组中列在通配符 '*.*
' 项之前,以使其优先级更高。
有关 EncryptionConfiguration
结构的更多详细信息,请参阅 加密配置 API。
警告
如果任何资源无法通过加密配置读取(因为密钥已更改),并且您无法恢复工作配置,您唯一的选择是直接从底层的 etcd 中删除该条目。
尝试读取该资源的任何 Kubernetes API 调用都将失败,直到它被删除或提供有效的解密密钥为止。
可用提供程序
在为集群的 Kubernetes API 中的数据配置静止加密之前,您需要选择将使用的提供程序(s)。
下表描述了每个可用的提供程序。
名称 | 加密 | 强度 | 速度 | 密钥长度 |
---|---|---|---|---|
identity | 无 | N/A | N/A | N/A |
资源按原样写入,不进行加密。当设置为第一个提供程序时,资源将在写入新值时被解密。现有的加密资源不会自动覆盖为纯文本数据。该identity提供程序是默认的,如果您没有指定其他提供程序。 | ||||
aescbc | 使用 PKCS#7 填充的 AES-CBC | 弱 | 快 | 32 字节 |
不推荐使用,因为 CBC 易受填充预言攻击。密钥材料可从控制平面主机访问。 | ||||
aesgcm | 使用随机 nonce 的 AES-GCM | 必须每 200,000 次写入旋转一次 | 最快 | 16、24 或 32 字节 |
除非实施自动密钥旋转方案,否则不建议使用。密钥材料可从控制平面主机访问。 | ||||
kmsv1 (自 Kubernetes v1.28 起已弃用) | 使用每个资源的 DEK 的信封加密方案。 | 最强 | 慢(与 kms 版本 2 相比) | 32 字节 |
数据使用 AES-GCM 通过数据加密密钥 (DEK) 加密;DEK 根据密钥管理服务 (KMS) 中的配置通过密钥加密密钥 (KEK) 加密。简单的密钥旋转,每次加密都会生成一个新的 DEK,KEK 旋转由用户控制。 阅读有关如何 配置 KMS V1 提供程序 的信息。 | ||||
kmsv2 | 使用每个 API 服务器的 DEK 的信封加密方案。 | 最强 | 快 | 32 字节 |
数据使用 AES-GCM 通过数据加密密钥 (DEK) 加密;DEK 根据密钥管理服务 (KMS) 中的配置通过密钥加密密钥 (KEK) 加密。Kubernetes 从密钥种子生成每个加密的 DEK。每次 KEK 旋转时,种子也会旋转。 如果使用第三方工具进行密钥管理,这是一个不错的选择。从 Kubernetes v1.29 开始可用为稳定版本。 阅读有关如何 配置 KMS V2 提供程序 的信息。 | ||||
secretbox | XSalsa20 和 Poly1305 | 强 | 更快 | 32 字节 |
使用相对较新的加密技术,这些技术在需要高度审查的环境中可能不被认为是可以接受的。密钥材料可从控制平面主机访问。 |
如果您没有指定其他提供程序,则 identity
提供程序是默认的。identity
提供程序不会加密存储的数据,并且不提供额外的机密性保护。
密钥存储
本地密钥存储
使用本地管理的密钥加密机密数据可以防止 etcd 被入侵,但无法防止主机被入侵。由于加密密钥存储在主机上的 EncryptionConfiguration YAML 文件中,因此熟练的攻击者可以访问该文件并提取加密密钥。
托管 (KMS) 密钥存储
KMS 提供商使用信封加密:Kubernetes 使用数据密钥加密资源,然后使用托管加密服务加密该数据密钥。Kubernetes 为每个资源生成一个唯一的数据密钥。API 服务器将数据密钥的加密版本存储在 etcd 中,与密文一起;读取资源时,API 服务器会调用托管加密服务并提供密文和(加密的)数据密钥。在托管加密服务中,提供商使用密钥加密密钥来解密数据密钥,解密数据密钥,最后恢复明文。控制平面和 KMS 之间的通信需要在传输过程中进行保护,例如 TLS。
使用信封加密会依赖于密钥加密密钥,该密钥不会存储在 Kubernetes 中。在 KMS 的情况下,想要获得对明文值的未经授权访问的攻击者需要入侵 etcd **和** 第三方 KMS 提供商。
加密密钥的保护
您应该采取适当措施来保护允许解密的机密信息,无论是本地加密密钥,还是允许 API 服务器调用 KMS 的身份验证令牌。
即使您依赖于提供商来管理主加密密钥(或密钥)的使用和生命周期,您仍然有责任确保托管加密服务的访问控制和其他安全措施适合您的安全需求。
加密您的数据
生成加密密钥
以下步骤假设您没有使用 KMS,因此这些步骤还假设您需要生成加密密钥。如果您已经拥有加密密钥,请跳到 编写加密配置文件。
警告
与没有加密相比,在 EncryptionConfig 中存储原始加密密钥仅能适度提高您的安全态势。
为了额外的机密性,请考虑使用 kms
提供商,因为它依赖于存储在 Kubernetes 集群之外的密钥。kms
的实现可以与硬件安全模块或由您的云提供商管理的加密服务一起使用。
要了解有关使用 KMS 设置静态数据加密的信息,请参阅 使用 KMS 提供商进行数据加密。您使用的 KMS 提供商插件也可能附带其他特定文档。
首先生成一个新的加密密钥,然后使用 base64 对其进行编码
生成一个 32 字节的随机密钥并对其进行 base64 编码。您可以使用此命令
head -c 32 /dev/urandom | base64
如果您想使用 PC 的内置硬件熵源,可以使用 /dev/hwrng
代替 /dev/urandom
。并非所有 Linux 设备都提供硬件随机数生成器。
生成一个 32 字节的随机密钥并对其进行 base64 编码。您可以使用此命令
head -c 32 /dev/urandom | base64
生成一个 32 字节的随机密钥并对其进行 base64 编码。您可以使用此命令
# Do not run this in a session where you have set a random number
# generator seed.
[Convert]::ToBase64String((1..32|%{[byte](Get-Random -Max 256)}))
注意
请保持加密密钥的机密性,包括在您生成它时,以及理想情况下,即使在您不再积极使用它之后也是如此。复制加密密钥
使用安全的文件传输机制,将该加密密钥的副本提供给所有其他控制平面主机。
至少,请使用传输中的加密 - 例如,安全外壳 (SSH)。为了更高的安全性,请在主机之间使用非对称加密,或者更改您正在使用的方法,以便您依赖于 KMS 加密。
编写加密配置文件
警告
加密配置文件可能包含可以解密 etcd 中内容的密钥。如果配置文件包含任何密钥材料,您必须适当地限制所有控制平面主机上的权限,以便只有运行 kube-apiserver 的用户才能读取此配置。创建一个新的加密配置文件。其内容应类似于
---
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
- resources:
- secrets
- configmaps
- pandas.awesome.bears.example
providers:
- aescbc:
keys:
- name: key1
# See the following text for more details about the secret value
secret: <BASE 64 ENCODED SECRET>
- identity: {} # this fallback allows reading unencrypted secrets;
# for example, during initial migration
要创建新的加密密钥(不使用 KMS),请参阅 生成加密密钥。
使用新的加密配置文件
您需要将新的加密配置文件挂载到 kube-apiserver
静态 pod。以下是如何执行此操作的示例
将新的加密配置文件保存到控制平面节点上的
/etc/kubernetes/enc/enc.yaml
。编辑
kube-apiserver
静态 pod 的清单:/etc/kubernetes/manifests/kube-apiserver.yaml
,使其类似于--- # # This is a fragment of a manifest for a static Pod. # Check whether this is correct for your cluster and for your API server. # apiVersion: v1 kind: Pod metadata: annotations: kubeadm.kubernetes.io/kube-apiserver.advertise-address.endpoint: 10.20.30.40:443 creationTimestamp: null labels: app.kubernetes.io/component: kube-apiserver tier: control-plane name: kube-apiserver namespace: kube-system spec: containers: - command: - kube-apiserver ... - --encryption-provider-config=/etc/kubernetes/enc/enc.yaml # add this line volumeMounts: ... - name: enc # add this line mountPath: /etc/kubernetes/enc # add this line readOnly: true # add this line ... volumes: ... - name: enc # add this line hostPath: # add this line path: /etc/kubernetes/enc # add this line type: DirectoryOrCreate # add this line ...
重新启动 API 服务器。
警告
您的配置文件包含可以解密 etcd 中内容的密钥,因此您必须适当地限制控制平面节点上的权限,以便只有运行kube-apiserver
的用户才能读取它。您现在已为 **一个** 控制平面主机设置了加密。典型的 Kubernetes 集群有多个控制平面主机,因此还有更多工作要做。
重新配置其他控制平面主机
如果您在集群中有多个 API 服务器,则应依次将更改部署到每个 API 服务器。
警告
对于具有两个或更多控制平面节点的集群配置,加密配置应在每个控制平面节点上保持一致。
如果控制平面节点之间的加密提供商配置存在差异,则此差异可能意味着 kube-apiserver 无法解密数据。
当您计划更新集群的加密配置时,请计划此操作,以便控制平面中的 API 服务器始终能够解密存储的数据(即使是在推出更改的过程中)。
确保您在每个控制平面主机上使用 **相同** 的加密配置。
验证新写入的数据是否已加密
数据写入 etcd 时会加密。重新启动 kube-apiserver
后,任何新创建或更新的 Secret(或 EncryptionConfiguration
中配置的其他资源类型)在存储时都应加密。
要检查这一点,您可以使用 etcdctl
命令行程序检索 Secret 数据的内容。
此示例展示了如何检查对 Secret API 进行加密。
在
default
命名空间中创建一个名为secret1
的新 Secretkubectl create secret generic secret1 -n default --from-literal=mykey=mydata
使用
etcdctl
命令行工具,从 etcd 中读取该 SecretETCDCTL_API=3 etcdctl get /registry/secrets/default/secret1 [...] | hexdump -C
其中
[...]
必须是连接到 etcd 服务器的附加参数。例如
ETCDCTL_API=3 etcdctl \ --cacert=/etc/kubernetes/pki/etcd/ca.crt \ --cert=/etc/kubernetes/pki/etcd/server.crt \ --key=/etc/kubernetes/pki/etcd/server.key \ get /registry/secrets/default/secret1 | hexdump -C
输出类似于此(已省略):
00000000 2f 72 65 67 69 73 74 72 79 2f 73 65 63 72 65 74 |/registry/secret| 00000010 73 2f 64 65 66 61 75 6c 74 2f 73 65 63 72 65 74 |s/default/secret| 00000020 31 0a 6b 38 73 3a 65 6e 63 3a 61 65 73 63 62 63 |1.k8s:enc:aescbc| 00000030 3a 76 31 3a 6b 65 79 31 3a c7 6c e7 d3 09 bc 06 |:v1:key1:.l.....| 00000040 25 51 91 e4 e0 6c e5 b1 4d 7a 8b 3d b9 c2 7c 6e |%Q...l..Mz.=..|n| 00000050 b4 79 df 05 28 ae 0d 8e 5f 35 13 2c c0 18 99 3e |.y..(..._5.,...>| [...] 00000110 23 3a 0d fc 28 ca 48 2d 6b 2d 46 cc 72 0b 70 4c |#:..(.H-k-F.r.pL| 00000120 a5 fc 35 43 12 4e 60 ef bf 6f fe cf df 0b ad 1f |..5C.N`..o......| 00000130 82 c4 88 53 02 da 3e 66 ff 0a |...S..>f..| 0000013a
验证存储的 Secret 是否以
k8s:enc:aescbc:v1:
为前缀,这表明aescbc
提供商已对结果数据进行了加密。确认etcd
中显示的密钥名称与上面提到的EncryptionConfiguration
中指定的密钥名称匹配。在此示例中,您可以看到名为key1
的加密密钥在etcd
和EncryptionConfiguration
中使用。验证 Secret 在通过 API 检索时是否已正确解密
kubectl get secret secret1 -n default -o yaml
输出应包含
mykey: bXlkYXRh
,其内容为使用 base64 编码的mydata
;请阅读 解码 Secret,了解如何完全解码 Secret。
确保所有相关数据都已加密
确保新对象被加密通常还不够:您还希望加密应用于已经存储的对象。
对于此示例,您已配置集群,以便 Secret 在写入时加密。对每个 Secret 执行替换操作会加密静态数据中的内容,其中对象保持不变。
您可以对集群中的所有 Secret 进行此更改
# Run this as an administrator that can read and write all Secrets
kubectl get secrets --all-namespaces -o json | kubectl replace -f -
上面的命令读取所有 Secret,然后使用相同的数据更新它们,以便应用服务器端加密。
注意
如果由于冲突写入而发生错误,请重试该命令。多次运行该命令是安全的。
对于较大的集群,您可能希望按命名空间细分 Secret,或编写更新脚本。
防止明文检索
如果您想确保对特定 API 类型的访问只能通过加密完成,则可以删除 API 服务器以明文读取该 API 的后端数据的权限。
警告
进行此更改会阻止 API 服务器检索标记为静态加密,但实际上以明文形式存储的资源。
当您为 API 配置静态加密时(例如:API 类型 Secret
,代表核心 API 组中的 secrets
资源),您 **必须** 确保集群中所有这些资源实际上都是静态加密的。在继续执行下一步之前,请检查这一点。
一旦集群中的所有 Secret 都已加密,您可以删除加密配置的 identity
部分。例如
---
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
- resources:
- secrets
providers:
- aescbc:
keys:
- name: key1
secret: <BASE 64 ENCODED SECRET>
- identity: {} # REMOVE THIS LINE
…然后依次重新启动每个 API 服务器。此更改会阻止 API 服务器访问明文 Secret,即使是意外访问也不行。
旋转解密密钥
更改 Kubernetes 的加密密钥而不会导致停机需要一个多步骤操作,尤其是在存在高度可用的部署的情况下,在这种情况下,多个 kube-apiserver
进程正在运行。
- 生成一个新密钥,并将其作为当前提供商的所有控制平面节点上的第二个密钥条目添加。
- 重新启动 **所有**
kube-apiserver
进程,以确保每个服务器都能解密使用新密钥加密的任何数据。 - 对新的加密密钥进行安全备份。如果您丢失了该密钥的所有副本,则需要删除使用丢失的密钥加密的所有资源,并且在静态加密中断期间,工作负载可能无法按预期运行。
- 将新密钥设为
keys
数组中的第一个条目,以便将其用于对新写入进行静态加密 - 重新启动所有
kube-apiserver
进程,以确保每个控制平面主机现在都使用新密钥进行加密 - 以特权用户身份运行
kubectl get secrets --all-namespaces -o json | kubectl replace -f -
,以使用新密钥加密所有现有 Secret - 在您已将所有现有 Secret 更新为使用新密钥并已对新密钥进行安全备份之后,请从配置中删除旧的解密密钥。
解密所有数据
此示例展示了如何停止对 Secret API 进行静态加密。如果您正在加密其他 API 类型,请调整步骤以进行匹配。
要禁用静态加密,请将 identity
提供商作为第一个条目放置在加密配置文件中
---
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
- resources:
- secrets
# list any other resources here that you previously were
# encrypting at rest
providers:
- identity: {} # add this line
- aescbc:
keys:
- name: key1
secret: <BASE 64 ENCODED SECRET> # keep this in place
# make sure it comes after "identity"
然后运行以下命令强制解密所有 Secret
kubectl get secrets --all-namespaces -o json | kubectl replace -f -
一旦您用不使用加密的后端数据替换了所有现有加密资源,就可以从 kube-apiserver
中删除加密设置。
配置自动重新加载
您可以配置加密提供商配置的自动重新加载。该设置决定 API 服务器 应该只在启动时加载为 --encryption-provider-config
指定的文件一次,还是在您更改该文件时自动加载。启用此选项允许您更改静态加密的密钥,而无需重新启动 API 服务器。
要允许自动重新加载,请将 API 服务器配置为使用 --encryption-provider-config-automatic-reload=true
运行。启用后,会每分钟轮询一次文件更改,以观察修改。apiserver_encryption_config_controller_automatic_reload_last_timestamp_seconds
指标标识了新配置生效的时间。这允许在不重新启动 API 服务器的情况下旋转加密密钥。
下一步
- 阅读关于解密已存储在静止状态的数据
- 详细了解加密配置配置 API (v1)。