GCS, KMS and wrapped secrets

2018-04-30

Simple procedure to encrypt and save a secret using Google Cloud Platforms Key Management System (KMS) and Cloud Storage (GCS). THis is one possible way to save a secret file (or any file) securely in an ACL-protected cloud storage bucket and on top of that, encrypt it with a key only select identities can decrypt.

This procedure is an implementation of some of the guidelines described in the following articles:

Possible usecase is to save a secret (JSON certificate file, API Key, etc) in as a wrapped (encrypted) filed on GCS that is protected by ACL and which can only get decrypted by Cloud KMS

What that basically describes is a way to encrypt a secret and save it in a GCS. The code sample here is in golang which does the following:

  • Encrypt

    • Initializes KMS and GCS clients
    • Remove all ACLs on that object except for the OWNER and principal that will read the object (SERVICE_ACCOUNT_A)
    • Sets KMS KeyRing IAM permissions on the principal (SERVICE_ACCOUNT_A) to use the decryption Key
    • Encrypts the secret.
    • Uploads the secret to GCS
  • Decrypt

    • Initializes client as another user (SERVICE_ACCOUNT_A)
    • Use SERVICE_ACCOUNT_A to access KMS and the encrypted object in GCS
    • Decrypt secret using KMS.

Note: if CLoud KMS supports remote user-specified key encryption directly (i.,e a key you upload first and KMS uses that specific key), the procedures outlined here will need be modified.

Usage

Setup ENV variables

export BUCKET_NAME=`echo $PROJECT_ID`-secret

Create GCS Bucket to hold secrets

$ gsutil mb  $BUCKET_NAM

Enable KMS and Create KeyRing, Key

$ gcloud services enable cloudkms.googleapis.com
$ gcloud kms keyrings create $KMS_KEYRING --location $KMS_LOCATION
$ gcloud kms keys create $KMS_KEY \
     --purpose encryption \
     --keyring $KMS_KEYRING --location $KMS_LOCATION

Create ServiceAccount JSON file

This is the principal that will have access to the secret

gcloud iam service-accounts create ${SERVICE_ACCOUNT_A} \
     --display-name=${SERVICE_ACCOUNT_A}
gcloud iam service-accounts keys create  \
    `echo ${SERVICE_ACCOUNT_A}`.json  \
    --iam-account="${SERVICE_ACCOUNT_A_EMAIL}"

Run Script

What that demonstrates is taking your plain text secret, using kms to encrypt the file, then upload the encrypted file to GCS…then finally remove ACL access to that file in GCS. This means only the OWNER and principal you set the ACL to read and use KMS (for that key) can read+decrypt it.

You can also enable data access logging to show who used a given key to decrypt a secret. for example, to enable key data access logs, run

gcloud kms keys get-iam-policy $KMS_KEY \
   --location $KMS_LOCATION \
   --keyring $KMS_KEYRING \
   --format=json > /tmp/policy.json

then edit /tmp/policy.json (remember to specify a new etag and your svc account!)

Finally apply the new policy:

After this if you run the KMS-GCS script below, you should see a similar audit log describing who used the key (of course this is just the KMS key details; once you secret is in possession of the client app, all bets are off!)

images/gcs_1.png

Here are some screenshots from the GCP console for KSM and the object ACLs enforced:

KMS key used

images/gcs_2.png

Shows ACL on the object in GCS

images/gcs_3.png

About Envelope Encryption

The primary example here is pretty straightforward: we’re using a KMS system to encrypt the secret outright. An alternative to this is one more layer of indirection with layered security. That is, you can use “Envelope Encryption”. What you are doing there is generating an encryption key locally (it can be anything), then using that key to symmetrically encrypt your secret, then ask KMS to encrypt your symmetric key that you just generated. After that, you can forget about the plain symmetric key. What you are left with is your secret that is now encrypted with a key you just generated locally and the key you just generated itself encrypted by one in KMS. You can save both together because both are encrypted. If someone needs to decrypt the data, they will take the encrypted symmetric key, ask KMS to decrypt it, then use that decrypted form to decode the original secret.

About Key Derivation Function (KDF)

In certain cases, the key you generate locally is not cryptographically strong (i.,e not enough randomness, etc). What you can do there is ‘strengthen’ it cryptographically by running the orignial key you generated through a KDF many times. Think of it as a hash of hash of hash of your original key. Once you have this derived key, you can then encrypt your data. If someone needs to decrypt it, they’d take your original key, then run it through the same KDF protocol (its one way repeatable), then end up with the same derived key…use that to decrypt your secret as normal

About GCS+CSK automatic encryption

GCS already has a built in capability to encrypt objects on upload and download. That is, GCS will always encrypt an object at rest using either Google’s own key or a key you provide with each request or a key that you specify that is stored by Google in its KMS. THese various options are described here:

  • Using Customer Managed Encryption Keys

However, the subtle difference between the procedure described in this article and the mode where you use GCS+CSK with KMS backend is that mere access to the GCS object allows you to see the object since Google does the decryption using your KMS keys for you. To put it another way, GCS+CSK with KMS allows you to define a key k1, allow GCS’s own internal service account access to encrypt/decrypt with k1. Then you say, “any object uploaded/downloaded to this bucket will use key k1”. If you save a secret with this mechanism, all a user needs to is access to the object to get the secret. The procedure outlined in this artilce requires two spaprate access controls: access to the object (GCS) and the callers (your) access to the KMS secret. I’ll provide a sample of GCS-CSK (with KMS) here:

Set IAM permissions to GCS’s default svc account for the KMS key

images/gcs_4.png

The encrypted file is now labeled as CSK backed:

images/gcs_5.png


Source

This site supports webmentions. Send me a mention via this form.