Kubernetes Secrets 通常用于共享部署在 Kubernetes 集群中的应用程序所使用的秘密数据。 但需要注意的是,Secrets 并不像其名称所暗示的那样安全。下文,我们将重点介绍 Kubernetes Secrets 面临的挑战以及将秘密管理与 Kubernetes 集群解耦的策略。
本文将借助配套代码仓库,描述如何在 Kubernetes 上创建 Redis Enterprise 的安全部署,该部署利用了 Vault 的强大功能,并强调了秘密管理与 Kubernetes Secrets 之间的区别。
一个 Kubernetes Secret 用于共享敏感数据,例如密码、密钥、凭证和身份验证令牌。Secrets 将秘密数据与应用程序代码分离,从而无需更改代码即可管理秘密。
在 Kubernetes 关于 Secrets 的官方文档中,强调了以下警告:
“默认情况下,Kubernetes Secrets 以未加密的形式存储在 API 服务器底层数据存储(etcd)中。任何拥有 API 访问权限的人都可以检索或修改 Secret,任何有权访问 etcd 的人也可以。此外,任何被授权在命名空间中创建 Pod 的人都可以利用该权限读取该命名空间中的任何 Secret;这包括间接访问,例如创建 Deployment 的能力。”
考虑到这一点,以下是一些可能使秘密管理成为挑战的因素:
DevOps 团队应该怎么做?最可能的情况是,团队会将这项任务委托给另一个服务,例如 HashiCorp Vault。
HashiCorp Vault 是一个平台无关、基于身份的外部秘密操作符。与 Kubernetes Secrets 不同,Vault 在共享敏感数据之前会授权所有访问。
Vault 秘密数据可以在任何地方访问。它不假设应用程序部署在哪里;访问控制通过 Vault 进行管理,Secrets 在 Vault 中被加密和存储,并且需要凭证和授权才能访问。
无论 Redis Enterprise 是作为软件安装、在云端完全托管,还是部署在 Kubernetes 上,用户都需要身份验证;通常会启用端到端安全性来支持这些功能。在 Kubernetes 的情况下,Redis Enterprise 集群的数据平面和控制平面都需要多个 Secrets。
2022 年 9 月,HashiCorp 宣布了 vault-k8s v1.0,这是数据领域的一项令人兴奋的进展,因为 Kubernetes 上的 Redis Enterprise 现在使用了 Vault Sidecar Agent Injector,一个 Kubernetes 变动性 Webhook 控制器。它还利用由此产生的 Sidecar 模式包含一个 Vault Agent 容器,授予 Redis Enterprise Pod 访问 Vault Secrets 的权限。
在 Kubernetes 和 Redis Enterprise 的背景下,Redis Enterprise Kubernetes Operator 公开的所有 Redis Enterprise 秘密数据都可以通过 Sidecar 委托给 Vault。
下面是关于如何在 Kubernetes 上创建使用 Vault 功能的安全 Redis Enterprise 部署的演练。以下是成功部署所需的步骤清单:
开始的前提条件是一个 Kubernetes 环境,其中有足够的资源用于 Vault 和 Redis Enterprise。对于 Redis Enterprise,您至少需要三个节点,每个节点至少有六个核心和 25GB 内存。但是,对于沙箱环境,四个核心和 10GB 内存也可以。
确保您有足够的容量来安装 Redis Enterprise 和 Vault。 容量不足可能需要增加一个节点。您还需要 Kubernetes 的正确管理员权限才能安装所有这些组件。
请注意,所有以下步骤都在配套代码仓库中完整记录。代码仓库 README 中的第一步是导入一个 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 替换到模板中,以避免输入错误。
您可能已经知道 Kubernetes 正在管理自己的一组 TLS 身份。不太清楚的是,可以使用相同的基础设施来创建新的 TLS 身份。仍然需要使用 openssl 或 cfssl 通过私钥和证书签名请求来引导此过程。
首先,创建两个 TLS 身份来引导环境,一个用于 Vault,一个用于 Redis Enterprise 集群 (REC)。换句话说,TLS 用于保护与 Vault、REC 使用的准入控制器 API、到 REC 的数据平面以及由 REC 托管的数据库之间的通信。
在所有情况下,模式如下:
创建私钥
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 教程是一个非常好的资源供您探索。为了本次演练的目的,需要在 Kubernetes 集群中运行一个简单的 Vault 实例(参见配套代码仓库的第一步)。为了创建沙箱环境,在 HashiCorp 提供的一些示例的帮助下,可以使用 TLS 设置 Vault。
HashiCorp 友好地提供了一个 Helm chart,用于在 Kubernetes 集群中安装 Vault。对于本次练习,请将 Vault 安装在与 Redis Enterprise 相同的 Kubernetes 集群中,但位于一个单独的 Kubernetes 命名空间内。
当然,Vault 可以位于单独的 Kubernetes 集群中,可以部署在裸金属虚拟机上,也可以通过 HCP Vault 在云端完全托管。对于此配置:
启用 TLS 和 Injector。
…
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 秘密数据,并且 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 Operator 的完整文档包含值得查阅的更多详细信息。配套代码仓库提供了 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 是我们在部署了数百万个集群后所学到的经验的产物。阅读更多内容,了解如何优化集群的整体性能。