乐趣区

关于后端:Kubernetes-的-secret-并不是真正的-secret

引言

Kubernetes 曾经成为古代软件基础设施中不可或缺的一部分。因而,治理 Kubernetes 上的敏感数据也是古代软件工程的一个重要方面,这样您就能够将安全性从新置于 DevSecOps 中。Kubernetes 提供了一种应用 Secret 对象存储敏感数据的办法。尽管总比没有好,但它并不是真正的加密,因为它只是 base64 编码的字符串,任何有权拜访集群或代码的人都能够对其进行解码。

留神:默认状况下,Kubernetes Secrets 未加密存储在 API 服务器的底层数据存储 (etcd) 中。具备 API 拜访权限的任何人都能够检索或批改 Secret,任何具备 etcd 拜访权限的人也能够。此外,任何有权在命名空间中创立 Pod 的人都能够应用该拜访权限来读取该命名空间中的任何 Secret;这包含间接拜访,例如创立 Deployment 的能力。— Kubernetes 文档

应用正确的 RBAC 配置和爱护 API 服务器能够解决从集群读取 secret 的问题,理解无关 RBAC 和集群 API 安全性的更多信息请查看如何应用最佳实际爱护您的 Kubernetes 集群。爱护源代码中的的 secret 是更大的问题。每个有权拜访蕴含这些 secret 的存储库的人也能够解码它们。这使得在 Git 中治理 Kubernetes secret 变得十分辣手。

让咱们看看如何应用更平安的形式设置 secret :

  • Sealed Secrets
  • External Secrets Operator
  • Secrets Store CSI driver

您须要一个 Kubernetes 集群来运行示例。我应用 k3d 创立了一个本地集群。您也能够应用 kind 或 minikube。


Sealed Secrets

Sealed Secrets 是一个开源的 Kubernetes 控制器和来自 Bitnami 的客户端 CLI 工具,旨在应用非对称明码加密解决“在 Git 中存储 secret”问题的一部分。具备 RBAC 配置的 Sealed Secrets 避免非管理员读取 secret 是解决整个问题的绝佳解决方案。

它的工作原理如下:

  1. 应用公钥和 kubeseal CLI 在开发人员机器上加密 secret。这会将加密的 secret 编码为 Kubernetes 自定义资源定义 (CRD)。
  2. 将 CRD 部署到指标集群。
  3. Sealed Secret 控制器应用指标集群上的私钥对秘密进行解密,以生成规范的 Kubernetes secret。

私钥仅供集群上的 Sealed Secrets 控制器应用,公钥可供开发人员应用。这样,只有集群能力解密秘密,而开发人员只能对其进行加密。

长处

  • 反对模板定义,以便能够将元数据增加到未加密的 secret 中。例如,您能够应用模板定义为未加密的 secret 增加标签和正文。
  • 未加密的 secret 将由加密的 secret CRD 领有,并在加密的 secret 更新时更新。
  • 默认状况下,证书每 30 天轮换一次,并且能够自定义。
  • secret 应用每个集群、命名空间和 secret 组合(私钥 + 命名空间名称 + secret 名称)的惟一密钥进行加密,避免解密中呈现任何破绽。在加密过程中,能够应用 strict, namespace-wide, cluster-wide 来配置范畴。
  • 可用于治理集群中的现有 secret。
  • 具备 VSCode 扩大,使其更易于应用。

毛病

  • 因为它将加密的 secret 解密为惯例 secret,如果您有权拜访集群和命名空间,您依然能够解码它们。
  • 须要为每个集群环境从新加密,因为密钥对对于每个集群都是惟一的。

装置

在集群上装置 controller,在本地机器上装置 CLI。

  1. 从 release 页面下载 controller.yaml
  2. 执行 kubectl apply -f controller.yaml 将 controller 部署到集群中。控制器将装置到 kube-system 命名空间下。
  3. 装置 CLI,通过 brew install kubeseal 装置,或者从 release 页面下载。

应用

让咱们创立一个 sealed secret。

  1. 创立一个 secret,通过命令 kubectl create secret 或者编写 yaml 文件,如下所示:
echo -n secretvalue | kubectl create secret generic mysecret \
  --dry-run=client \
  --from-file=foo=/dev/stdin -o yaml > my-secret.yaml

这将产生一个如下所示的 secret 定义;

# my-secret.yaml

apiVersion: v1
data:
  foo: c2VjcmV0dmFsdWU=
kind: Secret
metadata:
  creationTimestamp: null
  name: mysecret
  1. 应用 kubeseal CLI 加密 secret。这将应用从服务器获取的公钥加密 secret 并生成加密的 secret 定义。当初能够抛弃 my-secret.yaml 文件。您也能够下载公钥并在本地离线应用。
kubeseal --format yaml < my-secret.yaml > my-sealed-secret.yaml

这将产生一个加密的 secret 定义,my-sealed-secret.yaml,如下所示;

# my-sealed-secret.yaml

apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
  creationTimestamp: null
  name: mysecret
  namespace: default
spec:
  encryptedData:
    foo: AgA6a4AGzd7qzR8mTPqTPFNor8tTtT5...==
  template:
    metadata:
      creationTimestamp: null
      name: mysecret
      namespace: default

此文件能够平安地提交到 Git 或与其余开发人员共享。

  1. 最初,您能够将其部署到要解封的集群中。
kubectl apply -f my-sealed-secret.yaml
  1. 当初,您能够在集群中看到未加密的 secret。
kubectl describe secret mysecret

您能够像应用任何其余 Kubernetes 密钥一样在部署中应用此密钥。


External Secrets Operator

Sealed Secrets 是爱护 secret 的形式之一,但除此之外还有更好的办法。应用 External Secrets Operator (ESO) 和内部 secret 管理系统,如 HashiCorp Vault、AWS Secrets Manager、Google Secrets Manager 或 Azure Key Vault。尽管设置起来有点简单,但如果您应用云提供商来托管您的 Kubernetes 集群,这是一种更好的办法。ESO 反对许多这样的 secret 管理器并监督内部 secret 存储的变动,并使 Kubernetes secret 放弃同步。

ESO 提供了四个 CRD 来治理 secret。ExternalSecretClusterExternalSecret CRD 定义须要获取哪些数据以及如何转换这些数据。SecretStoreClusterSecretStore CRD 定义了与内部 secret 存储的连贯细节。Cluster 前缀的 CRD 示意作用范畴是集群。

它的工作原理如下;

  1. 创立 SecretStoreCRD 以定义与内部秘密存储的连贯详细信息。
  2. 在内部 secret 存储中创立 secret。
  3. 创立一个 ExternalSecretCRD 来定义须要从内部 secret 存储中获取的数据。
  4. 将 CRD 部署到指标集群。
  5. ESO 控制器将从内部 secret 存储中获取数据并创立 Kubernetes secret。

长处

  • secret 存储在平安的内部 secret 管理器中,而不是代码存储库中。
  • 使 secret 与内部 secret 管理器放弃同步。
  • 与许多内部 secret 管理者单干。
  • 能够在同一个集群中应用多个 secret 存储。
  • 提供用于监控的 Prometheus 指标。

毛病

  • 须要精心设置能力应用。
  • 创立一个 Kubernetes secret 对象,如果您有权拜访集群和命名空间,则能够对其进行解码。
  • 依附内部 secret 管理器及其拜访策略来确保安全。

装置

能够应用以下命令通过 Helm 装置 ESO:

helm repo add external-secrets https://charts.external-secrets.io

helm install external-secrets \
  external-secrets/external-secrets \
  --namespace external-secrets \
  --create-namespace

如果您想在 Helm release 中蕴含 ESO,请将 --set installCRDs=true 标记增加到上述命令中。

让咱们看看如何将 ESO 与不同的 secret 管理器一起应用。

应用 HashiCorp Vault

HashiCorp Vault 是一个风行的 secret 管理器,提供不同的 secret 引擎。ESO 只能与 Vault 提供的 KV Secrets Engine 一起应用。Vault 在 HashiCorp 云平台 (HCP) 上提供了一个您能够自行治理的收费开源版本和一个带有收费等级的托管版本。

确保您在本地 Vault 实例或 HCP cloud 中设置了键值 secret 存储。您还能够应用 Vault Helm chart 将 Vault 部署到 Kubernetes 集群。

  1. 创立一个新的 SecretStore CRD,vault-backend.yaml,以定义与 Vault 的连贯详细信息。
# vault-backend.yaml

apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
  name: vault-backend
spec:
  provider:
    vault:
      server: 'YOUR_VAULT_ADDRESS'
      path: 'secret'
      version: 'v2'
      namespace: 'admin' # required for HCP Vault
      auth:
        # points to a secret that contains a vault token
        # https://www.vaultproject.io/docs/auth/token
        tokenSecretRef:
          name: 'vault-token'
          key: 'token'
  1. 创立一个 secret 资源来保留 Vault token。应用具备对 Vault KV 存储中的 secret/ 门路具备读取权限的策略的令牌。
kubectl create secret generic vault-token \
  --dry-run=client \
  --from-literal=token=YOUR_VAULT_TOKEN
  1. 在 Vault 中创立一个 secret。如果您应用的是 Vault CLI,则能够应用以下命令创立一个 secret。确保您应用适当的策略从 CLI 登录到 vault 实例。
vault kv put secret/mysecret my-value=supersecret
  1. 创立一个 ExternalSecret CRD 来定义须要从 Vault 中获取的数据。
# vault-secret.yaml

apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: vault-example
spec:
  refreshInterval: '15s'
  secretStoreRef:
    name: vault-backend
    kind: SecretStore
  target:
    name: vault-example-sync
  data:
    - secretKey: secret-from-vault
      remoteRef:
        key: secret/mysecret
        property: my-value
  1. 将上述 CRD 利用到集群,它应该应用从 Vault 获取的数据创立一个名为 vault-example-sync 的 Kubernetes secret。
kubectl apply -f vault-backend.yaml
kubectl apply -f vault-secret.yaml

您能够应用 kubectl describe 命令查看集群中的 secret。

kubectl describe secret vault-example-sync

# output should have the below data
Name:         vault-example-sync
Namespace:    default
Labels:       <none>
Annotations:  reconcile.external-secrets.io/data-hash: ...

Type:  Opaque

Data
====
secret-from-vault:  16 bytes

如果您在创立 secret 时遇到问题,请查看 ExternalSecret 资源形容输入的 events 局部。

kubectl describe externalsecret vault-example

如果您看到权限谬误,请确保应用具备正确策略的令牌。

其余 secret managers

设置其余 secret 管理器与上述步骤相似。惟一的区别是 SecretStore CRD 和 ExternalSecret CRD 中的 remoteRef 局部。您能够在 ESO 文档中找到针对不同提供商的官网指南。


Secrets Store CSI Driver

Secrets Store CSI Driver 是一个原生的上游 Kubernetes 驱动程序,可用于从工作负载中形象出 secret 的存储地位。如果您想应用云提供商的 secret 管理器而不将 secret 公开为 Kubernetes secret 对象,您能够应用 CSI 驱动程序将 secret 作为卷装置在您的 pod 中。如果您应用云提供商来托管您的 Kubernetes 集群,这是一个很好的抉择。该驱动程序反对许多云提供商,并且能够与不同的 secret 管理器一起应用。

Secrets Store CSI Driver 是一个 daemonset 守护过程,它与 secret 提供者通信以检索 SecretProviderClass 自定义资源中指定的 secret。

它的工作原理如下;

  1. 创立一个 SecretProviderClassCRD 来定义从 secret 提供者获取的 secret 的详细信息。
  2. 在 pod 的 volume spec 中援用 SecretProviderClass
  3. 驱动程序将从 secret 提供者那里获取 secret,并在 pod 启动期间将其作为 tmpfs 卷挂载到 pod 中。该卷也将在 pod 删除后被删除。

驱动程序还能够同步对 secret 的更改。该驱动程序目前反对 Vault、AWS、Azure 和 GCP 提供商。Secrets Store CSI Driver 也能够将加密数据同步为 Kubernetes secret,只须要在装置期间明确启用此行为。

长处

  • secret 存储在平安的内部 secret 管理器中,而不是代码存储库中。
  • 使秘密与内部秘密管理器放弃同步。它还反对 secret 的轮换。
  • 与所有次要的内部 secret 管理者单干。
  • 将密钥作为卷装置在 pod 中,因而它们不会作为 Kubernetes secret 公开。它也能够配置为创立 Kubernetes secret。

毛病

  • 须要精心设置能力应用,并且比 ESO 更简单。
  • 应用比 ESO 更多的资源,因为它须要在每个节点上运行。
  • 依赖于内部 secret 存储及其拜访策略来确保安全。

应用 Google Secret Manager provider

让咱们看看如何配置 driver 以应用 Google Secret Manager (GSM) 作为 secret provider。

确保您应用的是启用了 Workload Identity 性能的 Google Kubernetes Engine (GKE) 集群。Workload Identity 容许 GKE 集群中的工作负载模仿身份和拜访治理 (IAM) 服务帐户来拜访 Google Cloud 服务。您还须要为我的项目启用 Kubernetes Engine API、Secret Manager API 和 Billing。如果未启用,gcloud CLI 会提醒您启用这些 API。

能够应用以下 gcloud CLI 命令创立启用了 Workload Identity 的新集群。

export PROJECT_ID=<your gcp project>
gcloud config set project $PROJECT_ID

gcloud container clusters create hello-hipster \
  --workload-pool=$PROJECT_ID.svc.id.goog

装置 Secrets Store CSI Driver

能够应用 Helm 命令在集群上装置 Secrets Store CSI 驱动程序:

helm repo add secrets-store-csi-driver https://kubernetes-sigs.github.io/secrets-store-csi-driver/charts

helm install csi-secrets-store \
    secrets-store-csi-driver/secrets-store-csi-driver \
    --namespace kube-system

这将在 kube-system 命名空间下装置驱动程序和 CRD。您还须要将所需的 provider 装置到集群中。

装置 GSM provider

让咱们将 GSM provider 装置到集群中:

kubectl apply -f https://raw.githubusercontent.com/GoogleCloudPlatform/secrets-store-csi-driver-provider-gcp/main/deploy/provider-gcp-plugin.yaml

创立 secret

首先,您须要设置一个工作负载身份服务帐户。

# Create a service account for workload identity
gcloud iam service-accounts create gke-workload

# Allow "default/mypod" to act as the new service account
gcloud iam service-accounts add-iam-policy-binding \
    --role roles/iam.workloadIdentityUser \
    --member "serviceAccount:$PROJECT_ID.svc.id.goog[default/mypodserviceaccount]" \
    gke-workload@$PROJECT_ID.iam.gserviceaccount.com

当初让咱们创立一个该服务帐户能够拜访的密钥。

# Create a secret with 1 active version
echo "mysupersecret" > secret.data
gcloud secrets create testsecret --replication-policy=automatic --data-file=secret.data
rm secret.data

# grant the new service account permission to access the secret
gcloud secrets add-iam-policy-binding testsecret \
    --member=serviceAccount:gke-workload@$PROJECT_ID.iam.gserviceaccount.com \
    --role=roles/secretmanager.secretAccessor

当初您能够创立一个 SecretProviderClass 资源,用于从 GSM 获取密钥。请记住将 $PROJECT_ID 替换为您的 GCP 我的项目 ID。

# secret-provider-class.yaml

apiVersion: secrets-store.csi.x-k8s.io/v1
kind: SecretProviderClass
metadata:
  name: app-secrets
spec:
  provider: gcp
  parameters:
    secrets: |
      - resourceName: "projects/$PROJECT_ID/secrets/testsecret/versions/latest"
        path: "good1.txt"
      - resourceName: "projects/$PROJECT_ID/secrets/testsecret/versions/latest"
        path: "good2.txt"

创立一个 Pod

当初您能够创立一个 pod 去应用该 SecretProviderClass 资源从 GSM 获取密钥。请记住将 $PROJECT_ID 替换为您的 GCP 我的项目 ID。

# my-pod.yaml

apiVersion: v1
kind: ServiceAccount
metadata:
  name: mypodserviceaccount
  namespace: default
  annotations:
    iam.gke.io/gcp-service-account: gke-workload@$PROJECT_ID.iam.gserviceaccount.com
---
apiVersion: v1
kind: Pod
metadata:
  name: mypod
  namespace: default
spec:
  serviceAccountName: mypodserviceaccount
  containers:
    - image: gcr.io/google.com/cloudsdktool/cloud-sdk:slim
      imagePullPolicy: IfNotPresent
      name: mypod
      resources:
        requests:
          cpu: 100m
      stdin: true
      stdinOnce: true
      terminationMessagePath: /dev/termination-log
      terminationMessagePolicy: File
      tty: true
      volumeMounts:
        - mountPath: '/var/secrets'
          name: mysecret
  volumes:
    - name: mysecret
      csi:
        driver: secrets-store.csi.k8s.io
        readOnly: true
        volumeAttributes:
          secretProviderClass: 'app-secrets'

将上述资源利用到集群中。

kubectl apply -f secret-provider-class.yaml
kubectl apply -f my-pod.yaml

期待 pod 启动,而后 exec 进入 pod 查看挂载文件的内容。

kubectl exec -it mypod /bin/bash
# execute the below command in the pod to see the contents of the mounted secret file
root@mypod:/# cat /var/secrets/good1.txt

其余 secret 管理器

您能够找到服务提供商的相似指南:AWS CSI provider、Azure CSI provider 和 Vault CSI provider。


论断

Sealed Secrets 是小型团队和我的项目在 Git 中爱护 secret 的绝佳解决方案。对于较大的团队和我的项目,External Secrets OperatorSecrets Store CSI Driver 是平安治理密钥的更好的解决方案。External Secrets Operator 能够与许多 secret 管理系统一起应用,并不限于上述零碎。当然,这应该与 RBAC 一起应用,以避免非管理员读取集群中的 secret。Secrets Store CSI Driver 可能比 ESO 波及更多,但它是一个更原生的解决方案。

退出移动版