Secure By Default: Using HashiCorp’s Packer With AWS and EBS

December 6, 2021

In my years at Compoze Labs, I’ve worked on numerous AWS based projects, in a number of highly regulated industries, throughout the years. Whether they fall under PCI, HIPAA, or FISMA, one of the first things we as engineers have had to do is ensure that all data in our systems is encrypted at rest. What does that mean in the AWS world? Ultimately it starts with encrypting all EBS volumes, including the root device attached to EC2 instances.

The way to ensure that all instances start (launch) encrypted by default, is to create an AMI with an encrypted root device. Any EC2 instance, or service requiring an EC2 instance (like EMR), should be created using these AMIs.

My teams over the years have solved this a number of ways, by following the same basic steps.

In order to accomplish this we’ve used a number of approaches including Terraform and even just the AWS console. However, there were problems with these solutions. The AWS console approach is manual and therefore not repeatable while Terraform does not officially support encrypted volume AMIs and requires a workaround.

The approach I’ve found to be the most scalable, repeatable, and maintainable is via Packer, by HashiCorp.

How it works

Packer works by ingesting a configuration file you provide and popping out a machine image with an operating system and various other softwares installed on it. This machine image can then be used as the blueprint to create virtual machines. Packer supports a multitude of image formats, including AWS AMIs.

Getting started

Packer has a few installation methods, the easiest of which is to download the pre-compiled binary.

Hashicorp also supports a docker container for running commands, which I find to be quite convenient for playing around with Packer for the first time. I recommend giving that a try!

Defining the configuration

Packer uses JSON to define its configuration. Let’s take a look at a sample Packer configuration to create a simple Ubuntu AMI

This barebones example is pretty straight forward. You just need to specify the machine type (in this case amazon-ebs) and various metadata like name, description, etc. The other interesting bit to pay attention to is source_ami_filter. This section of the template describes how Packer should find the AMI to use as our base image. In this case it will grab the most recent (most_recent: true) version of the ubuntu-xenial-16.04-amd64-server image. It is worth noting that the seemingly magical number, 099720109477, is the AWS marketplace owner id for Canonical, the group that supports Ubuntu.

Additionally, Packer will need the correct IAM permissions in order to create the AMI. Here is an example policy with the minimum IAM permissions required:

Further details on how you can grant Packer the appropriate permissions can be found here.

Encrypting Root Device

We could use the example template to generate an AMI, but the root volume for any EC2 instance using the AMI would be unencrypted; so how do we extend it to encrypt the root device? We just need to add the following JSON key-value pair:

“encrypt_boot”: true

And here is what out example looks like now:

That’s it! After adding our encrypted flag we can run the Packer build command:

packer build encrypted_ami.json

If our AWS credentials are setup correctly, we should see something similar to the following in the terminal:

Once Packer has finished we can go to the AWS console and see the AMI that was created.

If we launch an instance using this AMI we can also see that the root device is encrypted.

Additional Volume Encryption

Encrypting additional volumes is just as easy. Here is an example snippet for specifying an encrypted EBS volume as part of the AMI:

And here’s the full example:

Summary

Encryption is an important part of keeping our systems secure in order to protect user data. Cloud providers like AWS give us the tools to build secure systems; while tools like HashiCorp’s Packer work to give us the ability to harness these tools more easily.

Where to go next?

Packer is designed to create an easy workflow for creating machine images. As part of that workflow I encourage you to check out how to introduce testing into that process with tools like Chef’s InSpec. I’ve written a bit on InSpec already as well!

A complete working example of the code snippets from article can be found in this github repository

To chat about your project, shoot us an email at connect@compozelabs.com. We look forward to hearing from you and talking encryption!