Search code examples
google-cloud-storagegoogle-cloud-kms

Restricting encrypt/decrypt permissions for a Cloud KMS key with CMEK and Cloud Storage


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 ([email protected]) 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.


Solution

  • 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:

    [email protected]
    

    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.