How to use Google asymmetric KMS keys to encrypt given secrets in Terraform

To encrypt a given secret for use in Terraform, you can use either the google_kms_secret or the google_kms_secret_asymmetric. As I find the ciphertext for google_kms_secret a bit short, I prefer to use google_kms_secret_asymmetric. In this blog I will show you how to use an asymmetric KMS key to deploy secrets using Terraform.

Encrypting a given secret in terraform

To encrypt a given secret in Terraform you perform the following steps:

  • create an asymmetric KMS key
  • encrypt the secret
  • calculate the checksum
  • base64 encode the encrypted secret
  • use the encrypted secret in terraform
  • store the secret in the secret manager

Create an asymmetric KMS key

To create the asymmetric KMS key, use the following Terraform snippet:

resource "google_kms_key_ring" "cicd" {
  name     = "cicd"
  location = "global"
}

resource "google_kms_crypto_key" "terraform" {
  name     = "terraform"
  key_ring = google_kms_key_ring.cicd.id
  purpose  = "ASYMMETRIC_DECRYPT"
  version_template {
    algorithm = "RSA_DECRYPT_OAEP_4096_SHA256"
  }
}

data "google_kms_crypto_key_version" "terraform" {
  crypto_key = google_kms_crypto_key.terraform.id
}

resource "local_file" "public_key" {
    filename = "${path.module}/public-key.pem"
    content     = data.google_kms_crypto_key_version.terraform.public_key[0].pem
}

After this key is created, the public key is stored in the file public-key.pem.

The nice thing is that you can give the public key to anybody. If
you give the public key to the supplier of the secret, the plain text
secret will never-ever have to be handed over to you.

Encrypt the secret with the public key

To encrypt the secret with the public key, type:

$ echo -n B0FCE1C3-AF9F-4E92-A2E8-18748AB3C591  | \
  openssl pkeyutl -in - \
        -encrypt \
        -pubin \
        -inkey public-key.pem \
        -pkeyopt rsa_padding_mode:oaep \
        -pkeyopt rsa_oaep_md:sha256 \
        -pkeyopt rsa_mgf1_md:sha256 \
            -out my-api-key.enc

The encrypted secret is now in the file my-api-key.enc and is 512 bytes long!

Calculate the checksum

To ensure that the ciphertext is not tampered with, you can calculate and use the checksum too:

$ go install github.com/binxio/crc32@1.0.0
$ $HOME/go/bin/crc32 \
  -polynomial castagnoli < my-api-key.enc
cbe83625

Base64 encode the encrypted secret

As Terraform does not support binary files, we do a base64 encoding of the secret:

$ openssl base64 \
  -in my-api-key.enc \
  -out my-api-key.enc.b64

Use the encrypted secret in terraform

Now you can safely include the encrypted secret in your terraform template by using
the google_kms_secret_asymmetric data source:

data "google_kms_secret_asymmetric" "api_key" {
  crypto_key_version = data.google_kms_crypto_key_version.terraform.id
  crc32              = "cbe83625"
  ciphertext         = <<EOT
        kumghZcadUm6pwraawMQr5amqM98lpSWd35GhUPtlIHK1AAXACI8s5zfh7PS7qGA
        v37/mt9xVuLmNv1zdj4uEhvPm52E92d6CCn3HkAosRNmHaQ5x7d3SU5u5kjR5aRh
        l3UoPsJCWOpdkJ1e/UcCDFqNfFL297cFUO0MWqW2ya2PIk5U8K8ujziy9sNMKSVi
        Mb98+S/EAuGlNbk/KHTyNzCnicjoKNWZPFQipZCTrrxXpy27j5+9RJ+q7T/gAzGu
        BWn85bbslMjL8ozjy5XWpBAZuhIjcrBAeSz8Wbahmnh4wcRTBM2sFWp0cW7rzeCk
        adE8z6LvrIdz330O2iX8v8H+Q1z0tNVvB07fklvj3h5XjuwVixXSpVEwD9d6uDq7
        i+fxQfER2eJvwI1NEzBsyNgJKDeilEz0obJq7ljyFZAbSxDnsog43GoJbg0JVe9s
        CqXtk0DdmHgEym+jw1MdKeFVwNPeyghZBF74gGNJ/G1bkkXUlL6kIQ6gr+oA9En0
        2hrUb4Nd+vECMby7r0mqtpkMA/7Fxq49CFLqfY3hSlMkNippruCGu5jArt5QksAa
        Tg0XGPOt7oU7T3FjWnMOiab4AR9KpFTT5vCBOBXdssU41jt90lIpPM0kDUPpRPEG
        +hvi7FdT9YsLxRTlVA0SFsXMvAt+MSdsAqgRk6BBCE4=
  EOT

  provider           = google-beta
}

Deploy the secret in the secret manager

Finally you can deploy the secret in the secret manager:

resource "google_secret_manager_secret" "api_key" {
  secret_id = "api-key"

  replication {
    automatic = true
  }
}

resource "google_secret_manager_secret_version" "api_key" {
  secret = google_secret_manager_secret.api_key.id

  secret_data = data.google_kms_secret_asymmetric.api_key.plain_text
}

After apply the secret, you can access the secret in the secret manager:

$ gcloud secrets versions access latest --secret api-key 

Conclusion

With the google_kms_secret_asymmetric data source, you can encrypt a given secret to obtain
a 512 byte long ciphertext which can safely be included in a Terraform template.
The asymmetric key can be used for relatively short secrets up to ~200 bytes.

As the secret will be visible in your Terraform state file, we recommend to avoid the use of given secrets whenever possible. If you do have a given secret to deploy, the asymmetric data source is a good second best.

Mark van Holsteijn is a senior software systems architect, and CTO of binx.io. He is passionate about removing waste in the software delivery process and keeping things clear and simple.
Share this article: Tweet this post / Post on LinkedIn