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

Posted

技术标签:

【中文标题】如何使用 Spring Boot 将来自 Google Secret Manager 的秘密作为环境变量注入 Kubernetes Pod?【英文标题】:How to inject secret from Google Secret Manager into Kubernetes Pod as environment variable with Spring Boot? 【发布时间】:2021-01-03 11:34:01 【问题描述】:

为了布莱恩的一生,我该怎么做?

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

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

这是我想到的一个选项:

在我的部署中,我添加了一个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 时,我可以跳过使用机密管理器。与VaultIMO 相同。

【问题讨论】:

你需要使用像 Berglas 这样的工具:github.com/GoogleCloudPlatform/berglas 【参考方案1】:

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

第一个是secret-init:

secrets-init 是一个简约的初始化系统,旨在作为 PID 1 运行 在容器环境中,它与 多个秘密管理器服务,例如Google Secret Manager

第二个是kube-secrets-init:

kube-secrets-init 是一个 Kubernetes mutating admission webhook, 使任何使用特殊前缀环境的 K8s Pod 发生变异 变量,直接或来自 Kubernetes 作为 Secret 或 ConfigMap。

它还支持与 Google Secret Manager 的集成:

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

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

【讨论】:

这两个系统在我的应用程序中都不适合我。我都试过了。他们都出错了,日志中没有指定错误。 我最终使用了github.com/external-secrets/kubernetes-external-secrets。【参考方案2】:

如何从这里将它放入我的应用程序容器中?

您可以使用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: 

【讨论】:

我如何将它读回 Spring Boot 应用程序? 我没有在实践中尝试过,但是你可以使用spring.cloud.kubernetes.config.paths从文件系统中读取它或者实现一个PropertySource【参考方案3】:

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

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

【讨论】:

你试过了吗?这适用于工作负载身份吗?谢谢! 我将它用于服务帐户,而不是工作负载身份。我有 terraform 代码,它为它创建服务帐户、密钥并将密钥作为秘密存储在 k8s 中,然后应用程序使用该秘密来配置任何 spring-cloud-gcp 项目【参考方案4】:

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

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

【讨论】:

如何在 Spring Boot 端将其读回环境变量? 受***.com/a/20909045/3849555 启发,在启动spring-boot 之前必须阅读该文件。所以在 cmd 中应该在 java -jar xxx.jar:export $(grep -v '^#' /blah/blah/super-secrets.env | xargs -d '\n') 之前有这一行,然后其余的读取变量,就像 spring-boot 一样。

以上是关于如何使用 Spring Boot 将来自 Google Secret Manager 的秘密作为环境变量注入 Kubernetes Pod?的主要内容,如果未能解决你的问题,请参考以下文章

如何在嵌入式 tomcat 服务器上部署 Spring Boot Web 应用程序,来自 Spring Boot 本身

Thymeleaf + Spring (not Boot) - 如何显示来自 messageSource 的消息

spring-boot-starter-web 中默认的 JSON 错误响应来自哪里,如何调整?

如何使用spring boot后端使角度应用程序仅接收来自局域网的请求?

来自服务类的 Spring Boot 方法调用问题

如何在 Thymeleaf + spring boot 控制器中显示来自 Flux 的数据