3 min read

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

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

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.

Diagram showing AWS process for sharing encrypted EBS snapshots between source and target accounts. Encrypted snapshots are created in the source region using a KMS master key, then shared and used in the target account with a different KMS key.

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.

Blue background with white text reading "HashiCorp Packer" next to a white logo consisting of three geometric shapes.

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

Screenshot of JSON code for a Packer template. It includes configuration settings like the AWS region, AMI description, and instance type. The code specifies builders and owners for creating an Ubuntu AMI on AWS using HVM virtualization.

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:

A screenshot of JSON code displaying AWS IAM permissions. The code includes an "Allow" effect for actions like "ec2:AttachVolume," "ec2:RunInstances," "ec2:TerminateInstances," and more within a policy dated "2012-10-17.

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:

Screenshot of a JSON configuration file for Packer building an encrypted Ubuntu AMI. It includes details like type, region, AMI description, source AMI filter, owners, instance type, and AMI name.

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:

Screenshot of a command-line interface showing a process of launching and managing an AWS EC2 instance. The text includes actions like creating a security group, launching an instance, and modifying attributes, with status messages and identifiers.

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

Screenshot of an AWS EC2 Dashboard displaying details of an AMI. It shows information like AMI ID, Source, Owner, Creation Date, Architecture, and Image Type. The platform is Ubuntu, and the status is available.

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

A screenshot of an AWS EC2 dashboard displays detailed information about a volume. It shows the status, size, volume type, creation date, encryption status, and associated tags. The volume is encrypted and located in the US East region.

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:

Screenshot of a code snippet showing a JSON object with block device mappings. The code includes "encrypted": true, "device_name": "/dev/sdh", and "volume_size": 15. The file is hosted on GitHub.

And here’s the full example:

Screenshot of a JSON configuration file for creating an encrypted Amazon Machine Image (AMI) using Packer. Includes details like region, device name, source AMI filter, instance type, and owner ID.

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, click the button below. We look forward to hearing from you and talking encryption!

 

Adding Custom Cognito SignUp Message with Terraform, Lambda & Compoze

Adding Custom Cognito SignUp Message with Terraform, Lambda & Compoze

AWS Cognito is a secure and scalable user access management solution offered by Amazon Web Services. At Compoze Labs it is our go to solution for...

Read More
How to Fix Your Spaghetti Code (and Avoid it in the First Place)

How to Fix Your Spaghetti Code (and Avoid it in the First Place)

Software is said to be written with “Spaghetti Code” when the software is difficult to maintain or extend. Spaghetti code is hard to understand what...

Read More
Concepts to Consider While Building a RAG Chatbot

Concepts to Consider While Building a RAG Chatbot

Theworld is still only at day one of the Artificial Intelligence (AI) era, yet AI adoption has been much faster compared to the adoption of other...

Read More