有效且高效管理“Kubernetes Secrets”的方法

image.png

首先

现在,Kubernetes已经成为容器编排的标准。由于许多企业逐渐采用容器优先的开发结构,大部分现有的工作负载都在公共云或私有数据中心的虚拟机上运行。因此,许多企业面临从以前的方式迁移到Kubernetes并同时面对其后续困难的挑战。

Kubernetes的转换会对包括监控、日志记录、CI/CD和最重要的安全性在内的整个DevOps流程产生影响,安全性应在集群级别和应用程序级别都得到处理。

在这篇文章中,我想讲解一下如何在Kubernetes中有效地管理应用程序的机密信息。

在Kubernetes中,机密信息如API集成令牌、OAuth令牌、数据库密码等由Secret对象管理。这些Secret可以作为挂载卷的形式让Pod访问。

如果在每个环境中运行不同的Kubernetes集群(强烈建议这样做),建议将环境特定的所有机密信息保存在一个地方。然后,确保有一个能够智能识别部署Pod的环境,并相应获取机密信息的机密管理工具。关于这一点,在本帖的后半部分将有详细说明。

开始使用Kubernetes Secrets

使用Kubectl创建秘密对象,并将其作为卷挂载。

让我们创建一个应用程序使用的令牌密码,以便在第三方服务中进行身份验证。

可以使用文字值或文件来创建Secret。 在这种情况下,将秘密信息放在名为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中使用。

首先,创建Demo 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:/# 

使用这种方法,您可以安全地挂载包含机密数据的配置文件,并且之后可以从已挂载的目录中的应用程序中读取它们。然而,有时候您可能也希望将机密数据作为应用程序的环境变量来使用。让我们在下一个部分尝试一下这样做。

将”2.2Secrets”作为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作为环境变量插入,需要使用存储Secret的密钥来访问Secret,并将其映射到Pod的环境变量中。以下示例中,我们将在另一个示范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环境变量的值,并显示解码后的Secret值。

$ kubectl exec -it demo-pod-2 /bin/bash
root@demo-pod-2:/# echo $APP_AUTH_TOKEN
WEj4VmNF755uc9vZdz98zvPXB6DkHp
root@demo-pod-2:/# 

继续阅读

继续阅读更多

最近,Kubernetes宣布了一个新功能,用于对保存的密码进行加密,我强烈推荐你去阅读一下。尽管这个功能非常新,但也值得尝试一下。

3. Kubernetes秘密管理的高级功能

在之前,我们学习了Kubernetes Secrets的工作原理以及如何在Pod中使用secret。然而,如果我们运行多个Kubernetes集群(开发/灰度/生产),我们就需要一个集中式的secret存储和安全机制来将所需的secret导入到Pod环境中。

我将介绍两种解决方案。

    1. 在基于Kubernetes的身份验证中使用Vault

 

    通过将Chamber集成到Dockerfile中,从AWS参数存储加载Secret(适用于AWS上的Kubernetes集群)

3.1 使用Kubernetes作为基础认证的方式来使用Vault

Vault基于的认证工作流程可以总结如下。

    1. Pods are deployed in specific namespaces and associated with specific service accounts.

 

    1. The service accounts associated with namespaces are linked to the Kubernetes authentication role for the Vault backend.

 

    1. Pods authenticate with Vault using the token of the service account to obtain the VAULT_TOKEN.

Using the VAULT_TOKEN and VAULT_ADDRESS, secrets can be securely retrieved from Vault.

另外,在将这些Secrets作为环境变量注入到Pod中,有几个选项可供选择。稍后将对此进行解释。

3.2 Vault是什么?

Vault是一种轻量级工具,用于有效地保存和管理秘密。它完全加密,并支持安全的Kubernetes认证。Vault通常由Consul作为存储引擎进行支持。因此,它非常可靠且具有高度的容错能力,能够在节点故障时恢复。

3.3 设置Vault

为了为Kubernetes应用程序配置Vault,有几种方法可供选择。

    1. 在可从整个环境访问的Kubernetes集群中配置Vault。

 

    使用托管版本的Vault。

在这篇博文中,我们将不涉及如何在Kubernetes上设置Vault,但我将列出可用的资源。

    1. 以下是用中文原生方式释义的选项:

https://www.hashicorp.com/blog/announcing-the-vault-helm-chart
– 哈希公司发布了Vault Helm Chart。

https://github.com/hashicorp/consul-helm
– 哈希公司的Consul Helm仓库。

一旦设置好Vault,就可以连接到Kubernetes集群。正如前面所述,我们的目标是使得Pod能够有效地通过Vault进行认证,并使应用程序能够获取Secret。

使用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 认证服务账号扮演了审阅者的角色。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 Secret配置到环境变量中,以供应用程序使用。

如果你的应用程序中有逻辑使用VAULT_TOKEN和VAULT_ADDRESS环境变量直接从Vault中读取数据的话,你可以完全跳过这部分。否则,执行以下操作。

    1. 将vaultenv与Docker容器集成

 

    在入口点脚本的开头使用vaultenv来配置环境

4. 将密钥保存在AWS参数存储中。

4.1 AWS参数存储是什么?

这是一个由AWS提供的非常可扩展且安全的服务,用于存储Secrets。您可以使用AWS CLI来读写Secrets。如果要从EC2实例或ECS任务中进行访问,您需要配置适当的IAM角色。

4.2 什么是「チャンバー(Chamber)」?

Chamber是一个能够在AWS Parameter Store中读写秘钥的命令行实用工具。它支持许多其他命令,以便添加Secrets到执行环境或以各种格式导出秘钥。

开始这个,需要以下信息。

    1. AWS_DEFAULT_REGION: 亚马逊云服务默认区域

 

    1. AWS_SECRET_KEY_ID: 亚马逊云服务密钥ID

 

    AWS_SECRET_ACCESS_KEY: 亚马逊云服务访问密钥

4.3 Chamber进行本地安装

如果你正在使用Mac,你可以使用以下命令在本地安装Chamber。

brew update
brew install chamber

请确认笔记本电脑上已经设置了AWS命令行工具。

使用以下内容将Secret写入AWS参数存储。

chamber write <service> <key> <value>

使用以下方式,从AWS参数存储中读取秘钥。

chamber read <service> <key>

4.4 Chamber与Kubernetes应用程序的集成

要将Chamber与Kubernetes应用程序整合,需要进行一些较小的更改。

    1. 应用程序的Dockerfile

 

    1. 脚本的入口点

 

    部署清单文件

在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)"

在部署清单中需要设置以下环境变量。

    1. AWS_DEFAULT_REGION: 这对应于访问凭证的默认区域。

SERVICE: 这对应于想要使用Chamber从参数存储中读取Secrets的服务。

Chamber需要访问AWS_SECRET_KEY_ID和AWS_SECRET_ACCESS_KEY,但不建议在Pod清单文件中进行这些配置。强烈建议使用基于IAM角色的访问权限方法,在AWS Parameter Store中进行认证。为此,请确保为分配给Kubernetes工作节点的IAM角色启用了ssm:GetParameters操作。

当所有这些配置都完成后,Pod中的容器将会读取SERVICE环境变量中的秘密信息。然后,使用AWS参数存储进行身份认证,并将容器所需的所有秘密信息导入容器的环境中。

5. 总结

如果Kubernetes的密钥管理得当,可以显著简化部署过程。可以选择将它们插入应用程序的执行环境中,或使用自定义构建逻辑进行实时读取。

如果在AWS云上部署Kubernetes集群,我们强烈推荐使用Chamber和AWS参数存储(AWS Parameter Store)的整合。这是最简单且非常安全的开始方式。

此外,您还可以借助Kiam的帮助,通过使用细粒度的IAM访问来管理对参数存储库的访问。这样一来,只有集群内特定的Pod可以从AWS参数存储库获取和使用Secrets。然而,这又是另外一个讨论话题,以后再说吧。

那么,我们下一篇文章再见!

bannerAds