Deploying Vault with a Custom AMI
2022-04-02
In this article, we'll walk through creating a custom Amazon Machine Image (AMI) with HashiCorp Vault baked in using Packer, launching an EC2 instance with the generated AMI, and then configuring Vault to store and access secrets.
Overview
This guide covers:
- Baking the Vault AMI with Packer: Cloning the repository and running Packer to generate the AMI.
- Launching the Vault EC2 Instance: Using the created AMI to spin up an EC2 instance.
- Configuring Vault: Initializing, unsealing, and verifying Vault on the instance.
- Storing and Accessing Secrets: A brief walkthrough on adding and retrieving secrets with Vault.
Prerequisites
- An AWS account with appropriate IAM permissions.
- Packer installed locally.
- Optionally, the AWS CLI or Terraform to launch EC2 instances.
- Basic familiarity with HashiCorp Vault and AWS concepts.
Step 1: Baking the Vault AMI with Packer
touch vault-ami.pkr.hcl
Step 2: Build the AMI
packer {
required_plugins {
amazon = {
version = ">= 1.0.0"
source = "github.com/hashicorp/amazon"
}
}
}
variable "aws_region" {
default = "us-east-1"
}
variable "ami_name" {
default = "vault-server-ami"
}
variable "instance_type" {
default = "t3.micro"
}
variable "source_ami" {
default = "ami-090fa75af13c156b4" # Amazon Linux 2 (Change if using Ubuntu)
}
variable "ssh_username" {
default = "ec2-user" # Use "ubuntu" for Ubuntu AMIs
}
# AWS Builder Configuration
source "amazon-ebs" "vault_ami" {
region = var.aws_region
source_ami = var.source_ami
instance_type = var.instance_type
ssh_username = var.ssh_username
ami_name = var.ami_name
ami_description = "Pre-configured Vault AMI"
associate_public_ip_address = true
tags = {
Name = "Vault AMI"
}
}
# Provisioners (Runs the Vault install script)
build {
sources = ["source.amazon-ebs.vault_ami"]
provisioner "file" {
source = "install_vault.sh"
destination = "/tmp/install_vault.sh"
}
provisioner "shell" {
inline = [
"chmod +x /tmp/install_vault.sh",
"sudo /tmp/install_vault.sh"
]
}
}
Create the install script
#!/bin/sh # Download and move latest Vault release to bin. printf "\n\nFetching Vault binary" cd /opt/ && sudo curl -o vault.zip https://releases.hashicorp.com/vault/1.13.1/vault_1.13.1_linux_amd64.zip sudo unzip vault.zip sudo mv vault /usr/bin/ # Create a user named vault to be run as a service. printf "\n\nCreating vault user" sudo useradd --system --home /etc/vault.d --shell /bin/false vault # Configure Vault as a System Service printf "\n\nConfiguring vault as system service" sudo wget https://raw.githubusercontent.com/moabukar/vault-ami/main/non-tls/non-tls-vault.service sudo mv non-tls-vault.service /etc/systemd/system/vault.service sudo mkdir /etc/vault.d sudo chown -R vault:vault /etc/vault.d sudo mkdir /vault-data sudo chown -R vault:vault /vault-data sudo mkdir -p /logs/vault/ sudo wget https://raw.githubusercontent.com/moabukar/vault-ami/main/non-tls/non-tls.hcl sudo mv non-tls.hcl /etc/vault.d/vault.hcl # Start vault as a service printf "\n\nEnabling vault as a service" sudo systemctl enable vault sudo systemctl start vault sudo systemctl status vault
Review the Packer configuration file (for example, vault-pkr.hcl) to see how Vault is installed and configured. Then, build the AMI using Packer:
packer build vault-ami.pkr.hcl
Note: If the configuration file has a different name (for example, packer.hcl), use that filename.
Packer will run through the steps (e.g., installing Vault, applying configuration, etc.) and output an AMI ID in your AWS account.
Step 2: Launching the Vault EC2 Instance
With the AMI baked, launch an EC2 instance using the new AMI. You can use the AWS Management Console, CLI, or Terraform. For example, using Terraform
provider "aws" { region = "eu-west-2" } variable "ami_id" { default = "ami-010b1877662d8747c" } resource "aws_instance" "vault" { ami = var.ami_id instance_type = "t3.micro" key_name = "vault-dev" vpc_security_group_ids = ["sg-06d59bbad808fc1ee"] tags = { Name = "Vault Server" } } output "vault_public_ip" { value = aws_instance.vault.public_ip } output "ssh_command" { description = "SSH into your Vault server" value = format("ssh -i vault-dev.pem ec2-user@%s", aws_instance.vault.public_ip) }
Replace ami-xxxxxxxx with the AMI ID from the Packer build, and update the key name, security group, and subnet ID as appropriate.
Step 3: Configuring Vault on the Instance
Once your EC2 instance is up, SSH into it:
ssh -i your-keypair.pem ec2-user@<instance-public-ip>
Verify that Vault is installed:
vault --version
Next, initialize Vault. Important: The output will include your unseal key and root token—store these securely.
vault operator init -key-shares=1 -key-threshold=1
Then, unseal Vault using the unseal key provided:
vault operator unseal <unseal-key>
Once unsealed, you can log in with the root token:
vault login <root-token>
Step 4: Storing and Accessing Secrets Now that Vault is running, you can store secrets. For example, to store a database password:
vault kv put secret/db password="SuperSecret123!"
To retrieve the secret:
vault kv get secret/db
This demonstrates how you can securely manage secrets using Vault.
Conclusion
You now have a full walkthrough to bake a Vault AMI using Packer, launch an EC2 instance with that AMI, configure Vault and start managing secrets securely. This approach streamlines deploying Vault in AWS and is a great addition to any DevOps workflow.