使用Go SQL Connector连接Cloud SQL PostgreSQL(私有IP)遇权限错误
错误信息
ERROR Failed to dial instance {"error": "failed to get instance: Refresh error: failed to get instance metadata (connection name = "
"): Get "https://sqladmin.googleapis.com/sql/v1beta4/projects//instances/postgres/connectSettings?alt=json&prettyPrint=false": compute: Received 403 `Unable to generate access token; IAM returned 403 Forbidden: Permission 'iam.serviceAccounts.getAccessToken' denied on resource (or it may not exist).
This error could be caused by a missing IAM policy binding on the target IAM service account.
环境详情
- Google Cloud:启用私有IP连接的PostgreSQL实例
- 服务账号已配置权限:
- Cloud SQL Admin
- Cloud SQL Client
- Compute Network Admin
- Project IAM Admin
- Service Account Admin
- Service Account Token Creator
- Storage Admin
- Workload Identity User
- 运行环境:代码部署在同一VPC下GKE集群内的VM实例中
相关Go代码
func connectDB() error { user := "postgres" password := "password" connectionName := "connectionName" sslmode := "require" dbName := "databaseName" config, err := pgx.ParseConfig(connStr) if err != nil { return err } opts := append([]cloudsqlconn.Option{}, cloudsqlconn.WithDefaultDialOptions(cloudsqlconn.WithPrivateIP()), ) d, err := cloudsqlconn.NewDialer(context.Background(), opts...) if err != nil { return err } config.DialFunc = func(ctx context.Context, network, instance string) (net.Conn, error) { log.FromContext(ctx).Info(fmt.Sprintf("Dialing instance: %s", instance)) conn, err := d.Dial(ctx, connectionName) if err != nil { log.FromContext(ctx).Error(err, "Failed to dial instance") } return conn, err } dbURI := stdlib.RegisterConnConfig(config) dbPool, err := sql.Open("pgx", dbURI) if err != nil { return err } err = dbPool.Ping() if err != nil { return err } return nil }
已完成排查
- 验证数据库实例私有IP可在同一VPC内正常访问
- 确认服务账号已配置Cloud SQL Admin、Cloud SQL Client、Service Account Token Creator、Workload Identity User等核心权限
- 确认Go应用运行在可访问私有VPC的VM实例中
解决方案
1. 检查Workload Identity绑定有效性
确保GKE集群的K8s服务账号已与GCP IAM服务账号完成正确绑定:
gcloud iam service-accounts add-iam-policy-binding \ [GCP_IAM_SERVICE_ACCOUNT_EMAIL] \ --member="serviceAccount:[PROJECT_ID].svc.id.goog[K8S_NAMESPACE/K8S_SERVICE_ACCOUNT]" \ --role="roles/iam.workloadIdentityUser"
同时确认K8s服务账号的注解iam.gke.io/gcp-service-account已正确指向对应的GCP IAM服务账号。
2. 确认iam.serviceAccounts.getAccessToken权限绑定
虽然已配置Service Account Token Creator角色,需确保该角色是绑定到目标服务账号资源上(即需要生成Token的服务账号本身),而非仅在项目层级绑定:
gcloud iam service-accounts add-iam-policy-binding \ [TARGET_SERVICE_ACCOUNT_EMAIL] \ --member="serviceAccount:[CALLER_SERVICE_ACCOUNT_EMAIL]" \ --role="roles/iam.serviceAccountTokenCreator"
3. 验证Cloud SQL Admin API是否启用
检查项目中Cloud SQL Admin API状态,若未启用则需要开启:
# 检查API状态 gcloud services list --enabled | grep sqladmin.googleapis.com # 启用API gcloud services enable sqladmin.googleapis.com
4. 验证服务账号身份与Token生成能力
在GKE容器内执行以下命令,确认当前使用的服务账号是否正确,以及是否能正常生成访问Token:
# 获取当前服务账号邮箱 curl -H "Metadata-Flavor: Google" http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/email # 获取访问Token curl -H "Metadata-Flavor: Google" http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token
若无法获取Token,说明Workload Identity配置存在问题,需重新检查绑定流程。
5. 确认Connection Name格式正确性
检查代码中connectionName的格式是否为项目ID:区域:实例名称,格式错误会导致元数据请求失败。
内容的提问来源于stack exchange,提问作者piyush

