Hide secrets in Terraform

5 months, 2 weeks ago DevOps, Security

Infrastructure as Code (IaC) is the new buzzword in the city. DevOps Engineers have become crazy and are writing code to launch their infrastructure on the cloud. Terraform has just pushed that craziness to the next level. This tool has become very famous for building, changing, and versioning infrastructure. In fact, it has been the safest and the most efficient way of managing large infrastructure.

You write code for your infrastructure, right? You push the code to a git repo, don't you? How do you manage your secret files there? You don't, seriously? Your terraform project may have a lot of secret files like terraform.tfstate, private keys, and other secret files, you never want to be exposed. You may use S3 bucket as your state storage. But what about other files for which you prefer git repo to S3 bucket? Okay. In this blog post, we will learn how we can hide our secret files in a git repo.

I was highly fascinated when I found git-crypt project on Github. It enables GPG (GNU Privacy Guard) encryption and decryption of files in a git repository. Files which we choose to protect are encrypted when committed, and decrypted when checked out. The best part of this tool is that we can freely share our secrets with our teammates using GPG.

Install git-crypt

To install git-crypt and gpg on MacOS, we can use homebrew. The commands are simple:

$ brew install git-crypt
$ brew install gnupg

And for Ubuntu, we can run the following commands:

$ sudo apt update
$ sudo apt install git-crypt gnupg -y

Generate a new GPG key

If we don't have a GPG key, it is high time we should generate it.

$ gpg --full-generate-key

We will be asked to choose some options at the prompt. Let's choose by keeping this in mind: GPG key should use RSA with a key size of 4096 bits.

After a successful GPG key generation, we will export our public key to a keyserver. A keyserver is a repository on the Internet which can store and distribute our public key to correspondents who request it. The correspondents can import that key to their keyring, and they are ready for secure correspondence with us. To get the GPG key ID, we can enlist all the keys by this command:

$ gpg --list-secret-keys --keyid-format LONG

This command will show us something like this on terminal:

/Users/username/.gnupg/pubring.kbx
---------------------------------
sec   rsa4096/<key ID> <Generation Date> [<Expiry Date>]
      <Public key fingerprint>
uid                 [ultimate] <Full Name> <Email Address>
ssb   rsa4096/<Some Secret> <Generation Date> [E]

Let's grab the key ID and export it to the keyserver using this command:

$ gpg --keyserver https://pgp.mit.edu/ --send-keys <key ID>

Hide our secrets

To hide secrets in any git repository, we need to enter into the repo from the terminal. Now we have to configure git-crypt here:

$ git-crypt init
$ git-crypt add-gpg-user --trusted <email address>

But how will we specify the files we need to encrypt? Like a .gitignore file, we have to create a .gitattributes file in the parent directory of the repo and specify the files need to be encrypted. For a Terraform project, this file can look like below:

# files need to be encrypted
# format: file_name filter=git-crypt diff=git-crypt
id_rsa filter=git-crypt diff=git-crypt
*.pem filter=git-crypt diff=git-crypt
*.tfstate filter=git-crypt diff=git-crypt

# never encrypt .gitattributes file
.gitattributes !filter !diff

If we want to encrypt any specific file of any specific folder, then we can create another .gitattributes file in that folder and add files in it. If we need to encrypt an entire sub-tree dir/, then we have to place the following lines in dir/.gitattributes:

* filter=git-crypt diff=git-crypt
.gitattributes !filter !diff

Whatever the way is, we need to make sure that we are not adding these files in .gitignore file. Now let's add our changes to git and enlist all files for encryption:

$ git add -A
$ git-crypt status -e

All is well! Right? We can commit and push the changes:

$ git commit -m "using git-crypt for encryption"
$ git push <upstream> <branch>

We can check on the Web UI of the upstream and confirm.

Add our teammate

It's damn easy. We can grab our teammate's public key from keyserver or key file.

$ gpg --keyserver pgp.mit.edu --recv-key <teammate's key ID>

# Or

$ gpg --import /path/to/public/key/file

Now like before, we can add this key using his/her email address:

$ git-crypt add-gpg-user --trusted <teammate's email address>

Decrypt encrypted files

Normally, all files remain decrypted in the local repository. But after cloning a repository for the first time, we need to decrypt files with GPG:

$ git-crypt unlock

More tools

There is another project: git-secret. It can be used for the same purpose. For encrypting inline secrets, we can use terrahelp command line utility written in Go. Maybe, there are more ways. Please don't hesitate to share if you can know it.

Previous Next
Comments
Have you read the comment guideline?

Give me useful, constructive criticism. Spot a typo or an error? Let me know and I will correct it.