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 的能力”。
考虑到这一点,以下几个因素可能会使密钥管理成为一项挑战:
DevOps 团队该怎么办?团队很可能会委托另一项服务,例如 HashiCorp Vault。
HashiCorp Vault 是一个与平台无关、基于身份的外部密钥操作符。与 Kubernetes Secret 不同,Vault 会在共享敏感数据之前授权所有访问操作。
Vault 密钥可从任何地方访问。这里没有假设应用程序已部署在何处;访问控制通过 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 的演练。以下是成功部署所需步骤的清单。
开始的前提条件是具有足够资源用于 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代替到模板中,以避免键入错误。
你可能早已知道 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 图表以在 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 秘钥,并且 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 的完整文档 提供其他值得查阅的信息。配套 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 是我们在百万次集群部署后总结的经验。阅读更多信息了解如何优化集群的总体性能。