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

Posted

技术标签:

【中文标题】如何处理 Google App Engine 中的秘密?【英文标题】:How to handle secrets in Google App Engine? 【发布时间】:2020-02-10 18:58:43 【问题描述】:

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

最好的方法是什么?

我得到的这个问题的答案之一是use 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(1、2)。我没有使用 GCB,我怀疑我会,因为它太基础了。

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

【问题讨论】:

【参考方案1】:

[更新](截至 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 (您可能希望从版本控制中排除密钥文件。)

【讨论】:

这些可以在构建时使用吗?我正在使用 gitlab,它有 .gitlab-ci.yml 文件,负责部署,由谷歌云构建进一步处理。详细查询是这样的:***.com/questions/65265583/… @MrAJ - 您可以在构建时使用它,只需确保在调用可能需要它们的任何其他构建/初始化函数之前已成功检索到所需的任何秘密,这应该更容易一个管道而不是运行时。但是,在您的情况下使用 Gitlab secrets 可能更直接,除非您只想将它​​们存储在一个地方或不想通过 Gitlab 存储它们。 如何将秘密连接到清单 env_variables?我正在运行现成的容器,所以我不能在那里添加自定义代码。【参考方案2】:

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

[更新]

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

[结束更新]

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

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

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

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

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

注意:以下示例使用 Windows 语法。对于 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 Keyring 和 Keyname。

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 Decrypt

Java Samples

【讨论】:

感谢您的回答。这不会使 App Engine 变得毫无用处吗? Spring Boot、Django、Rails,它们都在启动时读取环境变量以连接到不同的数据库和服务。我试图避免使用 Kubernetes,因为它要复杂得多。 这是一个疯狂的声明。 App Engine Standard 是一种低成本的无服务器解决方案。你希望你的环境变量是秘密的。 App Engine 不提供该功能。这并不会使该服务对使用它的其他数百万客户毫无用处。今天最好的答案是使用提供您需要的功能的服务,或者添加您需要的功能。 Spring Boot 无法处理加密环境变量的问题是应该添加到 Spring Boot 中的一个特性,或者 Spring Boot 应该允许应用程序控制 init 进程。 你的意思是 Spring Boot、Rails、Django ......这是初始化框架的一种非常标准的方式,我还没有与没有做奇怪/坏事的人交谈,例如拥有存储库中的凭据或将 app.yaml 保留在存储库之外,从而限制了可以部署的人员。 @pupeno - 你提到的这些框架应该为机密和云供应商提供支持。【参考方案3】:

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

今天它是用 Go 编写的,它不兼容 Java。 I wrote a python library 给一些同事。如果您打算使用它,我可以编写一个 Java 包。难度不大。

告诉我

【讨论】:

【参考方案4】:

Berglas 看起来很有趣。

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

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

优点:

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

缺点:

您无法真正删除某个人的访问权限(因为文件总是可以被复制),因此您有时需要轮换机密 可能很难将未加密的文件保留在存储库之外。那些你习惯了它并且忽略文件和/或脚本的人,通常没问题。

【讨论】:

Berglas 不只适用于 Go 项目吗? 不,您可以将berglas exec 用于任何语言或流程。

以上是关于如何处理 Google App Engine 中的秘密?的主要内容,如果未能解决你的问题,请参考以下文章

如何处理 google firebase web app 的安全性?

在 Google App Engine 上处理 db.Timeout

如何处理 Google Map Direction API 中的 0,0 标记错误?

如何处理异步函数 UWP App GetFileFromPathAsync(path) 中的异常;

如何处理 Shiny App 部署中的文件计数超出限制错误 [关闭]

使用本地、纱线工作区依赖项和生产环境(例如 App Engine)环境变量构建?