Vault Enterprise Setup

After running the Terraform to deploy primary, disaster recovery, and performance replication clusters, do the following to initialise the vaults and setup replication:

Initialise the Vault and add the license

For all 3 clusters run:

vault operator init -recovery-shares=1 -recovery-threshold=1 -recovery-pgp-keys=keybase:hashicorpchip
vault write sys/license text=<license>

You should save the outputted recovery tokens to a safe place, for example AWS SSM Parameter Store

Setup disaster recover

Use the root tokens generated above to run the following steps across the 3 clusters.

On the primary cluster run:

vault write sys/replication/dr/primary/secondary-token id="secondary"

Then using the token output above, on the DR cluster run:

vault write sys/replication/dr/secondary/enable token=<token>

Setup performance replication

On the primary cluster run:

vault write sys/replication/performance/primary/secondary-token id=eucentral

Then using the token output above, on the performacne replication cluster run:

vault write sys/replication/performance/secondary/enable token=<token>

Configure Vault

You can then use this Terraform configuration to setup the basic Vault config.

Using the root token for the primary cluster run:

terraform init
terraform apply -target=vault_audit.syslog -target=vault_auth_backend.userpass -target=vault_namespace.namespace -target=vault_policy.admin -target=vault_generic_endpoint.admin

This will:

  • Enable the syslog audit device
  • Enable userpass authentication backend
  • Create 3 namespaces (dev, security, prod)
  • Create an admin user in the userpass auth backend which can be used instead of the root token to run the rest of the Terraform configuration

With the admin user created you should revoke the root token on the primary cluster:

vault token revoke <root token>

Run the rest of the Terraform with the following command to setup the rest of the policies and local transit mount on the EU cluster:

VAULT_TOKEN=$(vault login -method=userpass username=admin -format=json | jq -r '.auth.client_token') TF_VAR_eu_token=$(VAULT_ADDR=<performance replication cluster address> vault login -method=userpass username=admin -format=json | jq -r '.auth.client_token') terraform apply

Configure Vault Agent

Vault agent will need to be installed and configured on the application servers.

US and Europe application servers

Follow the instructions in the Vault Documentation to download the Vault binary.

Add the following to /etc/systemd/system/vault.server to configure the Vault service:

[Unit]
Description="HashiCorp Vault"
Documentation=https://www.hashicorp.com/
Requires=network-online.target
After=network-online.target
ConditionFileNotEmpty=/etc/vault/agent.hcl
StartLimitIntervalSec=240
StartLimitBurst=3

[Service]
User=root
Group=root
ExecStart=/usr/local/bin/vault agent -config=/etc/vault/agent.hcl
ExecReload=/bin/kill --signal HUP $MAINPID
KillMode=process
Restart=on-failure
LimitNOFILE=65536
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
KillSignal=SIGINT
Restart=on-failure
RestartSec=5
TimeoutStopSec=30
StartLimitInterval=60
StartLimitIntervalSec=60
StartLimitBurst=3
LimitMEMLOCK=infinity

[Install]
WantedBy=multi-user.target

US application server

Add the following Vault agent configuration to /etc/vault/agent.hcl:

pid_file = "/opt/vault/pidfile"

vault {
    address = "<vault load balancer address>:8200"
}

auto_auth {
    method "aws" {
        mount_path = "auth/aws"

        config = {
            type = "iam"
            role = "erp-us"
        }
    }

    sink "file" {
        config = {
            path = "/opt/flask/token"
            mode = 0665
        }
    }
}

cache {
    use_auto_auth_token = true
}

listener "tcp" {
    address = "127.0.0.1:8100"
    tls_disable = true
}

template {
    command = "systemctl restart flask"
    source = "/opt/flask/mysqldbcreds.json.tpl"
    destination = "/opt/flask/mysqldbcreds.json"
}

template {
    command = "systemctl restart flask"
    source = "/opt/flask/awscreds.json.tpl"
    destination = "/opt/flask/awscreds.json"
}

Setup the mysql dynamic credentials template at /opt/flask/mysqldbcreds.json.tpl:

{{ with secret "mysql-us/creds/erp" }}
{
  "username": "{{ .Data.username }}",
  "password": "{{ .Data.password }}",
  "hostname": "<US RDS endpoint>"
}
{{ end }}

And setup the AWS dynamic credentials template at /opt/flask/awscreds.json.tpl:

{{ with secret "aws/creds/s3-read-write" }}
{
  "ACCESS_KEY": "{{ .Data.access_key }}",
  "SECRET_KEY": "{{ .Data.secret_key }}"
}
{{ end }}

To configure support for encryption / decryption, install jq and change the contents of /opt/flask/vaulthook.sh to:

#!/bin/bash
command=$1
shift 1

if [ $command = "encrypt" ]; then
  text=$(VAULT_TOKEN=$(cat /opt/flask/token) VAULT_ADDR=http://localhost:8100 vault write transit/encrypt/erp plaintext="$(base64 <<< $@)" -format=json | jq -r '.data.ciphertext')
else
  text=$(base64 --decode <<< $(VAULT_TOKEN=$(cat /opt/flask/token) VAULT_ADDR=http://localhost:8100 vault write transit/decrypt/erp ciphertext="$@" -format=json | jq -r '.data.plaintext'))
fi

echo $text

EU application server

This is very similar to the process for the US application above, except the content of the files is slightly different to account for different roles and capabilities between the US and EU applications.

Add the following Vault agent configuration to /etc/vault/agent.hcl:

pid_file = "/opt/vault/pidfile"

vault {
    address = "<performance replicaiton cluster address>:8200"
}

auto_auth {
    method "aws" {
        mount_path = "auth/aws"
        config = {
            type = "iam"
            role = "erp-eu"
        }
    }

    sink "file" {
        config = {
            path = "/opt/flask/token"
            mode = 0665
        }
    }
}

cache {
    use_auto_auth_token = true
}

listener "tcp" {
    address = "127.0.0.1:8100"
    tls_disable = true
}

template {
    command = "systemctl restart flask"
    source = "/opt/flask/mysqldbcreds.json.tpl"
    destination = "/opt/flask/mysqldbcreds.json"
}

template {
    command = "systemctl restart flask"
    source = "/opt/flask/awscreds.json.tpl"
    destination = "/opt/flask/awscreds.json"
}

Setup the mysql dynamic credentials template at /opt/flask/mysqldbcreds.json.tpl:

{{ with secret "mysql-eu/creds/erp" }}
{
  "username": "{{ .Data.username }}",
  "password": "{{ .Data.password }}",
  "hostname": "<EU RDS endpoint>"
}
{{ end }}

And setup the AWS dynamic credentials template at /opt/flask/awscreds.json.tpl:

{{ with secret "aws/creds/s3-read-only" }}
{
  "ACCESS_KEY": "{{ .Data.access_key }}",
  "SECRET_KEY": "{{ .Data.secret_key }}"
}
{{ end }}

To configure support for encryption / decryption, install jq and change the contents of /opt/flask/vaulthook.sh to:

#!/bin/bash
command=$1
shift 1

if [ $command = "encrypt" ]; then
  text=$(VAULT_TOKEN=$(cat /opt/flask/token) VAULT_ADDR=http://localhost:8100 vault write transit/encrypt/erp plaintext="$(base64 <<< $@)" -format=json | jq -r '.data.ciphertext')
else
  text=$(base64 --decode <<< $(VAULT_TOKEN=$(cat /opt/flask/token) VAULT_ADDR=http://localhost:8100 vault write transit/decrypt/erp ciphertext="$@" -format=json | jq -r '.data.plaintext'))
fi

echo $text