将内容上传到 GCP 存储桶的 KMS 权限出现 403 错误

Posted

技术标签:

【中文标题】将内容上传到 GCP 存储桶的 KMS 权限出现 403 错误【英文标题】:403 Error on KMS Permissions for Uploading Content to GCP Storage Bucket 【发布时间】:2020-02-27 07:36:56 【问题描述】:

想通了

所以有两种方法可以解决这个问题:

选项 1:

我没有使项目能够访问用于加密/解密存储桶的 KMS 密钥。以我自己的身份登录时,我可以通过在 cli 中运行以下命令进行测试:

gsutil kms authorize -p PROJECTNAME -k projects/PROJECTNAME/locations/global/keyRings/KEYRINGNAME/cryptoKeys/KEYNAME
然后我以服务帐户登录并尝试上传文件。这样做之后就成功了。

选项 2:

在使用云控制台挖掘之后,我发现有一个存储服务帐户需要访问加密解密。此帐户列在“存储”>“设置”>“云存储服务帐户”下。 GCP 似乎将实际工作委派给此帐户以执行上传任务。因此,虽然它具有存储桶访问权限(显然,因为它是存储服务帐户),但它没有 KMS 访问权限。将 KMS 加密/解密添加到此 SA 后,它现在自动为我工作,无需任何 gsutil 干预。

我还更新了用于上传的 SA 凭证的范围,使其同时具有 cloudkms 和 devstorage.full_control。不过,我不确定这是否会影响任何事情。


原问题:

我正在创建一个工作流,可以自动为多租户托管环境创建服务帐户、存储桶和 KMS 密钥环和密钥。

我有一个具有有限 KMS、SA 和存储权限的服务帐户,可以创建其他服务帐户并允许他们成为自己的租户项目的管理员(例如:为租户创建一个服务帐户,它已满控制该租户的 KMS 和存储桶,但不控制其他租户的)。

但是,我目前在让新服务帐户能够上传文件时遇到问题。它拥有所需的所有权限:

1. KMS 管理和加密/解密密钥环2。存储桶管理员

但是,当我尝试使用该服务帐户上传内容时出现以下错误

[403] Errors [ 
          Message[Permission denied on Cloud KMS key. 
          Please ensure that your Cloud Storage service
          account has been authorized to use this key. ] 
      Location[ - ] 
      Reason[forbidden] 
      Domain[global] 

这是我用来分配权限的代码,后面是用于访问存储桶的代码:

class Program
  
    private static string solutionLocation = Path.GetFullPath(Path.Combine(Directory.GetCurrentDirectory(), @".." + Path.DirectorySeparatorChar + ".." + Path.DirectorySeparatorChar + ".." + Path.DirectorySeparatorChar));

static void Main(string[] args)

//Deserialize the JSON File for use with other things
JSONCreds jsonCreds =  JsonConvert.DeserializeObject<JSONCreds>(
File.ReadAllText(Path.Combine(solutionLocation, "gcp-general-sa.json")));



Environment.SetEnvironmentVariable("GOOGLE_APPLICATION_CREDENTIALS",
Path.Combine(solutionLocation, "gcp-general-sa.json"));

KeyManagementServiceClient client = KeyManagementServiceClient.Create();

StorageClient storageClient = StorageClient.Create();

//Collect Tenant ID for testing purposes
Console.WriteLine("Tenant ID?");
string TID = Console.ReadLine();
if (TID.Length > 23)

     TID = TID.Substring(0, 23);


//Setting some variables that are used throughout

string keyID = "key-" + TID;
string keyRingName = "ring-" + TID;
string keyLocationID = "global";
string saName = "sa-" + TID;

//Create a Service Account for this agency
var newServiceAccount = CreateServiceAccount(jsonCreds.project_id, saName, saName);


//Create an API Key for this Service Account, and then decode it 
var credential = GoogleCredential.GetApplicationDefault().CreateScoped(IamService.Scope.CloudPlatform);

var service = new IamService(new IamService.Initializer

    HttpClientInitializer = credential
);

var newServiceAccountFullKey = service.Projects.ServiceAccounts.Keys.Create( new CreateServiceAccountKeyRequest(), "projects/-/serviceAccounts/" + newServiceAccount.Email).Execute();

var newServiceAccountKey = System.Text.ASCIIEncoding.ASCII.GetString(Convert.FromBase64String(newServiceAccountFullKey.PrivateKeyData));
Console.WriteLine("Created Service Account Key For: " + newServiceAccountFullKey.Name);


//Create KMS Key Ring for this agency
KeyRing newKeyRing = CreateKeyRing(client, jsonCreds.project_id, keyLocationID, keyRingName);


//Create a KMS Key in that new Key Ring
CryptoKey newKey = CreateCryptoKey(client, jsonCreds.project_id, keyLocationID, newKeyRing.KeyRingName.KeyRingId, keyID);


//Create Bucket with specified Parameters
Bucket bucket = new Bucket

    Location = "us-central1",
    Name = TID,
    StorageClass = StorageClasses.Standard,
    Encryption = new Bucket.EncryptionData()
    
        DefaultKmsKeyName = newKey.Name
    
;
var newStorageBucket = storageClient.CreateBucket(jsonCreds.project_id, bucket);

//Set permissions for the new Service Account for the new KeyRing and Bucket
AddMemberToKeyRingPolicy(client, jsonCreds.project_id, keyLocationID, newKeyRing.KeyRingName.KeyRingId, "custom_role_with_multiple_permissions", "serviceAccount:" + newServiceAccount.Email);

AddBucketIamMember(newStorageBucket.Name, "roles/storage.admin", "serviceAccount:" + newServiceAccount.Email);


//Testing uploading to the new bucket with the new account
var newSACredential = GoogleCredential.FromJson(newServiceAccountKey.ToString()).CreateScoped("https://www.googleapis.com/auth/cloudkms");

var storage = StorageClient.Create(newSACredential);

using (var fileStream = new FileStream("sample_image.png", FileMode.Open, FileAccess.Read, FileShare.Read))

    storage.UploadObject(newStorageBucket.Name, "sample_image_uploaded.png", null, fileStream);

任何想法我可能做错了什么?看起来这是一个权限问题,但我已经将存储和 KMS 的几乎每一个都分配给了这个动态创建的新服务帐户。

全栈跟踪:

Google.GoogleApiException: Google.Apis.Requests.RequestError
Insufficient Permission [403]
Errors [
    Message[Insufficient Permission] Location[ - ] Reason[insufficientPermissions] Domain[global]
]

  at Google.Cloud.Storage.V1.StorageClientImpl.UploadHelper.CheckFinalProgress() in T:\src\github\google-cloud-dotnet\releasebuild\apis\Google.Cloud.Storage.V1\Google.Cloud.Storage.V1\StorageClientImpl.UploadObject.cs:204
  at Google.Cloud.Storage.V1.StorageClientImpl.UploadHelper.Execute() in T:\src\github\google-cloud-dotnet\releasebuild\apis\Google.Cloud.Storage.V1\Google.Cloud.Storage.V1\StorageClientImpl.UploadObject.cs:154
  at Google.Cloud.Storage.V1.StorageClientImpl.UploadObject(Object destination, Stream source, UploadObjectOptions options, IProgress`1 progress) in T:\src\github\google-cloud-dotnet\releasebuild\apis\Google.Cloud.Storage.V1\Google.Cloud.Storage.V1\StorageClientImpl.UploadObject.cs:97
  at Google.Cloud.Storage.V1.StorageClientImpl.UploadObject(String bucket, String objectName, String contentType, Stream source, UploadObjectOptions options, IProgress`1 progress) in T:\src\github\google-cloud-dotnet\releasebuild\apis\Google.Cloud.Storage.V1\Google.Cloud.Storage.V1\StorageClientImpl.UploadObject.cs:70
  at ConsoleApp1.Program.Main(String[] args) in /Users/btruman/Desktop/gcp_scripts/VOCA Onboarding/Program.cs:136

【问题讨论】:

1) 您为 KMS 密钥分配了哪些角色 (custom_role_with_multiple_permissions)? 2) 您正在创建一个只有 Cloud KMS 范围的存储客户端。对存储本身没有任何权利。 3) 显示您的整个代码并显示错误消息在您的代码中发生的位置。 我建议您首先使用 CLI 进行所有操作。然后翻译成c#。这将更容易获得帮助。我无法测试您的问题,因为我没有时间创建您遗漏的部分。 Permission denied on Cloud KMS key when using cloud storage的可能重复 同意Seth,您需要将GCS服务帐户添加到密钥中-我们只让专门授权的实体使用您的密钥,因此您需要确定GCS使用它。 好吧,我想我有办法了。我接受了您的建议,并首先在 gsutil 中使用它。在我授权项目本身使用 KMS 密钥后,我能够成功上传它!所以,我想就是这样 - 我忽略了 KMS 可以在各种环境中使用的事实,虽然我的服务帐户可以访问它,但这并不意味着资源所在的项目具有使用它的能力。非常感谢 Tim 和 John 的提示! 【参考方案1】:

您必须在与要加密的数据相同的位置创建 Cloud KMS 密钥。如需进一步参考,请查看链接 [1]。

https://cloud.google.com/storage/docs/encryption/using-customer-managed-keys#prereqs

【讨论】:

啊,所以我不能在“global”中创建一个key,然后在“us-central1”中的bucket上使用它? 不,不是这样。把keyring和key放在us-central1(和bucket一样)没有修复,“global”不是bucket位置。【参考方案2】:

所以有两种方法可以解决这个问题:

选项 1:

我没有使项目能够访问用于加密/解密存储桶的 KMS 密钥。以我自己的身份登录时,我可以通过在 cli 中运行以下命令进行测试:

gsutil kms authorize -p PROJECTNAME -k projects/PROJECTNAME/locations/global/keyRings/KEYRINGNAME/cryptoKeys/KEYNAME

然后我以服务帐户登录并尝试上传文件。这样做之后就成功了。

选项 2:

在使用云控制台挖掘之后,我发现有一个存储服务帐户需要访问加密解密。此帐户列在存储 > 设置 > 云存储服务帐户下。 GCP 似乎将实际工作委派给此帐户以执行上传任务。因此,虽然它具有存储桶访问权限(显然,因为它是存储服务帐户),但它没有 KMS 访问权限。将 KMS 加密/解密添加到此 SA 后,它现在自动为我工作,无需任何 gsutil 干预。

【讨论】:

以上是关于将内容上传到 GCP 存储桶的 KMS 权限出现 403 错误的主要内容,如果未能解决你的问题,请参考以下文章

使用 sql server 作业上传到 gcp 存储桶

如何设置 GCS 存储桶的限制

将数据从 gs 存储桶移动到 s3 亚马逊存储桶的 GCP dataproc 集群 hadoop 作业失败 [控制台]

使用 GCP_KMS 和 BYOK 解决方案管理密钥轮换

使用curl将数据加载到来自存储桶的Cloud SQL(Google云端平台)

数据流:storage.Client() 导入错误或如何列出 GCP 存储桶的所有 blob