Install HashiCorp Vault with Docker: Secrets, Policies and Terraform

· 2 min read · Terraform & OpenTofu Tutorials

What is Vault #

Vault stores secrets and controls who can read them. Instead of putting a database password in a file, an application authenticates to Vault and reads it at runtime, and every access is audited. In this tutorial you run Vault in a container, write secrets, create a restrictive policy and read a secret from Terraform.

Run Vault #

docker run -d --name vault --cap-add=IPC_LOCK -p 8200:8200 \
  -e VAULT_DEV_ROOT_TOKEN_ID=dev-token \
  hashicorp/vault

Install the vault CLI (see Packer for the HashiCorp repository) or run it inside the container with docker exec. Point it to the server:

export VAULT_ADDR=http://127.0.0.1:8200
export VAULT_TOKEN=dev-token
vault status

The web interface is at http://localhost:8200 (sign in with the token).

Store and read secrets #

Dev mode mounts a KV version 2 engine at secret/.

vault kv put -mount=secret myapp username=app password=s3cr3t
vault kv get -mount=secret myapp
vault kv get -mount=secret -field=password myapp
vault kv put -mount=secret myapp username=app password=n3w   # new version
vault kv get -mount=secret -version=1 myapp

KV v2 keeps previous versions, so a change can be rolled back.

Policies and tokens #

A policy lists paths and what can be done with them. The default root token can do everything, so create a token for an application that can only read one path:

myapp-read.hcl
path "secret/data/myapp" {
  capabilities = ["read"]
}
vault policy write myapp-read myapp-read.hcl
vault token create -policy=myapp-read -ttl=1h

Use the new token and check the limits:

VAULT_TOKEN=<new token> vault kv get -mount=secret myapp                  # works
VAULT_TOKEN=<new token> vault kv put -mount=secret myapp password=x       # permission denied

Note that KV v2 paths contain data/ in policies and in the HTTP API.

The HTTP API #

curl -s -H "X-Vault-Token: $VAULT_TOKEN" $VAULT_ADDR/v1/secret/data/myapp | jq .data.data

Any language can use this API, which is why Vault works with every platform. See jq for the filter.

Authentication methods for applications #

Humans use a token or OIDC. Machines should not hold a long-lived token: use AppRole, the Kubernetes method or the AWS method, where the workload proves its identity with its service account or IAM role and gets a short-lived token.

vault auth enable approle
vault write auth/approle/role/myapp token_policies=myapp-read token_ttl=15m
vault read auth/approle/role/myapp/role-id
vault write -f auth/approle/role/myapp/secret-id

Read a secret in Terraform #

main.tf
provider "vault" {
  address = "http://127.0.0.1:8200"
  # token comes from the VAULT_TOKEN variable
}

data "vault_kv_secret_v2" "app" {
  mount = "secret"
  name  = "myapp"
}

output "username" {
  value = data.vault_kv_secret_v2.app.data["username"]
}

Secrets read this way end up in the Terraform state, so protect the state (state encryption and secrets management). For values that must not be stored, use ephemeral resources in recent Terraform versions or inject secrets at runtime.

Alternatives #

On AWS, Secrets Manager and KMS cover most needs without running a server. Vault is the choice for multi-cloud, dynamic credentials and one place to audit access.

Clean up #

docker rm -f vault

#Vault #Security #Docker