如何使用 Spring Boot 将来自 Google Secret Manager 的秘密作为环境变量注入 Kubernetes Pod?

How to inject secret from Google Secret Manager into Kubernetes Pod as environment variable with Spring Boot?

为了 Bryan 的生命,我该怎么做?

Terraform 用于在 GCP 中创建一个 SQL 服务器实例。 随机生成root密码和用户密码,然后放入Google Secret Manager。 数据库的 IP 通过私有 DNS 区域公开。

我现在如何获取用户名和密码以访问我的 K8s 集群中的数据库? 运行 Spring 在此处启动应用程序。

这是我想到的一个选项:

在我的部署中,我添加了一个 initContainer:

- name: secrets
  image: gcr.io/google.com/cloudsdktool/cloud-sdk
  args: 
  - echo "DB_PASSWORD=$(gcloud secrets versions access latest --secret=\"$NAME_OF_SECRET\")" >> super_secret.env

好的,现在怎么办?我如何从这里将它放入我的应用程序容器?

还有像 bitnami/sealed-secrets 这样的选项,我不喜欢,因为设置已经在使用 Terraform 并将机密保存在 GCP 中。使用 sealed-secrets 时,我可以跳过使用机密管理器。与 Vault IMO 相同。

How do I get it into my application container from here?

您可以使用 volume 来存储秘密并在初始容器和主容器中安装相同的卷,以便与来自初始容器的主容器共享秘密。

apiVersion: v1
kind: Pod
metadata:
  name: my-app
spec:
  containers:
  - name: my-app
    image: my-app:latest
    volumeMounts:
    - name: config-data
      mountPath: /data
  initContainers:
  - name: secrets
    image: gcr.io/google.com/cloudsdktool/cloud-sdk
    args: 
    - echo "DB_PASSWORD=$(gcloud secrets versions access latest --secret=\"$NAME_OF_SECRET\")" >> super_secret.env
    volumeMounts:
    - name: config-data
      mountPath: /data
  volumes:
  - name: config-data
    emptyDir: {}

使用 emptyDir 的 volumesmedium: Memory 来保证秘密不会被持久化。

...
volumes:
      - name: scratch
        emptyDir:
          medium: Memory
          sizeLimit: "1Gi"
...

除了评论中的其他答案和建议之外,我还想推荐两个您可能会感兴趣的工具。

第一个是secret-init:

secrets-init is a minimalistic init system designed to run as PID 1 inside container environments and it`s integrated with multiple secrets manager services, e.x. Google Secret Manager

第二个是kube-secrets-init:

The kube-secrets-init is a Kubernetes mutating admission webhook, that mutates any K8s Pod that is using specially prefixed environment variables, directly or from Kubernetes as Secret or ConfigMap.

它还支持与 Google Secret Manager 集成:

用户可以将 Google 秘密名称(前缀为 gcp:secretmanager:)作为环境变量值。 secrets-init 将使用指定名称将任何环境值解析为引用的秘密值。

这里有一个很好的article关于它是如何工作的。

您可以使用 spring-cloud-gcp-starter-secretmanager 从 Spring 应用程序本身加载机密。

文档 - https://cloud.spring.io/spring-cloud-gcp/reference/html/#secret-manager

如果可以控制图像,可以更改入口点并使用 berglas

Docker 文件:

FROM adoptopenjdk/openjdk8:jdk8u242-b08-ubuntu  # or whatever you need

# Install berglas, see https://github.com/GoogleCloudPlatform/berglas
RUN mkdir -p /usr/local/bin/
ADD https://storage.googleapis.com/berglas/main/linux_amd64/berglas /usr/local/bin/berglas
RUN chmod +x /usr/local/bin/berglas

ENTRYPOINT ["/usr/local/bin/berglas", "exec", "--"]

现在我们构建容器并测试它:

docker build -t image-with-berglas-and-your-app .
docker run \
    -v /host/path/to/credentials_dir:/root/credentials \
    --env GOOGLE_APPLICATION_CREDENTIALS=/root/credentials/your-service-account-that-can-access-the-secret.json \
    --env SECRET_TO_RESOLVE=sm://your-google-project/your-secret  \
    -ti image-with-berglas-and-your-app env

这应该用 sm:// 替换为实际的秘密值来打印环境变量。

在K8s中我们运行使用Workload Identity,所以调度pod所代表的K8s服务账号需要绑定一个Google有访问权限的服务账号秘密。

最后你的广告连播描述应该是这样的:

apiVersion: v1
kind: Pod
metadata:
  name: your-app
spec:
  containers:
  - name: your-app
    image: image-with-berglas-and-your-app
    command: [start-sql-server]
    env:
      - name: AXIOMA_PASSWORD
        value: sm://your-google-project/your-secret