如何在 GKE (Go) 中为 Google Cloud Storage 对象生成签名 URL

Str*_*ter 4 go google-cloud-storage google-cloud-platform google-kubernetes-engine

目标:

在 GKE Pod 内生成签名 URL,无需手动注入服务帐户 JSON 密钥。生成它们的语法需要服务帐户电子邮件和私钥。

//import "cloud.google.com/go/storage"
url, err := storage.SignedURL(bucketName, objectName, &storage.SignedURLOptions{
    ContentType:    contentType,
    GoogleAccessID: saEmail,
    PrivateKey:     saPrivateKey,
})
Run Code Online (Sandbox Code Playgroud)

换句话说,我想加载saEmailGKEsaPrivateKey节点中自动可用的默认凭据。

试图:

ctx := context.Background()
//errors ignored for brevity
//import "golang.org/x/oauth2/google"
creds, _ := google.FindDefaultCredentials(ctx, storage.ScopeReadWrite)
cfg, _ := google.JWTConfigFromJSON(creds.JSON)
url, _ := storage.SignedURL(bucketName, objectName, &storage.SignedURLOptions{
    ContentType:    contentType,
    GoogleAccessID: cfg.Email,
    PrivateKey:     cfg.PrivateKey,
})
Run Code Online (Sandbox Code Playgroud)

当我google.FindDefaultCredentials()在 GKE pod 中运行时,结果 JSON 为空。

环境:

  • Go1.13
  • GKE1.14.10-gke.36
  • cloud.google.com/gov0.58.0
  • cloud.google.com/go/storagev1.8.0

补充笔记:

我已经测试了两种可能的替代方案,涉及将服务帐户密钥 (JSON) 手动注入 pod,但我希望尽可能避免使用它们:

  • 将服务帐户密钥写入文件并设置GOOGLE_APPLICATION_CREDENTIALS为其路径。完成后,google.FindDefaultCredentials()加载电子邮件和私钥。

  • 将服务帐户密钥作为字符串传递到 pod 中并使用 解析它google.JWTConfigFromJSON()。

gui*_*ere 5

要生成签名 URL,您需要拥有私钥。

当您使用 GCP 服务(这里是计算实例,即 K8S 集群的节点,但与 Cloud RUn、Cloud Functions 等其他 GCP 服务相同)并且使用默认凭据(并且没有定义 GOOGLE_APPLICATION_CREDENTIALS env var)时),图书馆使用元数据服务器。

元数据服务器允许您生成访问令牌

curl -H "Metadata-Flavor: Google" \
http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token
Run Code Online (Sandbox Code Playgroud)

或身份令牌(参数中包含受众)

curl -H "Metadata-Flavor: Google" \
http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/identity?audience=https://www.google.com
Run Code Online (Sandbox Code Playgroud)

因此,您无需拥有任何秘密或私钥,库就能够生成用于访问外部 API 的令牌(访问或身份)。

但是,元数据服务器不提供秘密(私钥),您无法使用它来生成签名 URL。

您在这里需要一个服务帐户密钥文件

您可以通过多种方式以安全的方式将其提供给 pod。

我不建议您将服务帐户密钥文件直接放入容器中,这并不安全

另一种解决方案

最终,您可以动态生成一个密钥并将其定义为服务帐户密钥(其名称为user-defined service account key)。

  • 您可以在集群上部署服务时生成它。像这样,您不必存储秘密,它每次都是动态生成的。
  • 您可以在容器启动时生成密钥,将其设置在服务帐户中,并将其保存在内存中。

然后,当您在代码中需要它时使用它,它应该可以工作,因为它链接到您的服务帐户。

然而,您还需要考虑如何清理旧的、无用的钥匙。