22

我的应用程序需要一堆秘密才能运行:数据库凭据、API 凭据等。它在 Google App Engine 标准 Java 11 中运行。我需要这些秘密作为环境变量或应用程序的参数,以便我的框架可以获取它们并相应地建立连接。我的特定框架是 Spring Boot,但我相信 Django、Rails 和许多其他人使用相同的方法。

这样做的最佳方法是什么?

我对这个问题的答案之一是使用 Google Cloud Key Management,这看起来很有希望,但我不知道如何将这些值转换为 App Engine 中的环境变量。可能吗?我已阅读 Setting Up Authentication for Server to Server Production Applications,但我没有看到任何关于如何在 App Engine 中将机密转换为环境变量的指示(我错过了吗?)。

我见过的其他替代方案包括将它们硬编码到app.yaml另一个从未提交并存在于我的机器中的文件中,这意味着我是唯一可以部署的人......我什至无法从另一台机器部署。这对我来说是个问题。

我见过的另一个潜在解决方案是将问题委托给 Google Cloud Build,以便它从 CKM 获取值/文件并将其推送到 App Engine ( 12 )。我没有使用 GCB,我怀疑我会,因为它是如此基本。

我真的希望 App Engine 有一个像 Heroku 一样的环境变量页面。

4

5 回答 5

17

[更新](截至 2020 年 2 月)GCP 的 Secret Manager处于测试阶段,请参阅:

https://cloud.google.com/secret-manager/docs/overview

对于特定于 Java 的实现,请参阅: https ://cloud.google.com/secret-manager/docs/creating-and-accessing-secrets#secretmanager-access-secret-version-java

您的具体解决方案将取决于您的应用程序的设置方式,但您应该能够访问密钥并使用这些值创建环境变量,或者将它们传递给您的应用程序。

您可以使用 GCP IAM 创建服务帐户来管理访问权限或Secret Manager Secret Accessor向现有成员/服务添加角色(例如,在这种情况下,我将该权限添加到App Engine default service account)。

我在 GAE 标准上使用 Node.js 进行了尝试,它似乎运行良好;我没有做任何性能测试,但应该没问题,特别是如果您主要需要应用程序启动或构建过程的一部分的秘密。

对于本地(非 GCP)开发/测试,您可以创建具有适当秘密管理器权限的服务帐户并获取 json 服务密钥。然后设置一个名为GOOGLE_APPLICATION_CREDENTIALS文件路径的环境变量,例如:

export GOOGLE_APPLICATION_CREDENTIALS=/path/to/local_service_key.json

并且在该 shell 会话中运行的应用程序应该无需任何额外的身份验证代码即可获得权限。请参阅:https ://cloud.google.com/docs/authentication/getting-started (您可能希望从版本控制中排除密钥文件。)

于 2020-02-15T19:16:02.953 回答
4

目前,App Engine Standard Standard 没有 Google 提供的用于存储应用程序机密的解决方案。

[更新]

我注意到您对另一个答案的评论,即在您拥有应用程序控制之前,您需要环境变量有效。在这种情况下,您今天对 App Engine 没有任何选择。我将部署到更适合您可以提供托管机密的系统目标的不同服务 (Kubernetes)。

[结束更新]

对于 App Engine 标准版,您有两种密钥选择:

  1. 将秘密作为环境变量存储在 app.yaml 中
  2. 将秘密存储在其他地方。

对于这两个选项,您都可以通过加密来增加一层安全性。但是,添加加密会添加另一个秘密(解密密钥),您必须以某种方式将其提供给您的应用程序。先有鸡还是先有蛋的情况。

App Engine 标准版使用服务帐号。此服务帐户可用作身份来控制对其他资源的访问。其他资源的示例包括 KMS 和 Cloud Storage。这意味着您可以安全地访问 KMS 或 Cloud Storage,而无需向 App Engine 添加其他密钥。

假设您的公司希望加密所有应用程序机密。我们可以使用 App Engine 服务帐号作为授权访问 KMS 的身份,获取单个密钥。

注意:以下示例使用 Windows 语法。将续行替换^\for Linux/macOS。

创建 KMS 密钥环。密钥环无法删除,因此这是一次性操作。

set GCP_KMS_KEYRING=app-keyring
set GCP_KMS_KEYNAME=app-keyname

gcloud kms keyrings create %GCP_KMS_KEYRING% --location global

创建 KMS 密钥。

gcloud kms keys create %GCP_KMS_KEYNAME% ^
--location global ^
--keyring %GCP_KMS_KEYRING% ^
--purpose encryption

将服务帐户添加到我们创建的密钥环和密钥的 KMS 策略中。

这将允许 App Engine 在不需要 KMS 机密的情况下解密数据。服务帐户身份提供访问控制。KMS 不需要任何角色。您需要提供可包含在 app.yaml 中的 KMS 密钥环和密钥名称。

set GCP_SA=<replace with the app engine service acccount email adddress>
set GCP_KMS_ROLE=roles/cloudkms.cryptoKeyDecrypter

gcloud kms keys add-iam-policy-binding %GCP_KMS_KEYNAME% ^
--location global ^
--keyring %GCP_KMS_KEYRING% ^
--member serviceAccount:%GCP_SA% ^
--role %GCP_KMS_ROLE%

对于此示例,假设您需要访问 MySQL 数据库。我们将凭证存储在 JSON 文件中并对其进行加密。该文件名为config.json.

{
        "DB_HOST": "127.0.0.1",
        "DB_PORT": "3306",
        "DB_USER": "Roberts",
        "DB_PASS": "Keep-This-Secret"
}

使用 Cloud KMS 加密 config.json 并将加密结果存储在 config.enc 中:

call gcloud kms encrypt ^
--location=global ^
--keyring %GCP_KMS_KEYRING% ^
--key=%GCP_KMS_KEYNAME% ^
--plaintext-file=config.json ^
--ciphertext-file=config.enc

加密后的文件可以存储在云存储中。由于它是加密的,您可以将文件与构建文件一起存储,但我不建议这样做。

最后一步是用 Java 编写代码,该代码是您的程序的一部分,该程序使用 KMS 使用 KMS 解密文件 config.enc。谷歌有很多 KMS 解密的例子:

Java KMS 解密

Java 示例

于 2019-10-17T00:22:54.110 回答
1

您可以在构建时将机密作为环境变量传递。此示例检索 Stripe API 密钥并更新 Cloud Build 中的 app.yaml,确保本地文件不会意外签入源代码控制

首先确保 CloudBuild 服务帐户具有 IAM 角色Secret Manager Secret Accessor

一个 app.dev.yaml 文件,其中包含 env 变量的占位符

runtime: python39
env: standard

instance_class: F4

automatic_scaling:
  max_instances: 1

env_variables:
  STRIPE_API_KEY: STRIPE_API_VAR

etc
etc

Cloudbuild.yaml 检索秘密并在构建时插入

steps:
- name: 'gcr.io/google.com/cloudsdktool/cloud-sdk'
  entrypoint: 'bash'
  args:
      - -c
      - |
        echo 'my api key from secret manager is '$$STRIPE_API_VAR
        sed -i "s|STRIPE_API_VAR|$$STRIPE_API_VAR|g" app.dev.yaml
        cat app.dev.yaml # you can now see the secret value inserted as the env variable
        gcloud app deploy --appyaml=app.dev.yaml # deploy with the updated app.yaml, the local copy of the file is not changed
  secretEnv: ['STRIPE_API_VAR']

availableSecrets:
  secretManager:
  - versionName: projects/$PROJECT_ID/secrets/stripe-api-key/versions/latest
    env: 'STRIPE_API_VAR'
于 2022-01-01T05:33:34.083 回答
0

贝格拉斯看起来很有趣。

另一种选择是将秘密放在 app.yaml 文件中(您可以拥有多个文件)并在将其提交给版本控制之前对其进行加密。

有许多工具可以在将机密放入版本控制之前对其进行加密,例如https://github.com/StackExchange/blackbox

优点:

  • 用途广泛
  • 与其他选项相比,我发现它很容易理解
  • 易于上手

缺点:

  • 你不能真正删除一个人的访问权限(因为文件总是可以被复制)所以你有时会轮换秘密
  • 可能很难将未加密的文件保留在存储库之外。那些你习惯了它并且忽略文件和/或脚本的人,通常没问题。
于 2019-10-23T07:24:01.917 回答
-1

对于秘密管理,我个人是Berglas项目的粉丝。它基于 KMS,此外还管理 DEK 和 KEK

它今天是用 Go 编写的,并且不符合 Java。我为一些同事写了一个 python 库。如果您打算使用它,我可以编写一个 Java 包。这不是很难。

让我知道

于 2019-10-15T05:23:21.397 回答