点 极速的未来即将来到贵所在的城市。

请加入 Redis 发布会。

如何真正保密 Kubernetes 秘钥

Kubernetes 秘钥通常用于共享 Kubernetes 集群中部署的应用程序所使用的秘钥。问题在于秘钥像其名称所示的并不安全。下面,我们将重点介绍 Kubernetes 秘钥带来的挑战,以及一种从 Kubernetes 集群中分离秘钥管理的策略。

Kubernetes 秘钥有多么保密?

本帖将通过一个配套代码库的帮助,描述如何通过 Vault 来创建安全的 Redis Enterprise on Kubernetes 部署,并重点说明了密钥管理和 Kubernetes 秘钥之间的差异。 

Kubernetes Secret 用于共享诸如密码、密钥、凭据和身份验证令牌之类的敏感数据。Secret 将密钥数据与应用程序代码分开,从而允许管理 Secret 而无需更改代码。 

安全性注意事项 

在 Kubernetes 关于 Secret 的文档中,它强调了以下警告: 

“默认情况下,Kubernetes Secret 以未加密的形式存储在 API 服务器的基础数据存储 (etcd) 中。任何具有 API 访问权限的人都可以检索或修改 Secret,具有 etcd 访问权限的人也可以。此外,任何有权在命名空间中创建 Pod 的人都可以使用该访问权限读取该命名空间中的任何 Secret;这包括间接访问,例如创建 Deployment 的能力”。 

考虑到这一点,以下几个因素可能会使密钥管理成为一项挑战: 

  • 密钥管理适用于应用程序的所有活动部分。假设一个无状态应用部署在 Kubernetes 集群中,而其他有状态应用部署在集群之外。在此场景中,如果使用 Kubernetes Secret 来管理密钥数据,则外部的有状态应用将被禁止访问。我们需要一个密钥管理平台,它可以跨越托管系统的全部环境。
  • 在平台独立性的类似方面,访问控制是通过 Kubernetes 角色进行的,即使能够解决不同应用程序位置的问题,这也会使访问密钥文件变得更加困难。
  • Kubernetes Secret 往往是一劳永逸的部署的;它留给 DevOps 团队处理更新、失效和密钥轮换。

DevOps 团队该怎么办?团队很可能会委托另一项服务,例如 HashiCorp Vault。

HashiCorp Vault 如何帮助解决密钥挑战

HashiCorp Vault 是一个与平台无关、基于身份的外部密钥操作符。与 Kubernetes Secret 不同,Vault 会在共享敏感数据之前授权所有访问操作。

HashiCorp Vault 功能  

Vault 密钥可从任何地方访问。这里没有假设应用程序已部署在何处;访问控制通过 Vault 进行管理,密钥在此处经过加密和存储,需要凭据和授权才能获得访问权限。 

Redis Enterprise、Kubernetes 和 Vault 保持一致 

无论 Redis Enterprise 安装为 软件、在 中完全托管,还是部署 在 Kubernetes 之上,用户都需要身份验证;端到端 安全 通常会启用以支持这些功能。对于 Kubernetes,Redis Enterprise 集群 的数据平面和控制平面都需要大量机密。

2022 年 9 月,HashiCorp 宣布 vault-k8s v1.0,这是数据领域一项激动人心的发展,因为 Kubernetes 上的 Redis Enterprise 现在使用 Vault Sidecar Agent Injector,它是一款 Kubernetes 突变 webhook 控制器。它还使用生成的 sidecar 模本来包含 Vault Agent 容器,授予 Redis Enterprise pod 访问 Vault 机密的权限。

在 Kubernetes 和 Redis Enterprise 的上下文中,Redis Enterprise Kubernetes Operator 公开的全部 Redis Enterprise 机密都可以通过 sidecar 委托给 Vault。 

以下是有关如何创建使用 Vault 强大功能在 Kubernetes 上安全部署 Redis Enterprise 的演练。以下是成功部署所需步骤的清单。 

  1. 准备 Vault Agent 以管理 Redis Enterprise 机密 
  1. 为 sidecar 注入准备 Redis Enterprise 运营商 
  1. 部署 Redis Enterprise Operator 
  1. 使用 Vault 管理的机密创建 Redis Enterprise 集群 
  1. 使用 Vault 管理的机密创建 Redis Enterprise 数据库 

开始之前  

开始的前提条件是具有足够资源用于 Vault 和 Redis Enterprise 的 Kubernetes 环境。对于 Redis Enterprise 而言,您至少需要三个节点,每个节点至少有六个核心和 25GB 内存。但是,对于沙箱目的,您可以使用四个核心和 10GB 内存。 

请确保您有足够容量来满足 Redis Enterprise 和 Vault 的需求。 不足可能会需要额外的节点。您还需要从 Kubernetes 获得正确的管理员权限来安装所有这些组件。 

请注意,所有后续步骤都在 配套的存储库 中完整记录。该存储库的第一个步骤 自述文件 是获取一个 shell 脚本以设置环境变量。从本质上讲,在两个系统之间协调配置时,这些环境变量会捕获通用元素。重要的是要强调使用单独的命名空间以及用于在 Vault 中存储机密文件的路径。

… 

# V_NAMESPACE is the Kubernetes namespace where the Vault service is running. 

# This is referenced by the Vault config. 

export V_NAMESPACE=hashicorp 

… 

# RE_NAMESPACE is the Kubernetes namespace where Redis is running 

export RE_NAMESPACE=redis 

… 

# V_SECRET_PREFIX is used the Vault policy name as well as a path component for 

# secrets stored in Vault. The RE_NAMESPACE is added to create a unique string 

# when there are multiple RE clusters created. Each RE operator and cluster must 

# be in its own namespace 

export V_SECRET_PREFIX=redisenterprise-${RE_NAMESPACE} 

…

随附仓库中,环境变量通过envsubst代替到模板中,以避免键入错误。 

创建 TLS 身份 

你可能早已知道 Kubernetes 为自己管理一组 TLS 身份集。不够清晰的是,同样的基础设施可以用于创建新 TLS 身份。使用opensslcfssl 来使用私钥和证书签名请求引导该流程仍然是必要的。  

首先,创建两个 TLS 身份以引导环境,一个用于 Vault,一个用于 Redis Enterprise 集群 (REC)。换句话说,TLS 用于保护与 Vault 的通信,即 REC 使用的准入控制器 API,REC 的数据平面,并且通过扩展,由 REC 托管的数据库。  

在所有情况下,模式如下:  

  • 使用 openssl 创建私钥
  • 构建证书签名请求 (CSR) 
  • 使用 openssl 用新创建的私钥签名 CSR
  • 将 CSR 提交到 Kubernetes 集群证书颁发机构。这保证签名 X.509 证书,该证书可追溯到已知 CA

Vault 的 TLS 身份示例 

创建私钥

openssl genrsa -out … 

创建 CSR

cat <<EOF >${TMPDIR}/csr-template.conf
[req] 
req_extensions = v3_req 
distinguished_name = req_distinguished_name 
… 
EOF

使用私钥签署 CSR

envsubst <./csr-template.conf >${TMPDIR}/csr.conf 

openssl req -new -key …

通过 Kubernetes 集群请求并批准证书

envsubst <./csr-resource.template >${TMPDIR}/csr-resource.yaml 
kubectl create -f ${TMPDIR}/csr-resource.yaml 
kubectl certificate approve ${V_CSR_NAME}

设置 Vault 

本帖讨论范围之外是设置 Vault 的完整指南。Vault 教程是值得探索的出色资源。出于本演练的目的,需要在 Kubernetes 集群中运行的 Vault 的简单实例(参见随附仓库的第一步)。要创建沙盒环境,有了一些来自 HashiCorp 的示例,就可以启用使用 TLS 来设置 Vault。

Vault 配置 

HashiCorp 体贴地提供了 Helm 图表以在 Kubernetes 集群中安装 Vault。对于本练习,在同一 Kubernetes 集群(虽说是在一个单独的 Kubernetes 名称空间中)安装 Vault,就像 Redis Enterprise 一样。 

当然,Vault 可以在一个单独的 Kubernetes 集群中,也可以部署在裸机 VM 上,或者可以通过HCP Vault完全在云中管理。针对此配置: 

启用 TLS 和注入器。

… 

global: 

  tlsDisable: false 

injector: 

  logLevel: debug 

  resources: 

    requests: 

      memory: 256Mi 

      cpu: 250m 

    limits: 

      memory: 256Mi 

      cpu: 250m 

…

确保有足够的资源用于 Vault。

… 

server: 

  loglevel: debug 

  resources: 

    requests: 

      memory: 256Mi 

      cpu: 250m 

    limits: 

      memory: 256Mi 

      cpu: 250m 

…

将 Kubernetes 环境链接到 Vault 配置。

standalone: 

    enabled: true 

    config: | 

      ui = true 

      listener "tcp" { 

        address = "[::]:8200" 

        cluster_address = "[::]:8201" 

        tls_cert_file = "/vault/userconfig/${V_SECRET_NAME}/${V_TLSCERT}" 

        tls_key_file  = "/vault/userconfig/${V_SECRET_NAME}/${V_TLSKEY}" 

        tls_client_ca_file = "/vault/userconfig/${V_SECRET_NAME}/${CA_CERT}" 

      } 

      storage "file" { 

        path = "/vault/data" 

      } 

      plugin_directory = "/usr/local/libexec/vault"

参阅随附仓库以获取完整配置文件。 

让 Vault 准备好面向 Redis Enterprise 

此时,Vault 已准备好管理 Redis Enterprise 秘钥,并且 Redis Enterprise 已知道要 Vault 中对其进行管理的位置。Redis Enterprise 仍需要访问这些秘钥的权限。 

具体来说,Redis Enterprise 不仅应该在已商定的路径上拥有对秘钥的完全 CRUD 访问权,而且还应该能够浏览这些密钥

… 

path "secret/data/${V_SECRET_PREFIX}/*" { 

 capabilities = ["create", "read", "update", "delete", "list"] 

} 

path "secret/metadata/${V_SECRET_PREFIX}/*" { 

 capabilities = ["list"] 

} 

…

Redis Enterprise 使用 Kubernetes 发行的服务账号。

… 

vault write auth/kubernetes/role/"redis-enterprise-operator-${RE_NAMESPACE}" \ 

       bound_service_account_names="redis-enterprise-operator"  \ 

       bound_service_account_namespaces="${RE_NAMESPACE}" \ 

       policies="${V_SECRET_PREFIX}" \ 

       ttl=24h 

…

Redis Enterprise 的最终步骤 

有关 Redis Enterprise Operator完整文档 提供其他值得查阅的信息。配套 repo 提供 Redis Enterprise 所需的其他配置,并且依赖于整篇文章中所做的假设。现在,简化的步骤是

安装 Redis Enterprise Operator

version=$(curl -s https://api.github.com/repos/RedisLabs/redis-enterprise- … 

kubectl apply -n ${RE_NAMESPACE} -f https://raw.githubusercontent.com/RedisLabs/redis-enterprise-k8s-docs/ …

创建 Redis Enterprise 集群

envsubst <./rec.template >${TMPDIR}/rec.yaml 

kubectl create -n ${RE_NAMESPACE} -f ${TMPDIR}/rec.yaml

将 REC 的 TLS 身份添加到 Vault

jq --null-input --rawfile certificate ${TMPDIR}/${PROXY_TLSCERT} … 

kubectl cp ${TMPDIR}/rec-identity.json vault-0:/tmp -n ${V_NAMESPACE} 

kubectl exec -n ${V_NAMESPACE} -it vault-0 -- vault kv put secret …

REC 部署完成后,可创建数据库

kubectl create -n ${RE_NAMESPACE} -f https://raw.githubusercontent.com/andresrinivasan/redis-enterprise-k8s- … 

连接

kubectl get redb -n ${RE_NAMESPACE} -o json | jq … 

redis-cli -h localhost -p ${REDB_PORT} --tls --cacert ${TMPDIR}/${CA_CERT} …

准备好在安全的地方保存秘钥 

虽然这一讨论有很多冲突,但这些信息通常会隐藏在真正的 DevOps 环境的自动化之中。此外,一般安全策略以及特定的 Kubernetes 策略通常会告知为保持敏感信息的安全而制定服务部署的不同策略。

Redis Enterprise Operator Kubernetes 是我们在百万次集群部署后总结的经验。阅读更多信息了解如何优化集群的总体性能。