I’ve been using Terraform to provision and configure some application deployed on Heroku, as well as some other infrastructure pieces on AWS. Since these configurations are kept on a git repository, I do not want any configuration values stored in clear text, to avoid having any sensitive information leaked. For this reason, I’ve searched for a solution to keep these secrets stored in a safe manner without too much configuration overhead. I’ve decided to give EJSON a try for this particular use case, for a couple of reasons:

  • It performs asymmetric encryption on the secrets with a pair of keys that are easy to share with your team, since the public key is actually stored on the .ejson file and the private key is just a string;
  • It’s installation is pretty simple, since EJSON is a Go binary and it’s actually available on rubygems, so you can gem install ejson;
  • It does not require any other piece of infrastructure besides your workstation;
  • The secrets are kept in JSON files, with the keys in clear text and the values encrypted.

This last bit is actually more interesting than it might look, since Terraform’s .tfvars files, which store the configurations’ variables, are either JSON or HCL files. As such, we can simply decrypt our EJSON file without any other transformation or adaptation and pass it directly to Terraform!

With all that being said, let’s go through how to achieve this setup and how to use it efficiently.

Setup

Assuming you’re on macOS, here’s how to install all the required tools:

brew install terraform
gem install ejson

Now we need to generate a keypair for EJSON. By default, these are stored at /opt/ejson/keys. To keep it simple and to keep everything in your user’s home, we’ll add an alias for ejson that defaults to ~/.ejson/keys. Adapt the last command to fit your particular shell setup.

mkdir -p ~/.ejson/keys
alias ejson='ejson --keydir ~/.ejson/keys'
echo "alias ejson='ejson --keydir ~/.ejson/keys'" >> ~/.bash_profile

Using an encrypted .tfvars file

First, we need to create a new keypair:

ejson keygen -w

Now add the following to a variables.tfvars.ejson file, replacing __OUTPUT_FROM_EJSON_KEYGEN__ with the public key outputted from the previous command:

As you see, this format is immediately usable by Terraform, as it’s just a JSON file, with key-value pairs that hold our Terraform variables.

To encrypt the file, it’s as easy as running:

$ ejson encrypt variables.tfvars.ejson
Wrote 211 bytes to variables.tfvars.ejson.

This is the file that you should add to your repository to save the variables safely.

It’s also pretty easy to decrypt it:

ejson decrypt variables.tfvars.ejson -o variables.tfvars

You should add these .tfvars files to your .gitignore so that they don’t end up in your repository:

This will keep the encrypted file intact and create a new, decrypted file. At this point, you’d be able to run a terraform plan as per usual:

terraform plan -var-file variables.tfvars

Streamline it with make

We can create a very simple Makefile to stream line this process after the initial setup:

So now, you just need to run make plan and make apply to use your safely encrypted secrets.

Conclusion

This is just a very simple way to keep your infrastructure’s configuration secrets on a git repository, whilst keeping them readily available for Terraform to consume. I would use this strategy for simple bootstrapping and infrastructure provision and use tools such as Vault for more complex scenarios, for instance, applications dynamically fetching their configurations on startup.

If you’d like to discuss this post, please drop me a line using any of my contacts.

Resources

  1. EJSON on Github