无法从 Secrets Manager 访问密钥。

huangapple go评论112阅读模式
英文:

Unable to access secret from Secrets Manager

问题

我已经将一些密钥添加到了密钥管理器中,但是在运行时尝试访问它们时出现了以下错误:

rpc error: code = Unauthenticated desc = transport: compute: Received 500 `Could not fetch URI /computeMetadata/v1/instance/service-accounts/default/token?scopes=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fcloud-platform

这是一个我重新设置的旧应用程序,所以我可能在权限方面漏掉了一些东西。我正在通过项目ID和密钥名称的完整路径访问密钥。

我运行了 gcloud app describe 命令,并验证了其中列出的 serviceAccount 具有 Owner 角色。我还将 Owner 添加到了 App Engine 默认服务账号中。我还尝试了特定的密钥管理器访问角色。

更新:
该服务与 文档 中用于访问密钥的 golang 示例相匹配。它使用标准的 Google 库来创建客户端和请求。我没有显式配置身份验证,所以应用程序应该使用 这里 描述的服务账号。

密钥的路径看起来是有效的:

projects/<my-project-id>/secrets/MY_SECRET/versions/latest

错误消息表明缺少 cloud-platform 范围,但是我没有看到任何特定的配置,因为这是 Google App Engine。

更新 2:
我推送了一个 存储库 来重新创建这个问题。它使用了来自 Google 的示例代码来访问密钥,并且出现了与 cloud-platform 范围相关的相同错误。

英文:

I've added some secrets to the Secret Manager but I get the following error trying to access them at runtime:

rpc error: code = Unauthenticated desc = transport: compute: Received 500 `Could not fetch URI /computeMetadata/v1/instance/service-accounts/default/token?scopes=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fcloud-platform

This is an old app I'm setting up again so I must have missed something in the permissions. I'm accessing the keys via the full path with the project id and the key name.

I've run gcloud app describe and verified that the serviceAccount listed there has the Owner Role. I've also added Owner to the App engine default service account. I've tried the specific Secret Manager Accessor role as well.

Update:
The service matches the golang example in the docs for accessing a secret. It's using the standard google libraries for create a client and requests. I am not explicitly configuring authentication so the app should be using the service account as described here.

The path for the secret looks valid:

projects/&lt;my-project-id&gt;/secrets/MY_SECRET/versions/latest

The error message suggests it's missing a cloud-platform scope but I don't see any specific configuration for this since it's Google App Engine.

Update 2:
I've pushed a repo that recreates this. It uses the sample code from google for accessing a secret and fails with the same error about the cloud-platform scope.

答案1

得分: 1

为了解决您的应用程序问题,我建议您执行以下操作:

为了确保您部署到App Engine的代码正在使用所需的帐户运行,我建议您使用代码传递凭据,并在代码中明确指定您的服务帐户文件。

./project-folder
├── test.go
├── service-account-credentials.json
├── ...

使用一个新的项目文件夹,创建一个服务帐户密钥并保存在该文件夹中。

使用以下代码测试您的服务帐户是否具有适当的权限,以排除问题是App Engine未使用正确帐户的可能性。如果出现问题,您可以检查App Engine日志。

package main

import (
	"context"
	"fmt"
	"log"
	"net/http"
	"os"

	secretmanager "cloud.google.com/go/secretmanager/apiv1"
	"google.golang.org/api/option"
	secretmanagerpb "google.golang.org/genproto/googleapis/cloud/secretmanager/v1"
	"google.golang.org/grpc/status"
)

// 您的项目ID
const projectId = "project-id"
// 您的密钥名称
const secret = "secret"

func main() {
	// 密钥的路径
	// 版本可以是字符串版本号(例如"5")或别名(例如"latest")。
	name := fmt.Sprintf("projects/%s/secrets/%s/versions/latest", projectId, secret)
	
	ctx := context.Background()
	
	// 显式从指定路径读取凭据。
	// 创建客户端。
	client, err := secretmanager.NewClient(ctx,
		option.WithCredentialsFile("service-account-key.json"))
	if err != nil {
		log.Fatal("Failed to create secretmanager client\n")
		if s, ok := status.FromError(err); ok {
			log.Println(s.Message())
			for _, d := range s.Proto().Details {
				log.Println(d)
			}
		}
	}
	defer client.Close()

	// 构建请求。
	req := &secretmanagerpb.AccessSecretVersionRequest{
		Name: name,
	}
	
	// 如果存在,accessSecretVersion访问给定密钥版本的有效负载。
	// 调用API。
	result, err := client.AccessSecretVersion(ctx, req)
	if err != nil {
		log.Fatal("Failed to access secret version\n")
		if s, ok := status.FromError(err); ok {
			log.Println(s.Message())
			for _, d := range s.Proto().Details {
				log.Println(d)
			}
		}
	}

	// 警告:不要在生产环境中打印密钥 - 此代码片段演示如何访问密钥材料。
	log.Printf("Plaintext: %s\n", string(result.Payload.Data))

	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(w, `<h1>仅供测试目的。</h1>
		<h2>请勿在生产环境中使用。</h2>
		<p>您的密钥将显示在日志中。<p>`)
	})

	port := os.Getenv("PORT")

	fmt.Printf("Starting server at port %s\n", port)
	if err := http.ListenAndServe(":"+port, nil); err != nil {
		log.Fatal(err)
	}
} 	

如上所述,您的服务帐户必须使用授予Secret Manager权限的Owner角色,以便访问Secret Manager中的密钥。

另请参阅:

英文:

For troubleshooting your app, I would suggest to do the following:

To be sure that the code you deploy to App Engine is running with the desired account, I suggest Passing credentials using code and explicitly point to your service account file in code.

./project-folder
├── test.go
├── service-account-credentials.json
├── ...

With a new project folder, create a service account key and save in the folder.

Use the following code to test that your service account has the proper permissions, to discard that the problem is App Engine not using the proper account. You can check your App Engine logs in case something goes wrong.

package main
import (
&quot;context&quot;
&quot;fmt&quot;
&quot;log&quot;
&quot;net/http&quot;
&quot;os&quot;
secretmanager &quot;cloud.google.com/go/secretmanager/apiv1&quot;
&quot;google.golang.org/api/option&quot;
secretmanagerpb &quot;google.golang.org/genproto/googleapis/cloud/secretmanager/v1&quot;
&quot;google.golang.org/grpc/status&quot;
)
// Your Project ID
const projectId = &quot;project-id&quot;
// Name of your secret
const secret = &quot;secret&quot;
func main() {
// Path to your secret
// The version can be a version number as a string (e.g. &quot;5&quot;) or an
// alias (e.g. &quot;latest&quot;).
name := fmt.Sprintf(&quot;projects/%s/secrets/%s/versions/latest&quot;, projectId, secret)
ctx := context.Background()
// Explicitly reads credentials from the specified path.
// Create the client.
client, err := secretmanager.NewClient(ctx,
option.WithCredentialsFile(&quot;service-account-key.json&quot;))
if err != nil {
log.Fatal(&quot;Failed to create secretmanager client\n&quot;)
if s, ok := status.FromError(err); ok {
log.Println(s.Message())
for _, d := range s.Proto().Details {
log.Println(d)
}
}
}
defer client.Close()
// Build the request.
req := &amp;secretmanagerpb.AccessSecretVersionRequest{
Name: name,
}
// accessSecretVersion accesses the payload for the given secret version if one exists.
// Call the API.
result, err := client.AccessSecretVersion(ctx, req)
if err != nil {
log.Fatal(&quot;Failed to access secret version\n&quot;)
if s, ok := status.FromError(err); ok {
log.Println(s.Message())
for _, d := range s.Proto().Details {
log.Println(d)
}
}
}
// WARNING: Do not print the secret in a production environment - this snippet
// is showing how to access the secret material.
log.Printf(&quot;Plaintext: %s\n&quot;, string(result.Payload.Data))
http.HandleFunc(&quot;/&quot;, func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, `&lt;h1&gt;For testing purposes only.&lt;/h1&gt;
&lt;h2&gt;Do not use in production enviroments&lt;/h2&gt;
&lt;p&gt;Your secret is shown on Logs.&lt;p&gt;`)
})
port := os.Getenv(&quot;PORT&quot;)
fmt.Printf(&quot;Starting server at port %s\n&quot;, port)
if err := http.ListenAndServe(&quot;:&quot;+port, nil); err != nil {
log.Fatal(err)
}
} 	

As mentioned, your service account must be using the Owner role that grants permissions to the Secret Manager in order to access to the secret in Secret Manager.

See also

huangapple
  • 本文由 发表于 2022年1月18日 02:29:59
  • 转载请务必保留本文链接:https://go.coder-hub.com/70745961.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定