0

I have two storage buckets in one Google cloud project, say storage-project. One bucket with default encryption, and another bucket encrypted with a Customer Managed Key (CMEK) created in another project called security-project. I have granted the role Cloud KMS CryptoKey Encrypter/Decrypter to the Cloud Storage service account (service-xxxxxxxx@gs-project-accounts.iam.gserviceaccount.com) in the storage-project. I could successfully upload files to this storage bucket using a Google account who is owner to both the projects. This is an expected behaviour.

Now I have another user account, who has the roles Viewer and Storage Object Creator on the storage-project, and no permissions on the security-project. My concern is that, the above user is able to upload and download files from the second storage bucket, even though the user is not granted encrypt/decrypt permission on the above mentioned key.

As per the link https://cloud.google.com/storage/docs/encryption/customer-managed-keys#service-accounts, encryption and decryption with customer-managed encryption keys is accomplished using service accounts. This implicitly means that anyone who has Storage Object Creator role on the storage-project, has the ability to encrypt/decrypt with that key.

Is there any way that I could restrict encrypt/decrypt permission for a user? More specifically, this user should be able to upload files to the first storage bucket, and not to the second bucket, like we could do with AWS KMS + S3.

sethvargo
  • 26,739
  • 10
  • 86
  • 156

3 Answers3

8

Background

Some background context is important for this to make sense. On Google Cloud, many services operate as a Service Account. For example, Google Cloud Storage has a unique service account per Google Cloud project. You can get the Cloud Storage service account via the Cloud Console, API, or even curl (as shown below):

$ curl https://storage.googleapis.com/storage/v1/projects/${PROJECT_ID}/serviceAccount \
    --header "Authorization: Bearer $(gcloud auth print-access-token)" 

The service account is usually expressed as an email like:

service-1234567890@gs-project-accounts.iam.gserviceaccount.com

When the Cloud Storage service interacts with other Google Cloud services, it uses this service account to authorize those actions.

Customer Managed Encryption Keys

By default, all data is encrypted at rest on Google Cloud. Normally this data is encrypted with Google-managed keys. When you enable Customer Managed Encryption Keys (CMEK) for Cloud Storage, you configure a Cloud Storage bucket to automatically encrypt/decrypt data that is uploaded/downloaded using a provided Cloud KMS key. You, the customer, have control over that key through Cloud KMS.

Note: I'm going to explain how this works for uploading files, but the same principles apply in reverse for downloading them.

Without CMEK

Without CMEK, a developer uploads an object to Cloud Storage. Cloud Storage encrypts the object with a Google-managed encryption key and persists the encrypted object to disk:

+-----------+         +---------------+                           +-------+
| Developer |         | Cloud Storage |                           | Disk  |
+-----------+         +---------------+                           +-------+
      |                       |                                       |
      | Upload object         |                                       |
      |---------------------->|                                       |
      |                       | ----------------------------------\   |
      |                       |-| Encrypt with Google-managed key |   |
      |                       | |---------------------------------|   |
      |                       |                                       |
      |                       | Write encrypted object                |
      |                       |-------------------------------------->|
      |                       |                                       |

With CMEK

With CMEK, a developer uploads an object to Cloud Storage. Cloud Storage invokes the Cloud KMS API using the Cloud Storage service account to encrypt the object and persists the encrypted object to disk:

+-----------+         +---------------+                     +-----------+ +-------+
| Developer |         | Cloud Storage |                     | Cloud KMS | | Disk  |
+-----------+         +---------------+                     +-----------+ +-------+
      |                       |                                   |           |
      | Upload object         |                                   |           |
      |---------------------->|                                   |           |
      |                       |                                   |           |
      |                       | Encrypt this object               |           |
      |                       |---------------------------------->|           |
      |                       |                                   |           |
      |                       |       Here's the encrypted object |           |
      |                       |<----------------------------------|           |
      |                       |                                   |           |
      |                       | Write encrypted object            |           |
      |                       |---------------------------------------------->|
      |                       |                                   |           |

The most important point here is that the Cloud KMS API is invoked using the Cloud Storage service account's identity, not the calling developer's identity.

This is by design, because most customers want CMEK to be transparent to the developer. When you enable CMEK on a Cloud Storage bucket, developers do not need to be aware of CMEK configuration. They use the Cloud Storage API as normal, and Cloud Storage takes care of the encryption/decryption operations using the key you specified. The developer does not need permissions on the Cloud KMS keys because, as shown in the diagram above, the developer never interacts with Cloud KMS directly.

Restricting Access

So, revisiting your original question:

Is there any way that I could restrict encrypt/decrypt permission for a user? More specifically, this user should be able to upload files to the first storage bucket, and not to the second bucket, like we could do with AWS KMS + S3.

You have a few options here:

  1. You could use Application-Layer Encryption (ALE) instead of CMEK. You can still use Cloud KMS, but the Developer encrypts the data using Cloud KMS before saving to Cloud Storage:

    +-----------+                       +-----------+ +---------------+                                      +-------+
    | Developer |                       | Cloud KMS | | Cloud Storage |                                      | Disk  |
    +-----------+                       +-----------+ +---------------+                                      +-------+
          |                                   |               |                                                  |
          | Encrypt this object               |               |                                                  |
          |---------------------------------->|               |                                                  |
          |                                   |               |                                                  |
          |       Here's the encrypted object |               |                                                  |
          |<----------------------------------|               |                                                  |
          |                                   |               |                                                  |
          | Upload KMS-encrypted object       |               |                                                  |
          |-------------------------------------------------->|                                                  |
          |                                   |               | ----------------------------------\              |
          |                                   |               |-| Encrypt with Google-managed key |              |
          |                                   |               | |---------------------------------|              |
          |                                   |               |                                                  |
          |                                   |               | Write KMS-encrypted, Google-encrypted object     |
          |                                   |               |------------------------------------------------->|
          |                                   |               |                                                  |
    
  2. Do not grant the user permissions on the bucket. Instead of restricting IAM permissions on the key, you need to restrict IAM permissions on the bucket.

sethvargo
  • 26,739
  • 10
  • 86
  • 156
  • Thank you for the great explanation. The scenario & the solution I mentioned works well in AWS. So I wanted to ensure that if it is possible with Google Cloud KMS. – Gayathri K S Mar 21 '20 at 06:07
  • @sethvargo so it looks like the key doesn't leave the KMS as the Cloud storage encrypts the object by sending the data to KMS and receives the encrypted version? Could you provide a reference to this flow? – pinkpanther Sep 01 '20 at 08:31
  • That is correct, kinda. Cloud Storage generates a data encryption key (DEK) and encrypts the data with that key. It then sends the DEK to KMS to encrypt the DEK. This is called envelope encryption. KMS never sees the plaintext storage object. – sethvargo Sep 01 '20 at 18:24
0

For each Cloud KMS object type for which you can set granular Cloud IAM permissions, that object has a testIamPermissions method. The testIamPermissions method returns the set of permissions the caller has been granted for that object. You can restrict encrypt/decrypt permission for a user by using this Documentation

  • I understand that we can grant granular accesses to every user. But in my case, even though the user is not explicitly granted encrypt permission on a CMEK, the user is able to upload file to the encryption enabled bucket. The expected behaviour is only user with permission `Cloud KMS CryptoKey Encrypter/Decrypter` on the CMEK should be able to upload files to the above bucket, and other users shouldn't be able to. – Gayathri K S Mar 20 '20 at 04:35
  • We could meet the above requirement while encrypting a file using a CMEK, say via gcloud shell. A user assigned with `Cloud KMS CryptoKey Encrypter` role on the CMEK is able to encrypt a file using that CMEK. But when the role is unassigned from the user, it gets permission denied error for the encryption command. Whereas, when it comes to GCS, it is the Cloud Storage service account who is performing the encrypt/decrypt actions on behalf of the user. Due to this, we are unable to restrict access at the user level. I am looking for a solution to this problem. – Gayathri K S Mar 20 '20 at 05:16
0

In addition to @sethvargo his answer, it's good to note that your choice conflicts with how Google Cloud Storage has its permissions designed.

If you want to prevent a user from decrypting an object you shouldn't grant the user storage.objects.get on that object. If you want to allow the user from seeing the objects, simply stick with storage.objects.list.

As storage.objects.get implicitly allows the identity to also decrypt the object through the Cloud Storage Service Account.