Set up HashiCorp Vault with AWS KMS auto-unseal to eliminate manual unsealing processes and enable high availability deployments with automatic recovery.
Prerequisites
- AWS account with IAM permissions
- Root or sudo access
- Basic knowledge of HashiCorp Vault
- SSL certificate (self-signed acceptable for testing)
What this solves
HashiCorp Vault requires manual unsealing after restarts, which creates operational overhead and prevents true high availability. AWS KMS auto-unseal eliminates this bottleneck by automatically unsealing Vault servers using AWS Key Management Service encryption keys. This configuration enables automated failover, reduces human intervention, and supports scalable secrets management in production environments.
Step-by-step configuration
Update system packages
Start by updating your package manager to ensure you get the latest versions and security patches.
sudo apt update && sudo apt upgrade -y
sudo apt install -y curl unzip jq awscli
Install HashiCorp Vault
Download and install the latest Vault binary from HashiCorp's official releases.
VAULT_VERSION="1.15.4"
wget https://releases.hashicorp.com/vault/${VAULT_VERSION}/vault_${VAULT_VERSION}_linux_amd64.zip
unzip vault_${VAULT_VERSION}_linux_amd64.zip
sudo mv vault /usr/local/bin/
sudo chmod +x /usr/local/bin/vault
vault --version
Create Vault user and directories
Create a dedicated system user for Vault and set up the necessary directory structure with proper permissions.
sudo useradd --system --home /opt/vault --shell /bin/false vault
sudo mkdir -p /opt/vault/data /opt/vault/logs /etc/vault.d
sudo chown -R vault:vault /opt/vault
sudo chmod 750 /opt/vault/data
Configure AWS CLI with IAM permissions
Set up AWS credentials for Vault to access KMS. Create an IAM user with KMS permissions first in the AWS Console.
sudo -u vault aws configure
Enter your AWS Access Key ID, Secret Key, region (e.g., us-east-1), and output format (json)
The IAM user needs this policy for KMS operations:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"kms:Encrypt",
"kms:Decrypt",
"kms:ReEncrypt*",
"kms:GenerateDataKey*",
"kms:DescribeKey"
],
"Resource": "arn:aws:kms:::key/*"
}
]
}
Create AWS KMS key for Vault unsealing
Create a dedicated KMS key for Vault auto-unseal operations with proper key policy and alias.
aws kms create-key --description "Vault Auto-Unseal Key" --usage ENCRYPT_DECRYPT
Note the KeyId from the output
Create an alias for easier reference
aws kms create-alias --alias-name alias/vault-unseal --target-key-id YOUR_KEY_ID_HERE
Get the key ARN for configuration
aws kms describe-key --key-id alias/vault-unseal --query 'KeyMetadata.Arn' --output text
Configure Vault server with auto-unseal
Create the main Vault configuration file with AWS KMS auto-unseal parameters and storage backend.
ui = true
api_addr = "https://vault.example.com:8200"
cluster_addr = "https://vault.example.com:8201"
storage "file" {
path = "/opt/vault/data"
}
listener "tcp" {
address = "0.0.0.0:8200"
cluster_address = "0.0.0.0:8201"
tls_cert_file = "/etc/vault.d/vault.crt"
tls_key_file = "/etc/vault.d/vault.key"
tls_min_version = "tls12"
}
seal "awskms" {
region = "us-east-1"
kms_key_id = "alias/vault-unseal"
endpoint = "https://kms.us-east-1.amazonaws.com"
}
log_level = "INFO"
log_file = "/opt/vault/logs/vault.log"
log_rotate_duration = "24h"
log_rotate_max_files = 30
default_lease_ttl = "168h"
max_lease_ttl = "720h"
cluster_name = "vault-prod"
Generate SSL certificates
Create self-signed certificates for testing or use your existing SSL certificates for production.
sudo openssl req -x509 -nodes -days 365 -newkey rsa:4096 \
-keyout /etc/vault.d/vault.key \
-out /etc/vault.d/vault.crt \
-subj "/C=US/ST=State/L=City/O=Organization/CN=vault.example.com" \
-addext "subjectAltName = DNS:vault.example.com,DNS:localhost,IP:127.0.0.1"
sudo chown vault:vault /etc/vault.d/vault.key /etc/vault.d/vault.crt
sudo chmod 600 /etc/vault.d/vault.key
sudo chmod 644 /etc/vault.d/vault.crt
Set configuration file permissions
Secure the Vault configuration file to prevent unauthorized access to sensitive settings.
sudo chown vault:vault /etc/vault.d/vault.hcl
sudo chmod 640 /etc/vault.d/vault.hcl
Create systemd service file
Configure Vault to run as a systemd service with proper security settings and automatic restart capabilities.
[Unit]
Description=HashiCorp Vault
Documentation=https://www.vaultproject.io/docs/
Requires=network-online.target
After=network-online.target
ConditionFileNotEmpty=/etc/vault.d/vault.hcl
[Service]
Type=notify
User=vault
Group=vault
ProtectSystem=full
ProtectHome=read-only
PrivateTmp=yes
PrivateDevices=yes
SecureBits=keep-caps
AmbientCapabilities=CAP_IPC_LOCK
Capabilities=CAP_IPC_LOCK+ep
CapabilityBoundingSet=CAP_SYSLOG CAP_IPC_LOCK
NoNewPrivileges=yes
ExecStart=/usr/local/bin/vault server -config=/etc/vault.d/vault.hcl
ExecReload=/bin/kill --signal HUP $MAINPID
KillMode=process
Restart=on-failure
RestartSec=5
TimeoutStopSec=30
StartLimitInterval=60
StartLimitBurst=3
LimitNOFILE=65536
LimitMEMLOCK=infinity
[Install]
WantedBy=multi-user.target
Enable and start Vault service
Start the Vault service and enable it to run automatically on system boot.
sudo systemctl daemon-reload
sudo systemctl enable vault
sudo systemctl start vault
sudo systemctl status vault
Initialize Vault with auto-unseal
Initialize Vault which will automatically unseal using the AWS KMS key. The initialization only happens once.
export VAULT_ADDR="https://vault.example.com:8200"
export VAULT_SKIP_VERIFY=1 # Only for self-signed certificates
Initialize Vault
vault operator init -recovery-shares=5 -recovery-threshold=3
Save the recovery keys and root token securely
Vault should automatically unseal after initialization
Configure firewall rules
Open the necessary ports for Vault API and cluster communication.
sudo ufw allow 8200/tcp comment "Vault API"
sudo ufw allow 8201/tcp comment "Vault Cluster"
sudo ufw reload
Test auto-unseal functionality
Test service restart and auto-unseal
Verify that Vault automatically unseals when the service restarts, eliminating manual intervention.
# Check current seal status
vault status
Restart the Vault service
sudo systemctl restart vault
Wait a few seconds and check status again
sleep 5
vault status
Vault should show "Sealed: false" without manual unsealing
Test failover scenario
Simulate a complete system restart to verify automatic unsealing during boot.
# Record current status
vault status
Simulate system restart
sudo systemctl stop vault
sudo systemctl start vault
Check logs for auto-unseal process
sudo journalctl -u vault -f --since="5 minutes ago"
Verify your setup
# Check Vault service status
sudo systemctl status vault
Verify Vault is unsealed
vault status
Check AWS KMS key usage
aws kms describe-key --key-id alias/vault-unseal
Test basic Vault operations
vault auth -method=userpass
vault secrets list
Common issues
| Symptom | Cause | Fix |
|---|---|---|
| Vault fails to start with permission errors | Incorrect file ownership or permissions | sudo chown -R vault:vault /opt/vault /etc/vault.d |
| Auto-unseal fails with AWS credentials error | Missing or incorrect AWS credentials | Run sudo -u vault aws sts get-caller-identity to verify credentials |
| KMS access denied error | IAM user lacks KMS permissions | Attach the vault-kms-policy to your IAM user |
| SSL/TLS handshake errors | Certificate issues or hostname mismatch | Update certificate SAN or use VAULT_SKIP_VERIFY=1 for testing |
| Vault sealed after AWS outage | Temporary KMS unavailability | Use recovery keys: vault operator unseal -mode=recovery |
| High availability cluster fails | Incorrect cluster configuration | Verify cluster_addr and network connectivity between nodes |
Next steps
- Implement Consul ACL security and encryption for production deployments
- Configure Vault database secrets engine for dynamic credentials
- Set up Vault high availability cluster with Consul backend
- Integrate Vault with Kubernetes service accounts for secure pod authentication
- Configure Vault PKI secrets engine for automated certificate management
Automated install script
Run this to automate the entire setup
#!/usr/bin/env bash
set -euo pipefail
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
# Configuration
VAULT_VERSION="${VAULT_VERSION:-1.15.4}"
VAULT_DOMAIN="${1:-vault.example.com}"
AWS_REGION="${2:-us-east-1}"
# Usage message
usage() {
echo "Usage: $0 [VAULT_DOMAIN] [AWS_REGION]"
echo "Example: $0 vault.company.com us-west-2"
exit 1
}
# Logging functions
log_info() { echo -e "${GREEN}[INFO]${NC} $1"; }
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
# Cleanup on failure
cleanup() {
log_error "Installation failed. Cleaning up..."
systemctl stop vault 2>/dev/null || true
systemctl disable vault 2>/dev/null || true
rm -f /etc/systemd/system/vault.service
rm -rf /tmp/vault_install
exit 1
}
trap cleanup ERR
# Check prerequisites
check_prerequisites() {
if [[ $EUID -ne 0 ]]; then
log_error "This script must be run as root"
exit 1
fi
if ! command -v curl >/dev/null 2>&1; then
log_error "curl is required but not installed"
exit 1
fi
}
# Detect distribution
detect_distro() {
if [[ ! -f /etc/os-release ]]; then
log_error "Cannot detect distribution - /etc/os-release not found"
exit 1
fi
. /etc/os-release
case "$ID" in
ubuntu|debian)
PKG_MGR="apt"
PKG_UPDATE="apt update && apt upgrade -y"
PKG_INSTALL="apt install -y"
FIREWALL_CMD="ufw"
;;
almalinux|rocky|centos|rhel|ol|fedora)
PKG_MGR="dnf"
PKG_UPDATE="dnf update -y"
PKG_INSTALL="dnf install -y"
FIREWALL_CMD="firewall-cmd"
;;
amzn)
PKG_MGR="yum"
PKG_UPDATE="yum update -y"
PKG_INSTALL="yum install -y"
FIREWALL_CMD="firewall-cmd"
;;
*)
log_error "Unsupported distribution: $ID"
exit 1
;;
esac
}
# Install packages
install_packages() {
log_info "[1/10] Updating system packages..."
eval $PKG_UPDATE
log_info "[2/10] Installing required packages..."
$PKG_INSTALL curl unzip jq
# Install AWS CLI if not present
if ! command -v aws >/dev/null 2>&1; then
if [[ "$PKG_MGR" == "apt" ]]; then
$PKG_INSTALL awscli
else
$PKG_INSTALL awscli2
fi
fi
}
# Install Vault
install_vault() {
log_info "[3/10] Installing HashiCorp Vault..."
mkdir -p /tmp/vault_install
cd /tmp/vault_install
wget "https://releases.hashicorp.com/vault/${VAULT_VERSION}/vault_${VAULT_VERSION}_linux_amd64.zip"
unzip "vault_${VAULT_VERSION}_linux_amd64.zip"
mv vault /usr/local/bin/
chmod 755 /usr/local/bin/vault
chown root:root /usr/local/bin/vault
# Verify installation
if ! /usr/local/bin/vault --version; then
log_error "Vault installation failed"
exit 1
fi
}
# Create Vault user and directories
create_vault_user() {
log_info "[4/10] Creating Vault user and directories..."
if ! id vault >/dev/null 2>&1; then
useradd --system --home /opt/vault --shell /bin/false vault
fi
mkdir -p /opt/vault/{data,logs} /etc/vault.d
chown -R vault:vault /opt/vault
chmod 755 /opt/vault
chmod 750 /opt/vault/data
chmod 755 /opt/vault/logs
chmod 755 /etc/vault.d
chown root:vault /etc/vault.d
}
# Configure AWS credentials
configure_aws() {
log_info "[5/10] AWS Configuration Required"
log_warn "Please ensure AWS CLI is configured with proper KMS permissions"
log_warn "Run: sudo -u vault aws configure"
log_warn "Required IAM permissions: kms:Encrypt, kms:Decrypt, kms:ReEncrypt*, kms:GenerateDataKey*, kms:DescribeKey"
}
# Generate SSL certificates
generate_ssl_certs() {
log_info "[6/10] Generating SSL certificates..."
openssl req -x509 -nodes -days 365 -newkey rsa:4096 \
-keyout /etc/vault.d/vault.key \
-out /etc/vault.d/vault.crt \
-subj "/C=US/ST=State/L=City/O=Organization/CN=${VAULT_DOMAIN}" \
-addext "subjectAltName = DNS:${VAULT_DOMAIN},DNS:localhost,IP:127.0.0.1"
chown vault:vault /etc/vault.d/vault.*
chmod 640 /etc/vault.d/vault.key
chmod 644 /etc/vault.d/vault.crt
}
# Create Vault configuration
create_vault_config() {
log_info "[7/10] Creating Vault configuration..."
cat > /etc/vault.d/vault.hcl << EOF
ui = true
api_addr = "https://${VAULT_DOMAIN}:8200"
cluster_addr = "https://${VAULT_DOMAIN}:8201"
storage "file" {
path = "/opt/vault/data"
}
listener "tcp" {
address = "0.0.0.0:8200"
cluster_address = "0.0.0.0:8201"
tls_cert_file = "/etc/vault.d/vault.crt"
tls_key_file = "/etc/vault.d/vault.key"
tls_min_version = "tls12"
}
seal "awskms" {
region = "${AWS_REGION}"
kms_key_id = "alias/vault-unseal"
endpoint = "https://kms.${AWS_REGION}.amazonaws.com"
}
log_level = "INFO"
log_file = "/opt/vault/logs/vault.log"
log_rotate_duration = "24h"
log_rotate_max_files = 30
default_lease_ttl = "168h"
max_lease_ttl = "720h"
cluster_name = "vault-prod"
EOF
chown root:vault /etc/vault.d/vault.hcl
chmod 640 /etc/vault.d/vault.hcl
}
# Create systemd service
create_systemd_service() {
log_info "[8/10] Creating systemd service..."
cat > /etc/systemd/system/vault.service << 'EOF'
[Unit]
Description=HashiCorp Vault
Documentation=https://www.vaultproject.io/docs/
Requires=network-online.target
After=network-online.target
ConditionFileNotEmpty=/etc/vault.d/vault.hcl
[Service]
Type=notify
User=vault
Group=vault
ProtectSystem=full
ProtectHome=read-only
PrivateTmp=yes
PrivateDevices=yes
SecureBits=keep-caps
AmbientCapabilities=CAP_IPC_LOCK
CapabilityBoundingSet=CAP_SYSLOG CAP_IPC_LOCK
NoNewPrivileges=yes
ExecStart=/usr/local/bin/vault server -config=/etc/vault.d/vault.hcl
ExecReload=/bin/kill --signal HUP $MAINPID
KillMode=process
Restart=on-failure
RestartSec=5
TimeoutStopSec=30
StartLimitInterval=60
StartLimitBurst=3
LimitNOFILE=65536
LimitMEMLOCK=infinity
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable vault
}
# Configure firewall
configure_firewall() {
log_info "[9/10] Configuring firewall..."
if [[ "$FIREWALL_CMD" == "ufw" ]]; then
if command -v ufw >/dev/null 2>&1 && ufw status | grep -q "Status: active"; then
ufw allow 8200/tcp
ufw allow 8201/tcp
fi
elif [[ "$FIREWALL_CMD" == "firewall-cmd" ]]; then
if systemctl is-active --quiet firewalld; then
firewall-cmd --permanent --add-port=8200/tcp
firewall-cmd --permanent --add-port=8201/tcp
firewall-cmd --reload
fi
fi
}
# Verify installation
verify_installation() {
log_info "[10/10] Verifying installation..."
# Check Vault binary
if ! vault --version; then
log_error "Vault binary verification failed"
exit 1
fi
# Check configuration syntax
if ! sudo -u vault vault validate /etc/vault.d/vault.hcl; then
log_error "Vault configuration validation failed"
exit 1
fi
# Start Vault service
systemctl start vault
# Check service status
if ! systemctl is-active --quiet vault; then
log_error "Vault service failed to start"
journalctl -u vault --no-pager
exit 1
fi
log_info "✓ Vault installation completed successfully!"
log_warn "Next steps:"
log_warn "1. Configure AWS CLI: sudo -u vault aws configure"
log_warn "2. Create KMS key: aws kms create-key --description 'Vault Auto-Unseal Key'"
log_warn "3. Create alias: aws kms create-alias --alias-name alias/vault-unseal --target-key-id YOUR_KEY_ID"
log_warn "4. Initialize Vault: vault operator init"
log_warn "5. Access Vault UI at: https://${VAULT_DOMAIN}:8200"
}
# Main execution
main() {
check_prerequisites
detect_distro
install_packages
install_vault
create_vault_user
configure_aws
generate_ssl_certs
create_vault_config
create_systemd_service
configure_firewall
verify_installation
}
main "$@"
Review the script before running. Execute with: bash install.sh