如何处理 Google App Engine 中的机密?

pup*_*eno 20 google-app-engine google-cloud-platform google-cloud-kms google-secret-manager

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

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

我对这个问题的答案之一是使用 Google Cloud Key Management,这看起来很有希望,但我不知道如何将这些值转换为 App Engine 中的环境变量。是否可以?我已经阅读 了为服务器到服务器生产应用程序设置身份验证,但我没有看到任何关于如何在 App Engine 中将机密转换为环境变量的指示(我错过了吗?)。

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

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

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

ldg*_*ldg 14

[更新](截至 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)开发/测试,您可以创建一个具有适当 secret manager 权限的服务帐户并获取 json 服务密钥。然后设置一个命名GOOGLE_APPLICATION_CREDENTIALS为文件路径的环境变量,例如:

export GOOGLE_APPLICATION_CREDENTIALS=/path/to/local_service_key.json
Run Code Online (Sandbox Code Playgroud)

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


ros*_*sco 7

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

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

带有 env 变量占位符的 app.dev.yaml 文件

runtime: python39
env: standard

instance_class: F4

automatic_scaling:
  max_instances: 1

env_variables:
  STRIPE_API_KEY: STRIPE_API_VAR

etc
etc
Run Code Online (Sandbox Code Playgroud)

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'
Run Code Online (Sandbox Code Playgroud)


Joh*_*ley 5

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

\n\n

[更新]

\n\n

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

\n\n

[结束更新]

\n\n

对于 App Engine Standard,您有两种密钥选择:

\n\n
    \n
  1. 将机密存储为 app.yaml 中的环境变量
  2. \n
  3. 将秘密存储在其他地方。
  4. \n
\n\n

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

\n\n

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

\n\n

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

\n\n

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

\n\n

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

\n\n
set GCP_KMS_KEYRING=app-keyring\nset GCP_KMS_KEYNAME=app-keyname\n\ngcloud kms keyrings create\xc2\xa0%GCP_KMS_KEYRING% --location global\n
Run Code Online (Sandbox Code Playgroud)\n\n

创建 KMS 密钥。

\n\n
gcloud kms keys create\xc2\xa0%GCP_KMS_KEYNAME% ^\n--location global ^\n--keyring\xc2\xa0%GCP_KMS_KEYRING% ^\n--purpose encryption\n
Run Code Online (Sandbox Code Playgroud)\n\n

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

\n\n

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

\n\n
set GCP_SA=<replace with the app engine service acccount email adddress>\nset GCP_KMS_ROLE=roles/cloudkms.cryptoKeyDecrypter\n\ngcloud kms keys add-iam-policy-binding\xc2\xa0%GCP_KMS_KEYNAME% ^\n--location global ^\n--keyring\xc2\xa0%GCP_KMS_KEYRING% ^\n--member serviceAccount:%GCP_SA% ^\n--role\xc2\xa0%GCP_KMS_ROLE%\n
Run Code Online (Sandbox Code Playgroud)\n\n

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

\n\n
{\n        "DB_HOST": "127.0.0.1",\n        "DB_PORT": "3306",\n        "DB_USER": "Roberts",\n        "DB_PASS": "Keep-This-Secret"\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

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

\n\n
call gcloud kms encrypt ^\n--location=global ^\n--keyring\xc2\xa0%GCP_KMS_KEYRING% ^\n--key=%GCP_KMS_KEYNAME% ^\n--plaintext-file=config.json ^\n--ciphertext-file=config.enc\n
Run Code Online (Sandbox Code Playgroud)\n\n

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

\n\n

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

\n\n

Java KMS 解密

\n\n

Java 示例

\n

  • 这是一个疯狂的说法。App Engine Standard 是一种低成本的无服务器解决方案。您希望您的环境变量是秘密的。App Engine 不提供该功能。但这并不意味着该服务对于其他使用该服务的数百万客户来说毫无用处。今天最好的答案是使用提供您需要的功能的服务,或者添加您需要的功能。Spring Boot 无法处理加密环境变量的问题是应该添加到 Spring Boot 中的一个功能,或者 Spring Boot 应该允许应用程序控制 init 进程。 (3认同)
  • 感谢你的回答。这不会让 App Engine 变得毫无用处吗?Spring Boot、Django、Rails,它们都在启动时读取环境变量以连接到不同的数据库和服务。我试图避免使用 Kubernetes,因为它要复杂得多。 (2认同)
  • 你的意思是 Spring Boot、Rails、Django...这是初始化框架的一种非常标准的方式,我还没有和那些没有做一些奇怪/坏事的人交谈过,比如在存储库中拥有凭证或保留应用程序。 yaml 脱离存储库,限制谁可以部署。 (2认同)

gui*_*ere -1

对于秘密管理,我个人很喜欢Berglas项目。它基于KMS,此外还管理DEK和KEK

今天它是用 Go 编写的,与 Java 不兼容。为一些同事写了一个 python 库。如果你打算使用它,我可以写一个Java包。这不是很难。

让我知道