Deploying Vault in AWS on EC2 Using Terraform
HashiCorp provides a set of official Terraform modules to make it easier to deploy a Vault Enterprise environment that adheres to the requirements and standards laid out in this HashiCorp Validated Design. These Terraform modules are used by both HashiCorp Professional Services as well as many of our trusted partners. Your HashiCorp account team can provide you with these Terraform modules upon request.
Tip
HashiCorp Professional Services or one of our trusted partners can help you accelerate your deployment of Vault Enterprise with confidence. For more information, contact your HashiCorp account team.For deploying Vault Enterprise in AWS on EC2, the following Terraform modules will be provided:
terraform-aws-vault-prerequisites
terraform-aws-vault
Using these modules, you will be able to use Terraform to deploy a complete, end-to-end Vault Enterprise deployment inside of your own AWS account.
Note
The following steps outline a manual method of installing Vault and its prerequisites using these modules. This method of installation is primarily designed for you to test the execution of the module and get to know the design. We strongly recommend adapting these steps into a more controlled deployment process, such as HCP Terraform, Terraform Enterprise, or your existing CI/CD pipeline infrastructure.Warning
NEVER store Terraform state in a version control system or in another unprotected location. Terraform state contains sensitive information and must be protected. For more information, refer to the HashiCorp Terraform documentation(opens in new tab).While we have made efforts throughout this document to provide prescriptive best practices, we recognize that each organization has their own unique requirements and constraints when it comes to deploying infrastructure. Wherever possible, we have attempted to represent the different considerations you will need to make when deploying Vault in your AWS environment within the context of this Terraform module. The modules contain additional capabilities that you may wish to review if the variables from this module do not suit your specific needs.
Deployment Sequence Overview
- Create the certificate files.
- Obtain the license file.
- Download the Vault CLI.
- Download the Terraform CLI.
- Obtain the HashiCorp Validated Design Terraform modules for deploying Vault Enterprise in AWS on EC2 (
terraform-aws-vault-prerequisites
andterraform-aws-vault
). - Configure your AWS credentials.
- Repeating for both modules:
- Initialize your Terraform workspace.
- Configure your variables in the module for deployment.
- Create a Terraform plan.
- Apply the plan.
- Validate the cluster was created and is reachable.
- Initialize the Vault cluster.
Preparation
Create the Certificate Files
Create a standard X.509 certificate that will be installed on the Vault servers. Refer to your organization’s process on creating a new certificate that matches the DNS record you intend to direct users to when accessing Vault.
A total of three files will be needed:
- The certificate: (
vault-public.pub
) - The certificate's private key (
vault-private.key
): - The bundle file from the certificate authority used to vend the certificate: (
ca.pub
)
Keep these files handy, as you will need them later in the installation process.
Obtain the Vault Enterprise License File
Obtain the Vault Enterprise License File from your HashiCorp account team. This file contains a license key unique to your environment. The file will be named something like vault.hclic
.
Keep this file handy, as you will need it later in the installation process.
Download and Install the Vault CLI
Note
This guide was tested using Vault1.15.0+ent
.Download the appropriate package for your operating system from the HashiCorp Releases site.
Unzip the package.
Move the vault
binary (vault.exe
for Windows) to a directory in your system's PATH
.
Download and Install the Terraform CLI
Note
This guide was tested using Terraform1.6.2
.Download the appropriate package for your operating system from the HashiCorp Releases site.
Unzip the package.
Move the terraform
binary (terraform.exe
for Windows) to a directory in your system's PATH.
Download the Terraform modules
For the purpose of an automated deployment, HashiCorp and HashiCorp partners provide private Terraform modules to customize and support your deployment.
Once you have downloaded the modules, navigate to the examples/hvd-vault
directory. Use this as the base working directory during the installation process.
Configure AWS Credentials
Ensure the correct AWS credentials are in place and accessible to Terraform. Terraform can read credentials from:
- Credentials file: typically located at
$HOME/.aws/credentials
(%UserProfile%\.aws\credentials
on Windows) - Environment variables:
AWS_ACCESS_KEY_ID
AWS_SECRET_ACCESS_KEY
AWS_SESSION_TOKEN
(if using an IAM role or other expiring credentials)AWS_DEFAULT_REGION
For complete details on how to configure AWS credentials for Terraform, see the HashiCorp Terraform AWS Provider Documentation.
Ensure that the credentials you will be using have sufficient permissions to perform the necessary actions that Terraform will be performing.
Installation
Note
Remember to repeat the installation phase twice: once for the prerequisites module, once for the installation module. The instructions for populating theterraform.tfvars
file on the installation module has important differences from the prerequisites file.Initialize Terraform
Run terraform init
to initialize your Terraform workspace. Inspect the output to ensure that all providers and modules are successfully downloaded, and that there are no outstanding errors before continuing.
Configure Variables For Deployment - Prerequisites Module
Create a file called terraform.tfvars
. Review the file terraform.tfvars.example
for explanations of each variable, then fill in the list of variables in your own file with the applicable values for your environment.
#------------------------------------------------------------------------------
# General
#------------------------------------------------------------------------------
# Prefix for all resources created by this module
friendly_name_prefix = "vault-example"
# AWS Tagging Configuration
common_tags = {
deployment = "dev"
site = "one"
}
create_ssh_keypair = true
ssh_public_key = "ssh-rsa AAAAB2C3D4E5fg=="
#------------------------------------------------------------------------------
# Network Configuration
#------------------------------------------------------------------------------
vpc_enable_ssm = true
route53_zone_name = "example.domain"
route53_failover_record = {
create = true
record_name = "vault"
set_id = "fso1"
}
lb_sg_rules_details = {
vault_api_ingress = {
cidr_blocks = ["0.0.0.0/0"] # Allow all external traffic to the Vault UI/API
}
}
#------------------------------------------------------------------------------
# Secret Manager Configuration - Initial Vault Secrets
#------------------------------------------------------------------------------
secretsmanager_secrets = {
vault = {
license = {
name = "vault-license"
path = "./vault.hclic"
}
}
ca_certificate_bundle = {
name = "vault-ca-bundle"
path = "./ca.pub"
}
cert_pem_secret = {
name = "vault-public-cert"
path = "./vault-public.pub"
}
cert_pem_private_key_secret = {
name = "vault-private-cert"
path = "./vault-private.key"
}
}
#------------------------------------------------------------------------------
# S3 Configuration
#------------------------------------------------------------------------------
s3_buckets = {
snapshot = {
bucket_name = "vault-example-snapshots"
force_destroy = true
}
}
#------------------------------------------------------------------------------
# IAM Configuration
#------------------------------------------------------------------------------
iam_resources = {
ssm_enable = true
log_forwarding_enabled = true
}
Configure Variables For Deployment - Installation Module
Warning
You can only configure variables for the installation module's `terraform.tfvars` file after the prerequisites module has successfully applied via Terraform. This is because the output of the prerequisites module must be manually added to the `terraform.tfvars` file.Create a file called terraform.tfvars
. Review the file terraform.tfvars.example
for explanations of each variable, then replace each of the variables in your own file with the applicable values from the prerequisites module output.
Contained below is a table that outlines the input for the module and the corresponding output from the terraform-aws-vault-prerequisites
module.
Deployment Module Input | Prerequisite Module Output |
---|---|
friendly_name_prefix | N/A |
ssh_keypair_name | ssh_key_pair |
vpc_id | vpc_id |
vault_hostname | route53_failover_fqdn |
iam_instance_profile | iam_profile_name |
kms_key_arn | kms_key_alias_arn |
ec2_subnet_ids | private_subnet_ids |
lb_tg_arns | lb_tg_arns |
lb_type | lb_type |
lb_security_group_id | lb_security_group_ids |
cloudwatch_log_group_name | cloudwatch_log_group_name |
vault_secrets_arn | vault_secrets_arn |
vault_cert_secret_arn | cert_pem_secret_arn |
vault_privkey_secret_arn | cert_pem_private_key_secret_arn |
ca_bundle_secret_arn | ca_certificate_bundle_secret_arn |
asg_hook_value | asg_hook_value |
asg_max_size | N/A |
asg_instance_count | N/A |
common_tags | N/A |
#------------------------------------------------------------------------------
# General
#------------------------------------------------------------------------------
# Prefix for all resources created by this module
friendly_name_prefix = "hashicat"
# AWS Tagging Configuration
common_tags = {
deployment = "dev"
site = "one"
}
#------------------------------------------------------------------------------
# Vault Configuration
#------------------------------------------------------------------------------
vault_version = "1.15.0+ent"
vault_hostname = "vault.hashicat.aws.sbx.hashicorpdemo.com"
asg_hook_value = "hashicat-9c6c44-us-east-2-vault-asg-hook"
ssh_keypair_name = "hashicat-9c6c44-vault-keypair"
#------------------------------------------------------------------------------
# Network Configuration
#------------------------------------------------------------------------------
ec2_subnet_ids = ["subnet-060ad98b2a2bad6a3", "subnet-0dc426ead0506b1c3", "subnet-07f45c38621d1da2a"]
vpc_id = "vpc-0a6ccd67e4d393561"
permit_all_egress = true
#------------------------------------------------------------------------------
# Secret Manager Configuration
#------------------------------------------------------------------------------
vault_secrets_arn = "arn:aws:secretsmanager:us-east-2:441170333099:secret:hashicat-9c6c44-vault-odetla"
vault_cert_secret_arn = "arn:aws:secretsmanager:us-east-2:441170333099:secret:hashicat-9c6c44-cert_pem_public-odetla"
vault_privkey_secret_arn = "arn:aws:secretsmanager:us-east-2:441170333099:secret:hashicat-9c6c44-cert_pem_private-odetla"
ca_bundle_secret_arn = "arn:aws:secretsmanager:us-east-2:441170333099:secret:hashicat-9c6c44-cert_pem_bundle-odetla"
#------------------------------------------------------------------------------
# IAM Configuration
#------------------------------------------------------------------------------
iam_profile_name = "hashicat-9c6c44-us-east-2-vault-role"
#------------------------------------------------------------------------------
# KMS Configuration
#------------------------------------------------------------------------------
kms_key_arn = "arn:aws:kms:us-east-2:441170333099:alias/hashicat-9c6c44-vault-kms-key-alias"
#------------------------------------------------------------------------------
# LB Configuration
#------------------------------------------------------------------------------
lb_type = "network"
lb_tg_arns = ["arn:aws:elasticloadbalancing:us-east-2:441170333099:targetgroup/hashicat-a8659c-vault-console-tg/3fd0a37541e9753e", "arn:aws:elasticloadbalancing:us-east-2:441170333099:targetgroup/hashicat-a8659c-vault-cluster-tg/a900c5846de82e95"]
lb_security_group_id = "sg-002d8b2c407fcc39c"
#------------------------------------------------------------------------------
# Logging Configuration
#------------------------------------------------------------------------------
cloudwatch_log_group_name = "hashicat-9c6c44-vault-log-group"
log_forwarding_enabled = true
Create and apply Terraform plan
Generate a Terraform plan with the following command:
terraform plan -out plan
Review the plan output to see the changes that will be applied, then apply the changes with this command:
terraform apply plan
Confirm the changes by typing yes
when prompted.
Validate installation
Run the following command:
terraform output
Using this output, obtain the URL of the Vault cluster (vault_url
) as well as the root token (vault_root_token
), and then set the following environment variables:
$ export VAULT_ADDR=$(terraform output vault_url)
$ export VAULT_TOKEN=$(terraform output vault_root_token)
Test that the Vault cluster is running and reachable by running:
vault status
Initialize
Create GPG public keys
For each individual who will be receiving a key share, create their own unique GPG public key:
$ cat > alice_key.conf << EOF
%echo Generating a basic OpenPGP key for Alice
Key-Type: 1
Key-Length: 4096
Subkey-Type: 1
Subkey-Length: 4096
Name-Real: Alice
Name-Comment: Alice is a Vault PGP user
Name-Email: alice@example.com
Expire-Date: 1
Passphrase: recede-yard-unwilling-shrouded
%commit
%echo done
EOF
$ gpg --full-gen-key --batch alice_key.conf
…
$ gpg --output alice_key.pub --export alice@example.com
Initialize Vault
Pass in the GPG public key from each participating individual to initialize Vault with the API, CLI, or UI. Once Vault initializes, it returns the key shares, each one encrypted with the GPG public keys which you passed in at initialization time.
$ vault operator init \
-pgp-keys “alice_key.pub,bob_key.pub,carol_key.pub,dan_key.pub,frank_key.pub" \
-root-token-pgp-key "alice_key.pub"
Recovery Key 1: Fr6OZBDRG…KsgqC
Recovery Key 2: X6/RDBpC…rZfUM
Recovery Key 3: XCAmAgbr…J0+P3
Recovery Key 4: JwoPoWtl…QXhyV
Recovery Key 5: 8Rdk5D+7…0tgWJ
Initial Root Token: hvs.NpBT…cbbXZ
Success! Vault is initialized
Recovery key initialized with 5 key shares and a key threshold of 3. Please
securely distribute the key shares printed above.
Record the root token value from this output. You will need it in order to log into Vault initially.
Each individual should now record their unique unseal key and store it safely. This is their key share, which is now encrypted and base64-encoded.
Unseal Vault
To unseal Vault using encrypted key shares, each individual will need to both base64-decode and decrypt their unseal key before sending it to the Vault server. When decrypting with GPG each individual will also need to pass the unique passphrase that was supplied during the creation of their own GPG key:
$ cat encoded-encrypted-keyshare1 | base64 --decode > encrypted-keyshare1
$ gpg --passphrase recede-yard-unwilling-shrouded --decrypt encrypted-keyshare1 > keyshare1
$ vault operator unseal keyshare1
gpg: encrypted with 4096-bit RSA key, ID 91002A8F909C6714, created 2023-04-17
"Alice (Alice is a Vault PGP user) <alice@example.com>"
Key Value
--- -----
Seal Type shamir
Initialized true
Sealed true
Total Shares 5
Threshold 3
Unseal Progress 1/3
…
Repeat this process until a sufficient number of key shares have been supplied to Vault.
At this point the Vault server is now considered unsealed. The output of the final unseal command should look something like this:
gpg: encrypted with 4096-bit RSA key, ID 6ED85F446B6FAA27, created 2023-04-17
"Dan (Dan is a Vault PGP user) <dan@example.com>"
Key Value
--- -----
Seal Type shamir
Initialized true
Sealed false
Total Shares 5
Threshold 3
…
This concludes the key ceremony.
Congratulations, you now have a running cluster! You should now be able to log into Vault:
$ VAULT_ADDR=$(terraform output vault_addr) VAULT_TOKEN=$(terraform output vault_root_token) vault login