--- title: 使用 KMS 驱动进行数据加密 content_type: task --- 本页展示了如何配置秘钥管理服务—— Key Management Service (KMS) 驱动和插件以启用 Secret 数据加密。 ## {{% heading "prerequisites" %}} * {{< include "task-tutorial-prereqs.md" >}} {{< version-check >}} * 需要 Kubernetes 1.10.0 或更新版本 * 需要 etcd v3 或更新版本 {{< feature-state for_k8s_version="v1.12" state="beta" >}} KMS 加密驱动使用封套加密模型来加密 etcd 中的数据。 数据使用数据加密秘钥(DEK)加密;每次加密都生成一个新的 DEK。 这些 DEK 经一个秘钥加密秘钥(KEK)加密后在一个远端的 KMS 中存储和管理。 KMS 驱动使用 gRPC 与一个特定的 KMS 插件通信。这个 KMS 插件作为一个 gRPC 服务器被部署在 Kubernetes 主服务器的同一个主机上,负责与远端 KMS 的通信。 ## 配置 KMS 驱动 为了在 API 服务器上配置 KMS 驱动,在加密配置文件中的驱动数组中加入一个类型为 `kms` 的驱动,并设置下列属性: * `name`: KMS 插件的显示名称。 * `endpoint`: gRPC 服务器(KMS 插件)的监听地址。该端点是一个 UNIX 域套接字。 * `cachesize`: 以明文缓存的数据加密秘钥(DEKs)的数量。一旦被缓存, 就可以直接使用 DEKs 而无需另外调用 KMS;而未被缓存的 DEKs 需要调用一次 KMS 才能解包。 * `timeout`: 在返回一个错误之前,kube-apiserver 等待 kms-plugin 响应的时间(默认是 3 秒)。 参见[理解静态数据加密配置](/zh/docs/tasks/administer-cluster/encrypt-data) ## 实现 KMS 插件 为实现一个 KMS 插件,你可以开发一个新的插件 gRPC 服务器或启用一个由你的云服务驱动提供的 KMS 插件。 你可以将这个插件与远程 KMS 集成,并把它部署到 Kubernetes 的主服务器上。 ### 启用由云服务驱动支持的 KMS 有关启用云服务驱动特定的 KMS 插件的说明,请咨询你的云服务驱动商。 ### 开发 KMS 插件 gRPC 服务器 你可以使用 Go 语言的存根文件开发 KMS 插件 gRPC 服务器。 对于其他语言,你可以用 proto 文件创建可以用于开发 gRPC 服务器代码的存根文件。 * 使用 Go:使用存根文件 [service.pb.go](https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/apiserver/pkg/storage/value/encrypt/envelope/v1beta1/service.pb.go) 中的函数和数据结构开发 gRPC 服务器代码。 * 使用 Go 以外的其他语言:用 protoc 编译器编译 proto 文件: [service.proto](https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/apiserver/pkg/storage/value/encrypt/envelope/v1beta1/service.proto) 为指定语言生成存根文件。 然后使用存根文件中的函数和数据结构开发服务器代码。 **注意:** * kms 插件版本:`v1beta1` 作为对过程调用 Version 的响应,兼容的 KMS 插件应把 v1beta1 作为 VersionResponse.version 返回 * 消息版本:`v1beta1` 所有来自 KMS 驱动的消息都把 version 字段设置为当前版本 v1beta1 * 协议:UNIX 域套接字 (`unix`) gRPC 服务器应监听 UNIX 域套接字 ### 将 KMS 插件与远程 KMS 整合 KMS 插件可以用任何受 KMS 支持的协议与远程 KMS 通信。 所有的配置数据,包括 KMS 插件用于与远程 KMS 通信的认证凭据,都由 KMS 插件独立地存储和管理。 KMS 插件可以用额外的元数据对密文进行编码,这些元数据是在把它发往 KMS 进行解密之前可能要用到的。 ### 部署 KMS 插件 确保 KMS 插件与 Kubernetes 主服务器运行在同一主机上。 ## 使用 KMS 驱动加密数据 为了加密数据: 1. 使用 `kms` 驱动的相应的属性创建一个新的加密配置文件: ```yaml kind: EncryptionConfiguration apiVersion: apiserver.config.k8s.io/v1 resources: - resources: - secrets providers: - kms: name: myKmsPlugin endpoint: unix:///tmp/socketfile.sock cachesize: 100 timeout: 3s - identity: {} ``` 2. 设置 kube-apiserver 的 `--encryption-provider-config` 参数指向配置文件的位置。 3. 重启 API 服务器。 ## 验证数据已经加密 写入 etcd 时数据被加密。重启 kube-apiserver 后,任何新建或更新的 Secret 在存储时应该已被加密。 要验证这点,你可以用 etcdctl 命令行程序获取 Secret 内容。 1. 在默认的命名空间里创建一个名为 secret1 的 Secret: ``` kubectl create secret generic secret1 -n default --from-literal=mykey=mydata ``` 2. 用 etcdctl 命令行,从 etcd 读取出 Secret: ``` ETCDCTL_API=3 etcdctl get /kubernetes.io/secrets/default/secret1 [...] | hexdump -C ``` 其中 `[...]` 是用于连接 etcd 服务器的额外参数。 3. 验证保存的 Secret 是否是以 `k8s:enc:kms:v1:` 开头的,这表明 `kms` 驱动已经对结果数据加密。 4. 验证 Secret 在被 API 获取时已被正确解密: ``` kubectl describe secret secret1 -n default ``` 结果应该是 `mykey: mydata`。 ## 确保所有 Secret 都已被加密 因为 Secret 是在写入时被加密的,所以在更新 Secret 时也会加密该内容。 下列命令读取所有 Secret 并更新它们以便应用服务器端加密。如果因为写入冲突导致错误发生, 请重试此命令。对较大的集群,你可能希望根据命名空间或脚本更新去细分 Secret 内容。 ``` kubectl get secrets --all-namespaces -o json | kubectl replace -f - ``` ## 从本地加密驱动切换到 KMS 驱动 为了从本地加密驱动切换到 `kms` 驱动并重新加密所有 Secret 内容: 1. 在配置文件中加入 `kms` 驱动作为第一个条目,如下列样例所示 ```yaml kind: EncryptionConfiguration apiVersion: apiserver.config.k8s.io/v1 resources: - resources: - secrets providers: - kms: name : myKmsPlugin endpoint: unix:///tmp/socketfile.sock cachesize: 100 - aescbc: keys: - name: key1 secret: ``` 2. 重启所有 kube-apiserver 进程。 3. 运行下列命令使用 `kms` 驱动强制重新加密所有 Secret。 ``` kubectl get secrets --all-namespaces -o json| kubectl replace -f - ``` ## 禁用静态数据加密 要禁用静态数据加密: 1. 将 `identity` 驱动作为配置文件中的第一个条目: ```yaml kind: EncryptionConfiguration apiVersion: apiserver.config.k8s.io/v1 resources: - resources: - secrets providers: - identity: {} - kms: name : myKmsPlugin endpoint: unix:///tmp/socketfile.sock cachesize: 100 ``` 2. 重启所有 kube-apiserver 进程。 3. 运行下列命令强制重新加密所有 Secret。 ``` kubectl get secrets --all-namespaces -o json | kubectl replace -f - ```