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
Decrypt
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.
5/12*
Added info about using default GCS-KMS encryption5/23*
Added info about Envelope Encryption and Key Derivation Functions5/15/19
: see https://github.com/GoogleCloudPlatform/berglas5/31/19
: see: https://github.com/google/tink/blob/master/docs/GOLANG-HOWTO.md#envelope-encryptionSetup 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!)
Here are some screenshots from the GCP console for KSM and the object ACLs enforced:
KMS key used
Shows ACL on the object in GCS
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.
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
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:
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
The encrypted file is now labeled as CSK backed:
Source
This site supports webmentions. Send me a mention via this form.