关于【Kubernetes密钥】的设置和管理进行解释说明
首先
近年,Kubernetes已成为容器编排的标准。由于企业逐渐采用容器优先的开发结构,大部分现有工作负载都在公共云或私有数据中心的虚拟机上运行。因此,很多企业正在从以前的方法转向Kubernetes,但事实上他们面临着许多问题。
将工作载迁移到Kubernetes会对整个DevOps流程产生影响,包括监控、日志记录、持续集成/持续部署以及最为重要的安全性。安全性可以在集群级别和应用程序级别都进行处理。
在这篇文章中,我们将更详细地介绍如何在Kubernetes中有效地管理应用程序的机密信息。
在Kubernetes中,机密信息如API整合令牌、OAuth令牌、数据库密码等,通过Secret对象进行管理。这些Secret可以作为挂载的卷被Pod访问到。
如果在不同的环境中运行不同的Kubernetes集群(推荐),建议将特定于环境的所有秘密保存在一个地方。然后,确保有一个可以智能识别部署Pod所在环境并相应获取秘密的秘密管理工具。关于此,我将在本帖的后半部分进行详细说明。
Kubernetes秘密管理入门
使用Kubectl创建机密并将其作为卷挂载
让应用程序创建用于在第三方服务中进行身份验证的令牌密钥。
您可以通过文字值或者文件来创建一个秘钥。在这种情况下,我们把秘钥信息存放在名为access.txt的文件中。
$cat access.txt
APP_AUTH_TOKEN=WEj4VmNF755uc9vZdz98zvPXB6DkHp
$ kubectl create secret generic auth-token --from-file=./access.txt
secret "auth-token" created
$kubectl get secrets
NAME TYPE DATA AGE
auth-token Opaque 1 14s
default-token-k7vmv kubernetes.io/service-account-token 3 17m
$ kubectl describe secret auth-token
Name: auth-token
Namespace: default
Labels: <none>
Annotations: <none>
Type: Opaque
Data
====
access.txt: 46 bytes
让我们将这个秘密作为挂载在 Pod 上的卷来使用。
首先,创建一个演示Pod,然后应用清单。
$ cat pod.yaml
---
apiVersion: v1
kind: Pod
metadata:
name: demo-pod
spec:
containers:
- name: demo-pod
image: ubuntu
command: ["/bin/bash", "-ec", "while :; do echo '.'; sleep 5 ; done"]
volumeMounts:
- name: myvolume
mountPath: "/tmp"
readOnly: true
volumes:
- name: myvolume
secret:
secretName: auth-token
$ kubectl apply -f pod.yaml
pod "demo-pod" created
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
demo-pod 1/1 Running 0 1m
在这里,如果在此Pod内执行,应该能够找到挂载在/tmp目录的密钥。
$ kubectl exec -it demo-pod /bin/bash
root@demo-pod:/# ls /tmp
access.txt
root@demo-pod:/# cat /tmp/access.txt
APP_AUTH_TOKEN=WEj4VmNF755uc9vZdz98zvPXB6DkHp
root@demo-pod:/#
使用该方法,可以安全地挂载包含机密数据的配置文件。然后,可以从挂载的目录中由应用程序进行读取。但是,有时候想要将机密数据作为应用程序的envvar使用。让我们在下一节中尝试一下这个。
使得秘密可以作为Pod环境变量可用。
为了能够将秘密作为环境变量使用,创建并应用秘密对象清单。在将数据放入秘密文件之前,首先对数据进行编码,确保数据以纯文本形式存储。
$ echo WEj4VmNF755uc9vZdz98zvPXB6DkHp | base64
V0VqNFZtTkY3NTV1Yzl2WmR6OTh6dlBYQjZEa0hwCg==
$ cat auth_secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: mysecret
type: Opaque
data:
auth_token: V0VqNFZtTkY3NTV1Yzl2WmR6OTh6dlBYQjZEa0hwCg==
$ kubectl apply -f auth_secret.yaml
secret "mysecret" created
$ kubectl describe secret mysecret
Name: mysecret
Namespace: default
Labels: <none>
Annotations:
Type: Opaque
Data
====
auth_token: 31 bytes
要将secret作为envvar插入,需要使用保存secret的密钥访问secret,并将其映射到pod的envvar中。下面的示例使用另一个演示pod demo-pod-2 来完成这个操作。
$ cat pod-2.yaml
apiVersion: v1
kind: Pod
metadata:
name: demo-pod-2
spec:
containers:
- name: demo-pod-2
image: ubuntu
command: ["/bin/bash", "-ec", "while :; do echo '.'; sleep 5 ; done"]
env:
- name: APP_AUTH_TOKEN
valueFrom:
secretKeyRef:
name: mysecret
key: auth_token
$ kubectl apply -f pod-2.yaml
pod "demo-pod-2" created
在这里运行demo-pod-2,并检查APP_AUTH_TOKEN环境变量的值,将会显示解码后的秘钥值。
$ kubectl exec -it demo-pod-2 /bin/bash
root@demo-pod-2:/# echo $APP_AUTH_TOKEN
WEj4VmNF755uc9vZdz98zvPXB6DkHp
root@demo-pod-2:/#
文献引用
最近,Kubernetes发布了一个用于加密保存的秘密的新功能。非常强烈地建议您阅读此内容。尽管这个功能是全新的,但我认为仍然值得一试。
Kubernetes秘密的高级管理
迄今为止,我们已经解释了Kubernetes的机密如何工作以及如何在Pod中使用它们。然而,如果您正在运行多个Kubernetes集群(开发/预发布/生产),则需要一个集中的机密存储和安全机制来设置Pod环境所需的机密。
从这里开始,我们将考虑针对这个问题的两种解决方案。
- 使用基于Kubernetes的身份验证来使用Vault,在Dockerfiles中集成Chamber,从AWS参数存储输入秘密(针对AWS上的Kubernetes集群)。
使用基于Kubernetes的认证来使用Vault。
Vault基于身份验证的工作流程可以总结如下。
-
- Pod被部署在特定的命名空间中,并与特定的服务账号关联。
-
- 与命名空间相关联的服务账号将与Vault后端的Kubernetes认证角色关联。
-
- Pod将使用服务账号的令牌在Vault中进行认证,并获取VAULT_TOKEN。
使用VAULT_TOKEN和VAULT_ADDRESS,可以安全地从Vault获取机密信息。
而且,在将这些密码作为环境变量插入到Pod中时,有几个选项可供选择。稍后会进行说明。
Vault 是什么?
Vault是一款轻量级工具,可有效地存储和管理保密信息。它强力支持完全加密的安全Kubernetes认证。通常,Vault由Consul作为存储引擎来支持。因此,Vault具有高可靠性和对节点故障的抗性。
Vault的配置设置
有几种可以为Kubernetes应用程序配置Vault的方法。
-
- 在可跨环境访问的Kubernetes集群中配置Vault。
- 使用托管的Vault的版本。
虽然本文的目的超出了在Kubernetes中配置Vault的方法,但以下列举了几种可用的资源供参考。
-
- 这是 HashiCorp 宣布发布 Vault Helm Chart 的博客:https://www.hashicorp.com/blog/announcing-the-vault-helm-chart
这是 HashiCorp 官方提供的 Consul Helm 的 GitHub 链接:https://github.com/hashicorp/consul-helm
安装Vault后,可以连接到Kubernetes集群。如前所述,目标是使Pod可以有效地通过Vault进行身份验证,以便应用程序可以获取秘密。
使用 Vault 进行 Kubernetes Pod 的认证
首先,在下述权限下创建一个Kubernetes服务账号。
$ cat vault_sa.yaml
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
name: role-tokenreview-binding
namespace: default
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: system:auth-delegator
subjects:
- kind: ServiceAccount
name: vault-auth
namespace: default
$ kubectl apply -f vault_sa.yaml
clusterrolebinding.rbac.authorization.k8s.io "role-tokenreview-binding" created
接下来,我们将获取变量并在Vault后端中启用Kubernetes认证。
Kubernetes 主机 – 关联到 Vault 的地址
k8s_host="$(kubectl config view --minify | grep server | cut -f 2- -d ":" | tr -d " ")"
集群授权数据-用于验证连接的证明
k8s_cacert="$(kubectl config view --raw --minify --flatten -o jsonpath='{.clusters[].cluster.certificate-authority-data}' | base64 --decode)"
用户令牌 – vault-auth服务帐户具有令牌审核器的角色。 Vault使用此服务帐户与集群进行交互。
secret_name="$(kubectl get serviceaccount vault-auth -o go-template='{{ (index .secrets 0).name }}')"
account_token="$(kubectl get secret ${secret_name} -o go-template='{{ .data.token }}' | base64 --decode)"
当获取到这些值之后,您可以在Vault后端中启用Kubernetes认证。
vault auth enable kubernetes
vault write auth/kubernetes/config \
token_reviewer_jwt=${account_token} \
kubernetes_host=${k8s_host} \
kubernetes_ca_cert=${k8s_cacert}
接下来,我们需要确保新启动的Pod可以在Vault服务器上进行身份验证。为此,我们将命名空间绑定到Vault角色,并将来使用该命名空间的服务账户JWT进行身份验证。
vault write auth/kubernetes/role/demo bound_service_account_names=vault-auth bound_service_account_namespaces=default policies=demo-policy ttl=1h
让我们来做个测试吧!
请制作一个演示用的Pod,并使用Vault进行身份验证。
kubectl run -it --rm --image=ubuntu --serviceaccount=vault-auth test -- /bin/bash
root$ apt-get update -y && apt-get install vim curl jq mysql-client -y
#Let's get the service account JWT token
root$ JWT="$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)"
#Now we can use this to get the vault token
root$ VAULT_TOKEN="$(curl --request POST --data '{"jwt": "'"$JWT"'", "role": "demo"}' -s -k https://${VAULT_ADDRESS}/v1/auth/kubernetes/login | jq -r '.auth.client_token')"
现在,您可以使用VAULT_TOKEN在Vault上进行身份验证并获取秘密。但这只是战斗的一半。我们的目标是将Vault秘密设置到环境变量中,以便应用程序可以使用它们。
如果应用程序中存在使用VAULT_TOKEN和VAULT_ADDRESS环境变量直接从Vault中读取逻辑,您可以完全跳过这部分。否则,您可以执行以下操作。
-
- 将vaultenv与Docker容器集成。
- 在入口脚本的开头使用vaultenv,将数据输入到环境中。
将秘密保存在AWS参数存储中。
请使用中文为以下内容进行释义,只需要提供一种版本:
– I am going to the store to buy some groceries.
AWS参数存储是什么?
在中国土生土长人士的母语中改写:
AWS参数存储是由AWS提供的非常可扩展且安全的服务,用于存储机密信息。您可以使用AWSCLI来读写机密信息。如果要从EC2实例或ECS任务进行访问,则需要设置适当的IAM角色。
“什么是乐室?”
Chamber是一款有用的命令行工具,用于从AWS参数存储中读取和写入机密信息。它支持许多其他命令,以便将机密信息输入到执行环境中,或以各种格式进行导出。
开始这个的话,需要以下的信息。
-
- AWS_DEFAULT_REGION:AWS的默认区域
-
- AWS_SECRET_KEY_ID:AWS的密钥ID
- AWS_SECRET_ACCESS_KEY:AWS的访问密钥
把Chamber安装到本地
如果您正在使用Mac,可以使用以下命令将Chamber安装到本地。
brew update
brew install chamber
请确认笔记本电脑上已经配置了AWSCLI。
使用以下方法将秘密写入AWS参数存储。
chamber write <service> <key> <value>
使用下面的代码,从AWS参数存储中读取秘密。
chamber read <service> <key>
将Chamber与Kubernetes应用程序相融合
要将Chamber与Kubernetes应用程序集成,需要进行一些小的更改。
-
- アプリのDockerfile
-
- スクリプトのエントリポイント
-
- デプロイメントマニフェストファイル
- Dockerfileの上部に、次のコンテンツを追加
FROM golang:1.10.4 AS build
RUN CGO_ENABLED=0 GOOS=linux go get -v github.com/segmentio/chamber
FROM <existing base image>
COPY --from=build /go/bin/chamber /chamber
…
根据这个,chamber二进制文件将被构建并集成到容器中。
在入口脚本中,我们将进行以下更改。
在主入口逻辑启动之前,我们将添加以下语句来设置环境变量。
eval "$(chamber env $SERVICE)"
以下的环境变量需要在部署清单中进行设置。
AWS_DEFAULT_REGION:これはアクセス認証情報のデフォルトリージョンに対応します
SERVICE:これは、Chamberを使用してパラメータストアからシークレットを読み取りたいサービスに対応します
Chamber需要访问AWS_SECRET_KEY_ID和AWS_SECRET_ACCESS_KEY,但不建议在Pod Manifest文件中设置这些。强烈推荐使用IAM角色为基础的权限方法来在AWS参数存储中进行身份验证。要实现这一点,请确保赋予了Kubernetes工作节点上分配的IAM角色ssm:GetParameters权限操作。
当设置好这些后,容器在服务环境变量下会读取秘钥。然后,请使用AWS参数存储进行身份验证,并在容器环境中设置该服务所需的所有秘钥。
总结
如果正确管理,Kubernetes的Secret可以大大简化部署过程。您可以将它们插入应用程序的执行环境,或者选择使用自定义构建逻辑来即时读取它们。
如果将Kubernetes集群部署到AWS云上,强烈建议使用Chamber和AWS参数存储库的集成,因为它是最简单和安全的启动方式。
此外,您可以使用Kiam来管理对参数存储的访问,通过使用细粒度的IAM访问控制。这样一来,只有在集群中的特定Pod才能从AWS参数存储获取和使用机密信息。不过,这涉及到另一个讨论话题。
如果您需要监控Kubernetes的设置,请预订MetricFire的演示并直接联系我们。我们已经帮助了许多MetricFire客户监视他们的Kubernetes集群。