【问题标题】:Google Cloud Functions - How to securely store service account private key when using Google Source Repository?Google Cloud Functions - 使用 Google Source Repository 时如何安全地存储服务帐户私钥?
【发布时间】:2018-02-03 22:05:20
【问题描述】:

我使用 Google Source Repository 来存储我的 Google Cloud Functions。 (基本上由 Google 托管的 Git 存储库)

我的一个功能需要访问私人 Google 表格文件,因此我创建了一个服务帐户。 (权限太多了,因为很难理解我们应该为服务帐户赋予什么确切的权限,而且以后很难更新,但我离题了)

现在,出于显而易见的原因,显然不建议将服务帐户 JSON 文件存储在 git 存储库本身中。这是它的样子(从值中剥离)

{
  "type": "service_account",
  "project_id": "",
  "private_key_id": "",
  "private_key": "",
  "client_email": "",
  "client_id": "",
  "auth_uri": "",
  "token_uri": "",
  "auth_provider_x509_cert_url": "",
  "client_x509_cert_url": ""
}

我一直在寻找环境变量来配置函数或类似的东西,但没有找到任何东西。跟踪密钥(因此可能在多个存储库中复制该文件)听起来确实不是一个好主意。但我还没有找到任何“正确”的方法来做到这一点。而且由于 Google Functions 的工作方式,除了环境变量,我想不出其他任何东西。

【问题讨论】:

    标签: security google-cloud-functions google-cloud-source-repos


    【解决方案1】:

    我在使用服务帐号的云功能时的解决方案是:

    1. 使用 Cloud KMS/vault 加密您的服务帐号凭据 json 文件并将其上传到 Cloud Storage。
    2. 从 Cloud Storage 获取服务帐号凭据 json 文件,并使用具有加密/解密权限的 Cloud KMS 服务帐号对其进行解密。

    3. 在运行时解析服务账户凭证json文件,得到private_keyclient_emailprojectId

    4. 将这三个秘密变量传递给客户端库

    我们将配置变量存储为云功能的环境变量,它们是纯文本,但没关系。因为它们不是秘密。

    我们不能存储诸如纯文本之类的秘密内容,例如云函数环境变量。

    【讨论】:

    • 这似乎是迄今为止最全面和最可靠的答案,因此我将其标记为“答案”。自从我很久以前切换到 AWS 以来,我还没有测试它,也不会测试它。未来的读者:如果这个答案不能回答问题,那么请在 cmets 中让我知道(ping),我将对其进行审查。 :)
    • 仅当您需要使用特定服务帐户 JSON 文件创建客户端时才需要。例如 FCM 客户端。如果您的 Google 函数使用从当前上下文获取凭据的 API,那么此答案适合您。 stackoverflow.com/questions/55671256/…
    【解决方案2】:

    您可以将服务帐户文件与您的函数一起上传,并在您的代码中使用它。它将在那里保持安全。大多数开发人员将使用 .gitignore 或等效机制来防止将该文件添加到源代码管理中。有一个从Firebase samples 加载服务帐户凭据的示例。 (如果您不使用 Firebase SDK,则必须注意将函数定义转换为 Cloud 样式。

    您也可以使用环境变量,但在引用和转义值时必须特别小心,以确保它们正确地到达您的函数。这有点麻烦,但可行。

    【讨论】:

    • 所以,如果我理解正确,您的解决方案是以某种方式在 Google 函数中部署 service-account-credentials.json 文件而不将其添加到 Google 源代码库,依靠 firebase/gcloud CLI? (我确实不使用 Firebase。你将如何使用环境变量?你能添加一个示例或文档吗?谢谢。
    • 是的,这就是我要说的。 Firebase CLI 的文档:firebase.google.com/docs/cli
    • 嘿@DougStevenson!我实际上对这里的 Firebase SDK 参考有点困惑。您是否实质上建议将服务帐户密钥作为云功能的一部分部署到 GCP?
    • 这正是我对我的项目所做的,该项目根据我众多环境中的服务帐户凭据初始化应用程序。我在我的服务帐户 JSON 文件夹中添加了一个 .gitignore 条目。我直接部署云功能。
    【解决方案3】:

    截至 2020 年 1 月,Google 已经发布了Secret Manager,其中is described as

    Secret Manager 是一项新的 Google Cloud 服务,它提供了一种安全便捷的方法来存储 API 密钥、密码、证书和其他敏感数据。 Secret Manager 提供了一个中心位置和单一事实来源,用于管理、访问和审核 Google Cloud 中的机密信息。

    对于 Cloud Functions,有一个教程 here 介绍了如何创建密钥,然后从云函数中检索它。

    【讨论】:

    • 请注意,即使在使用 Secret Manager 时,人们也应该质疑“但是我应该在哪里存储访问 Secret Manager 的凭据?”或“如何确定谁可以访问这些秘密?”。您链接的所有示例都依赖 Google 的 ADC 机制来访问机密,该机制使用云功能的服务帐户来处理初始身份验证。您的回答是通用凭据(超出 GCP 范围)的 @slideshowp2 解决方案的一个很好的替代品,但要访问 GCP 资源,我认为人们应该完全依赖 ADC。
    【解决方案4】:

    Here 您可以找到如何使用环境变量GOOGLE_APPLICATION_CREDENTIALS 为您的应用程序提供凭据。

    【讨论】:

    • 这是一个解决方案,但不适用于 Cloud Functions,因为您无法控制托管系统。您可以在本地机器或您拥有的服务器中使用它,但不能使用云功能。另外,我不喜欢它,因为如果您需要使用几个不相关的不同凭据(例如几个服务,每个服务都提供需要自己凭据的东西),那么您就不能使用它。 (除非你制作一个包含所有这些凭据的大凭据,但......微服务,对吧?)
    • 使用环境变量 GOOGLE_APPLICATION_CREDENTIALS 是您的偏好。您还可以将您的凭证文件保存在存储桶上(提供适当的访问级别)并解决它。然后,您可以在您的代码中手动获取并提供服务帐户凭据,如本文档中所述:cloud.google.com/docs/authentication/…
    • 每个人都知道谷歌云文档的链接,但只有在本地运行应用程序时才有效。问题是文档没有解释如何在生产中设置 json 文件。
    • 部署函数时使用本地的credentials.json文件设置云函数的GOOGLE_APPLICATION_CREDENTIALS环境变量怎么样?
    【解决方案5】:

    这就是我解决这个问题的方法。首先在文件keys.js中创建一个逻辑来判断你是在开发还是生产(并创建相应的./dev.js和./prod.js文件,你应该在.ignore文件中包含./dev.js到确保它没有上传到你的 github 远程):

    if (process.env.NODE_ENV === "production") {
      module.exports = require("./prod");
    } else {
      module.exports = require("./dev");
    }
    

    其次,您需要上述逻辑所在的 keys.js 文件,并根据从 keys.js 收到的数据创建凭证对象:

    const credentials = {
      type: keys.googleType,
      project_id: keys.googleProjectId,
      private_key_id: keys.googlePrivateKeyId,
      private_key: keys.googlePrivateKey,
      client_email: keys.googleClientEmail,
      client_id: keys.googleClientId,
      auth_uri: keys.googleAuthUri,
      token_uri: keys.googleTokenUri,
      auth_provider_x509_cert_url: keys.googleAuthProviderX509CertUrl,
      client_x509_cert_url: keys.googleClientX509CertUrl
    };
    

    现在,对于每个谷歌云服务,您都可以使用以下示例模式:

      const storage = new Storage({
        project_id: credentials.project_id,
        credentials
      });
      const client = new textToSpeech.TextToSpeechClient({
        project_id: credentials.project_id,
        credentials
      });
    ...
    etc.
    

    【讨论】:

      【解决方案6】:

      我认为您实际上不需要在代码中存储任何密钥文件。

      您的函数使用指定的服务帐户(通常是“App Engine 默认服务帐户”,但您可以在高级设置中进行更改)运行。如果您有特定需求,您应该为您的功能创建一个特定的服务帐户,并为其授予所需的所有权限。

      在您的函数中,身份验证将使用Application Default Credentials 自动进行,因此您不必关心任何事情(忘记环境变量、密钥文件或任何事情)。只需确保您使用适用于您的语言的 Google Cloud 客户端库,它们会为您隐式处理所有内容。

      特别是,我避免像@slideshowp2 提出的那样创造性的解决方案。但我同意它们有它的用途(例如,假设我需要存储外部系统的凭据,超出 GCP 范围。在这种情况下,他的解决方案可能是一种可行的方法),但是只使用 Google 服务,让我们保留它很简单。

      【讨论】:

        猜你喜欢
        • 2015-01-27
        • 1970-01-01
        • 1970-01-01
        • 2020-10-04
        • 2015-11-11
        • 1970-01-01
        • 2022-06-30
        • 1970-01-01
        • 2018-12-12
        相关资源
        最近更新 更多