如何使用Vault与Kubernetes为密码提供强有力的保障
今天就跟大家聊聊有关如何使用Vault与Kubernetes为密码提供强有力的保障,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。
介绍
Kubernetes 已经成为了容器编排方案的工业标准,而来自 HashiCorp 的 Vault 则是密码管理的工业标准。那问题来了: 怎样将这两项技术结合使用从而可以让你在 Kubernetes 的应用程序中使用来自于 Vault 中心实例的密码呢?
一种解决方法是使用AppRole认证。Boostport为 AppRoles 在 Kubernetes 上的使用提供了完美的集成。另一个可行的方法是使用Kubernetes 认证。这种认证机制为 Vault 和 Kubernetes 集群创建一个可信的联系因而你可以使用一个服务账号到 Vault 进行认证。后期你可以使用Kubernetes 的 Vault 节点获取和更新认证令牌。
这篇实践的文章中,我会向你展示如何使用一些 Go 助手工具实现诸如认证更新令牌这些相同的工作,并且还会进一步实现-从 Vault 到 Kubernetes 同步预定义的密码子集。
等级: 高级
准备工作
简单起见我有一些选项:
用多种不同的方法启动一个 Kubernetes 集群。通常来说,
minikube
用来测试或者开发。我会使用kubeadm
因为它非常简单的就可以启动一个真正的集群。在 Kubernetes 中,会使用
default
命名空间。Vault 会在开发模式下运行。_不要像在生产环境下那样使用它!_确保在环境变量中设置了
VAULT_ADDR
。代码示例中会使用 Ubuntu。这些已经在 GCE 上配置为 2 vCPU 和 7.5 GB 的 Ubuntu 18.10 VM 上进行了测试。(可以看看 GCP 上 $300 的免费套餐,就是说说而已哈…)
除非另有说明,将会使用 Bash。
Kubernetes
让我们从一个简单的测试集群开始。下面的代码你会看到一个简单节点的安装步骤是什么样的。
# 1) Install Kubernetes on a Ubuntu machinesudo -icurl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add -echo 'deb http://apt.kubernetes.io/ kubernetes-xenial main' >> /etc/apt/sources.list.d/kubernetes.listapt update && apt install -y docker.io kubelet kubeadm kubectlsudo systemctl enable docker.servicekubeadm init --pod-network-cidr=10.244.0.0/16 # Flannel pod network, see belowexit# 2) Prepare kubectlmkdir -p $HOME/.kubesudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/configsudo chown $(id -u):$(id -g) $HOME/.kube/configecho "source <(kubectl completion bash); alias k=kubectl; complete -F __start_kubectl k" >> .bashrc && exec $SHELL# 3) Finalize K8s config kubectl cluster-info kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.ymlkubectl taint nodes --all node-role.kubernetes.io/master-kubectl get nodes -o wide# For details, see: # - https://kubernetes.io/docs/setup/independent/install-kubeadm/# - https://kubernetes.io/docs/setup/independent/create-cluster-kubeadm/
Vault
安装
Vault 的安装非常的直接: 下载解压二进制包即可:
# Install Vaultsudo apt install -y zipcurl -OL https://releases.hashicorp.com/vault/1.1.1/vault_1.1.1_linux_amd64.zipunzip vault_1.1.1_linux_amd64.zipsudo mv vault /usr/local/bin/vault -autocomplete-install && exec $SHELL
运行一个 Vault 服务器
我们会以开发模式运行一个 Vault 服务器。同样,这也非常的简单。请记住,当启动一个开发服务器的时候,一个 root 令牌会被写入到$HOME/.vault-token
中,对 root 用户来说同样如此。使用&
符号会让 Vault 进程在后台运行因此我们可以继续使用相同的 shell。
$ vault server -dev -dev-listen-address=0.0.0.0:8200 &==> Vault server configuration: Api Address: http://0.0.0.0:8200 Cgo: disabled Cluster Address: https://0.0.0.0:8201 Listener 1: tcp (addr: "0.0.0.0:8200", cluster address: "0.0.0.0:8201", max_request_duration: "1m30s", max_request_size: "33554432", tls: "disabled") Log Level: info Mlock: supported: true, enabled: false Storage: inmem Version: Vault v1.1.1 Version Sha: a3dcd63451cf6da1d04928b601bbe9748d53842e
配置 Kubernetes 认证
现在我们必须确保 Kubernetes 可以通过 Kubernetes 认证与 Vault 进行通信。这将会在 Kubernetes 和 Vault 之间建立一个信任的联系。预命名的vault-demo-role
将会映射策略以及定义一个 TTL。
因为我们使用kubeadm
启动的 Kubernetes 集群,它非常轻松的就可以找到kubernetes_ca_cert
参数的证书颁发机构(CA)存储的值。如果使用一个在云安装的 Kubernetes 该过程会比较困难一些。
# NOTE: You may need to set these addresses differently.export INTERNAL_IP=$(dig +short `hostname -f`)export VAULT_ADDR=http://${INTERNAL_IP}:8200# Enable and configure the Kubernetes auth method.# For details, see: # - https://www.vaultproject.io/docs/auth/kubernetes.html# - https://www.vaultproject.io/api/auth/kubernetes/index.htmlvault auth enable kubernetesvault write auth/kubernetes/config \ kubernetes_host=https://${INTERNAL_IP}:6443 \ kubernetes_ca_cert=@/etc/kubernetes/pki/ca.crt vault write auth/kubernetes/role/vault-demo-role \ bound_service_account_names=vault-serviceaccount \ bound_service_account_namespaces=default \ policies=vault-demo-policy \ ttl=1h# Create a policy for demo purposescat <基于角色的访问控制(RBAC)
在 Kubernetes 这边,我们现在需要安装设置相匹配的 RBAC。首先我们创建一个名称为
vault-serviceaccount
的服务账号。然后我们会添加一个叫做vault-closterrolebinding
的集群角色绑定,因而我们新创建的服务账号可以被允许使用默认的集群角色system:auth-delegator
发送认证请求。角色vault-secrectadmin-role
和角色绑定vault-secretadmin-rolebinding
也绑定到了vault-serviceaccount
上这样我们就可以同步密码了。---apiVersion: v1kind: ServiceAccountmetadata: name: vault-serviceaccount---apiVersion: rbac.authorization.k8s.io/v1beta1kind: ClusterRoleBindingmetadata: name: vault-clusterrolebindingroleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: system:auth-delegatorsubjects: - kind: ServiceAccount name: vault-serviceaccount namespace: default---kind: RoleapiVersion: rbac.authorization.k8s.io/v1metadata: name: vault-secretadmin-rolerules:- apiGroups: [""] resources: ["secrets"] verbs: ["*"]---kind: RoleBindingapiVersion: rbac.authorization.k8s.io/v1metadata: name: vault-secretadmin-rolebindingsubjects:- kind: ServiceAccount name: vault-serviceaccountroleRef: kind: Role name: vault-secretadmin-role apiGroup: rbac.authorization.k8s.io让我们应用下面的配置:
$ k apply -f vault-rbac.yaml serviceaccount/vault-serviceaccount createdclusterrolebinding.rbac.authorization.k8s.io/vault-clusterrolebinding createdrole.rbac.authorization.k8s.io/vault-secret-admin-role createdrolebinding.rbac.authorization.k8s.io/vault-demo-secret-admin-rolebinding created准备工作完成了。现在我们可以移步到我们的用例上了。
用例
我们将会覆盖下面的这三个用例:
第一个例子会演示怎样认证到 Vault 然后使用一个初始化的容器获取一个认证令牌。
第二个例子会演示怎样使用 sidecar 容器更新这个令牌。
第三个例子将会演示怎样从 Vault 到 Kubernetes 同步密码。
所有这三个用例均由我在 PostFinance 的同事构建的三个 Docker 镜像上运行的。特别感谢 Marc Sauter,他在Seth Vargo工作的启发下编纂了最初的实现方案。创建的这三个镜像-在Docker Hub上均可获取到-包括了一点 Go 的助手工具,代码均可以从GitHub上找到。
使用初始容器进行认证
第一个例子会展示
vault-kubernetes-authenticator
(简称 *auther*)镜像的用法。auther 在一个初始容器中运行,使用服务账号vault-serviceaccount
向 Vault 进行认证然后将 Vault 的认证令牌写入到/home/vault/.vault-token
中。---apiVersion: apps/v1kind: Deploymentmetadata: name: vault-kubernetes-authenticator-demo labels: appl: vault-kubernetes-authenticator-demospec: replicas: 1 selector: matchLabels: appl: vault-kubernetes-authenticator-demo template: metadata: labels: appl: vault-kubernetes-authenticator-demo spec: serviceAccountName: vault-serviceaccount volumes: - name: vault-token emptyDir: medium: Memory initContainers: - name: vault-kubernetes-authenticator image: postfinance/vault-kubernetes-authenticator imagePullPolicy: Always volumeMounts: - name: vault-token mountPath: /home/vault env: - name: VAULT_ADDR value: ${VAULT_ADDR} - name: VAULT_ROLE value: vault-demo-role - name: VAULT_TOKEN_PATH value: /home/vault/.vault-token containers: - name: kuard image: gcr.io/kuar-demo/kuard-amd64:blue volumeMounts: - name: vault-token mountPath: /home/vault我们应用这些配置然后执行一些测试来验证所有配置都能成功运行。
$ envsubst < vault-kubernetes-authenticator-demo.yaml | k apply -f -deployment.apps/vault-kubernetes-authenticator-demo created$ k get allNAME READY STATUS RESTARTS AGEpod/vault-kubernetes-authenticator-demo-fc49b957c-b5bnx 1/1 Running 0 81sNAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGEservice/kubernetes ClusterIP 10.96.0.1443/TCP 20hNAME READY UP-TO-DATE AVAILABLE AGEdeployment.apps/vault-kubernetes-authenticator-demo 1/1 1 1 81sNAME DESIRED CURRENT READY AGEreplicaset.apps/vault-kubernetes-authenticator-demo-fc49b957c 1 1 1 81s$ k logs vault-kubernetes-authenticator-demo-fc49b957c-b5bnx -c vault-kubernetes-authenticator 2019/04/16 04:45:23 successfully authenticated to vault2019/04/16 04:45:23 successfully stored vault token at /home/vault/.vault-token$ k exec vault-kubernetes-authenticator-demo-fc49b957c-b5bnx -- sh -c "VAULT_ADDR=${VAULT_ADDR} sh"~ $ cat /home/vault/.vault-token; echos.xrrJoCARIC0Z84vcvcwuH5XG~ $ wget --header="X-Vault-Token: $(cat /home/vault/.vault-token)" -q -O - ${VAULT_ADDR}/v1/secret/data/demo/most-used-password{"request_id":"12660a6b-7ad0-85bc-8841-d21c7cc8248a","lease_id":"","renewable":false,"lease_duration":0,"data":{"data":{"password":"123456"},"metadata":{"created_time":"2019-04-16T05:11:44.651116748Z","deletion_time":"","destroyed":false,"version":1}},"wrap_info":null,"warnings":null,"auth":null}~ $ wget --header="X-Vault-Token: $(cat /home/vault/.vault-token)" -q -O - ${VAULT_ADDR}/v1/secret/data/sensitive-passwordwget: server returned error: HTTP/1.1 403 Forbidden 使用 Sidecar 更新令牌
第二个例子将向你展示
vault-kubernetes-token-renewer
(简称 *renewer*)镜像的使用。renewer 运行在一个 sidecar 容器中,周期性地检查 TTL 然后根据检查的情况更新认证令牌。---apiVersion: apps/v1kind: Deploymentmetadata: name: vault-kubernetes-token-renewer-demo labels: appl: vault-kubernetes-token-renewer-demospec: replicas: 1 selector: matchLabels: appl: vault-kubernetes-token-renewer-demo template: metadata: labels: appl: vault-kubernetes-token-renewer-demo spec: shareProcessNamespace: true serviceAccountName: vault-serviceaccount volumes: - name: vault-token emptyDir: medium: Memory initContainers: - name: vault-kubernetes-authenticator image: postfinance/vault-kubernetes-authenticator imagePullPolicy: Always volumeMounts: - name: vault-token mountPath: /home/vault env: - name: VAULT_ADDR value: ${VAULT_ADDR} - name: VAULT_ROLE value: vault-demo-role - name: VAULT_TOKEN_PATH value: /home/vault/.vault-token containers: - name: vault-kubernetes-token-renewer image: postfinance/vault-kubernetes-token-renewer imagePullPolicy: Always volumeMounts: - name: vault-token mountPath: /home/vault env: - name: VAULT_ADDR value: ${VAULT_ADDR} - name: VAULT_ROLE value: vault-demo-role - name: VAULT_TOKEN_PATH value: /home/vault/.vault-token - name: kuard image: gcr.io/kuar-demo/kuard-amd64:blue volumeMounts: - name: vault-token mountPath: /home/vault我们也同样应用一下这些配置然后做一些验证。(我删除了先前的 deployment。)
$ envsubst < vault-kubernetes-token-renewer-demo.yaml | k apply -f -deployment.apps/vault-kubernetes-token-renewer-demo created$ k get allNAME READY STATUS RESTARTS AGEpod/vault-kubernetes-token-renewer-demo-694cc7dbbd-rkbbs 2/2 Running 0 4sNAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGEservice/kubernetes ClusterIP 10.96.0.1443/TCP 31hNAME READY UP-TO-DATE AVAILABLE AGEdeployment.apps/vault-kubernetes-token-renewer-demo 1/1 1 1 4sNAME DESIRED CURRENT READY AGEreplicaset.apps/vault-kubernetes-token-renewer-demo-694cc7dbbd 1 1 1 4s$ k logs vault-kubernetes-token-renewer-demo-694cc7dbbd-rkbbs -c vault-kubernetes-authenticator 2019/04/16 15:40:55 successfully authenticated to vault2019/04/16 15:40:55 successfully stored vault token at /home/vault/.vault-token$ k logs vault-kubernetes-token-renewer-demo-694cc7dbbd-rkbbs -c vault-kubernetes-token-renewer 2019/04/16 15:40:56 start renewer loop2019/04/16 15:40:56 token renewed 从 Vault 到 Kubernetes 同步密码
第三个例子将向你展示
vault-kubernetes-synchronizer
(简称 *syncer*)的用法。syncer 可以用在多个方面。在这个 demo 里面,一个 Kubernetes 任务将被用来从预定义的路径一次性同步 Vault 密码。这些 Vault 密码将会被写入到相应的 Kubernetes 密码中。---apiVersion: batch/v1kind: Jobmetadata: name: vault-kubernetes-synchronizer-demospec: backoffLimit: 0 template: spec: serviceAccountName: vault-serviceaccount restartPolicy: Never volumes: - name: vault-token emptyDir: medium: Memory initContainers: - name: vault-kubernetes-authenticator image: postfinance/vault-kubernetes-authenticator imagePullPolicy: Always volumeMounts: - name: vault-token mountPath: /home/vault env: - name: VAULT_ADDR value: ${VAULT_ADDR} - name: VAULT_ROLE value: vault-demo-role - name: VAULT_TOKEN_PATH value: /home/vault/.vault-token containers: - name: vault-kubernetes-synchronizer image: postfinance/vault-kubernetes-synchronizer imagePullPolicy: Always volumeMounts: - name: vault-token mountPath: /home/vault env: - name: VAULT_ADDR value: ${VAULT_ADDR} - name: VAULT_ROLE value: vault-demo-role - name: VAULT_TOKEN_PATH value: /home/vault/.vault-token - name: VAULT_SECRETS value: secret/demo/first,secret/demo/second,secret/demo/first:third,secret/demo/greek/同样,我们也应用一下这些配置然后看看所有的配置是否如预期一样运行正常:
$ envsubst < vault-kubernetes-synchronizer-demo.yaml | k apply -f -job.batch/vault-kubernetes-synchronizer-demo created$ k get allNAME READY STATUS RESTARTS AGEpod/vault-kubernetes-synchronizer-demo-m2xnz 1/1 Running 0 4sNAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGEservice/kubernetes ClusterIP 10.96.0.1443/TCP 3d5hNAME COMPLETIONS DURATION AGEjob.batch/vault-kubernetes-synchronizer-demo 0/1 4s 4s$ k logs pod/vault-kubernetes-synchronizer-demo-m2xnz -c vault-kubernetes-authenticator2019/04/18 14:29:42 successfully authenticated to vault2019/04/18 14:29:42 successfully stored vault token at /home/vault/.vault-token$ k logs pod/vault-kubernetes-synchronizer-demo-m2xnz2019/04/18 14:29:43 read secret/demo/first from vault2019/04/18 14:29:43 update secret third from vault secret secret/demo/first2019/04/18 14:29:43 read secret/demo/greek/alpha from vault2019/04/18 14:29:43 update secret alpha from vault secret secret/demo/greek/alpha2019/04/18 14:29:43 read secret/demo/greek/beta from vault2019/04/18 14:29:43 update secret beta from vault secret secret/demo/greek/beta2019/04/18 14:29:43 read secret/demo/greek/gamma from vault2019/04/18 14:29:43 update secret gamma from vault secret secret/demo/greek/gamma2019/04/18 14:29:43 read secret/demo/first from vault2019/04/18 14:29:43 update secret first from vault secret secret/demo/first2019/04/18 14:29:43 read secret/demo/second from vault2019/04/18 14:29:43 update secret second from vault secret secret/demo/second2019/04/18 14:29:44 secrets successfully synchronized$ k get secretsNAME TYPE DATA AGEalpha Opaque 1 2m43sbeta Opaque 1 2m43sdefault-token-ssd7f kubernetes.io/service-account-token 3 3d5hfirst Opaque 2 2m43sgamma Opaque 1 2m43ssecond Opaque 2 2m43sthird Opaque 2 2m43svault-serviceaccount-token-f6tnw kubernetes.io/service-account-token 3 2d20h$ k describe secret firstName: firstNamespace: defaultLabels: Annotations: vault-secret: secret/demo/firstType: OpaqueData====one: 10 bytestwo: 10 bytes$ k describe secret alphaName: alphaNamespace: defaultLabels: Annotations: vault-secret: secret/demo/greek/alphaType: OpaqueData====philosopher: 5 bytes syncer 同样可以用在 Kubernetes 的 cron 任务中从 Vault 周期性同步密码或者同步到另一个 Kubernetes deployment 的初始容器中,这样密码就会保持最新状态。
需要注意的是 Kubernetes 密码保护不是很好。Seth Vargo 在最近的 FOSDEM访谈中指出,默认情况下,它们仅做了 base64 编码和存储,就像在 etcd 中那样。你应该允许数据的静态加密。也请确保你只同步那些你的 Kubernetes 应用程序使用的那些密码,这些密码由相应的 Vault 策略以及命名角色保护。除此之外,该方法还允许你以云原生行为使用密码。你的应用程序不能直接访问 Vault 密码可以被注入到环境变量中。
Kubernetes 和 Vault 这两项技术在结合使用或者集成它们使用时均是很棒的组合。集成的方案看似不简单但是依旧可行。
看完上述内容,你们对如何使用Vault与Kubernetes为密码提供强有力的保障有进一步的了解吗?如果还想了解更多知识或者相关内容,请关注行业资讯频道,感谢大家的支持。