In our tutorial, we will be using embedded integrated storage.
# Vault cluster
resource "digitalocean_droplet" "vault-nl-1" {
image = var.ol7_base_image
name = "vault-nl-1.example.com"
region = "ams3"
size = var.size
tags = ["vault"]
monitoring = true
}
resource "digitalocean_droplet" "vault-de-1" {
image = var.ol7_base_image
name = "vault-de-1.example.com"
region = "fra1"
size = var.size
tags = ["vault"]
monitoring = true
}
resource "digitalocean_droplet" "vault-uk-1" {
image = var.ol7_base_image
name = "vault-uk-1.example.com"
region = "lon1"
size = var.size
tags = ["vault"]
monitoring = true
}
variable "size" {
description = "Droplet size"
default = "s-1vcpu-1gb"
}
variable "ol7_base_image" {
description = "Base ol7 DO image made by packer"
default = 312166483
type = number
}
resource "digitalocean_firewall" "general-firewall" {
name = "general-firewall"
tags = [ "vault" ]
inbound_rule {
protocol = "icmp"
source_addresses = ["0.0.0.0/0", "::/0"]
}
inbound_rule {
protocol = "tcp"
port_range = "22"
source_addresses = var.bastion_ip_add
}
outbound_rule {
protocol = "udp"
port_range = "all"
destination_addresses = ["0.0.0.0/0", "::/0"]
}
outbound_rule {
protocol = "icmp"
destination_addresses = ["0.0.0.0/0", "::/0"]
}
outbound_rule {
protocol = "tcp"
port_range = "all"
destination_addresses = ["0.0.0.0/0", "::/0"]
}
}
variable "bastion_ip_add" {
description = "List of bastion IP addresses"
default = ["144.85.22.183/32",
"12.68.22.226/32",
]
}
variable "internal_vpn_ip_add" {
description = "List of internal VPN IP addresses"
default = ["122.85.77.183/32",
"44.68.2.226/32",
]
}
variable "customer_customer_1_ip_add" {
description = "List of customer_1 IP addresses"
default = ["158.63.250.15/32",
"158.63.250.16/32",
]
}
inbound_rule {
protocol = "tcp"
port_range = "8200"
source_addresses = setunion(var.internal_vpn_ip_add, var.bastion_ip_add, var.customer_customer_1_ip_add)
}
terraform init
terraform plan
terraform apply
- name: Prepare certificates
hosts: hashicorp_vault
gather_facts: true
become: yes
roles:
- role: letsencrypt-ssl
tags: letsencrypt
post_tasks:
- name: Copy the certificates to the vault config dir for the first time
shell: |
rsync -L /etc/letsencrypt/live/{{ inventory_hostname }}/privkey.pem /etc/vault.d/privkey.pem
rsync -L /etc/letsencrypt/live/{{ inventory_hostname }}/fullchain.pem /etc/vault.d/fullchain.pem
chown vault:vault /etc/vault.d/privkey.pem
chown vault:vault /etc/vault.d/fullchain.pem
pkill -SIGHUP vault
args:
creates: /etc/vault.d/fullchain.pem
tags: letsencrypt
- name: Deploy hashicorp vault cluster
hosts: hashicorp_vault
gather_facts: true
become: yes
roles:
- role: ansible-community.ansible-vault
tags: vault_install
vault_version: "1.12.0"
vault_install_hashi_repo: true
vault_harden_file_perms: true
vault_service_restart: false
# listeners configuration
vault_api_addr: "{{ vault_protocol }}://{{ inventory_hostname }}:{{ vault_port }}"
vault_tls_disable: false
vault_tls_certs_path: /etc/vault.d
vault_tls_private_path: /etc/vault.d
vault_tls_cert_file: fullchain.pem
vault_tls_key_file: privkey.pem
vault_tls_min_version: "tls12"
vault_raft_cluster_members:
- peer: hasd-vault-nl-1.itsts.net
api_addr: https://vault-nl-1.example.com:8200
- peer: hasd-vault-de-1.itsts.net
api_addr: https://vault-de-1.example.com:8200
- peer: hasd-vault-uk-1.itsts.net
api_addr: https://vault-uk-1.example.com:8200
# Ansible managed
cluster_name = "dc1"
max_lease_ttl = "768h"
default_lease_ttl = "768h"
disable_clustering = "False"
cluster_addr = "https://333.222.10.107:8201"
api_addr = "https://vault-nl-1.example.com:8200"
plugin_directory = "/usr/local/lib/vault/plugins"
listener "tcp" {
address = "333.222.10.107:8200"
cluster_address = "333.222.10.107:8201"
tls_cert_file = "/etc/vault.d/fullchain.pem"
tls_key_file = "/etc/vault.d/privkey.pem"
tls_min_version = "tls12"
tls_disable = "false"
}
listener "tcp" {
address = "127.0.0.1:8200"
cluster_address = "333.222.10.107:8201"
tls_cert_file = "/etc/vault.d/fullchain.pem"
tls_key_file = "/etc/vault.d/privkey.pem"
tls_min_version = "tls12"
tls_disable = "false"
}
storage "raft" {
path = "/opt/vault/data"
node_id = "hasd-vault-nl-1"
retry_join {
leader_api_addr = "https://vault-de-1.example.com:8200"
}
retry_join {
leader_api_addr = "https://vault-uk-1.example.com:8200"
}
}
// HashiCorp recommends disabling mlock when using Raft.
disable_mlock = true
ui = true
Be extremely careful at this stage, it is vitally important!
Lost Shamir keys = permanently locked storage after a daemon restart.
rsync -L /etc/letsencrypt/live/{{ inventory_hostname }}/privkey.pem /etc/vault.d/privkey.pem
rsync -L /etc/letsencrypt/live/{{ inventory_hostname }}/fullchain.pem /etc/vault.d/fullchain.pem
chown vault:vault /etc/vault.d/privkey.pem
chown vault:vault /etc/vault.d/fullchain.pem
pkill -SIGHUP vault
#!/bin/bash
LEADER=$(vault status -format=json | jq -r '.leader_address')
HOSTNAME=$(hostname)
ROLE_ID_VAULT_MAINTENANCE={{ vault_maintenance_role_id }}
# Get the secret ID for the vault_maintenance_snapshot AppRole
SECRET_ID_VAULT_MAINTENANCE=$(cat /root/approle_vault_maintenance_snapshot_secret_id)
# Check if this node is the leader and if the hostname matches
if [[ "$LEADER" == "https://$HOSTNAME:8200" ]]; then
TOKEN=$(vault write auth/approle/login --format=json role_id=$ROLE_ID_VAULT_MAINTENANCE secret_id="$SECRET_ID_VAULT_MAINTENANCE" | jq -r '.auth.client_token')
vault login $TOKEN >/dev/null 2>&1
vault operator raft snapshot save {{ vault_snapshot_location }}
# Send the result to the cloud backup. This will happen on the leader node only
/root/scripts/duplicity-backup.sh -c /root/scripts/{{ duplicity_config_name_s3 }}.conf -b > /dev/null
fi
path "sys/storage/raft/snapshot"
{
capabilities = ["read", "update"]
}
path "auth/token/revoke"
{
capabilities = ["update"]
}