HashiCorp Vault — Policies Exercises

3 minute read

Practice writing and testing Vault policies.


Exercise 1 — Least Privilege Policy

Task: Write a policy for a web application that needs to:

  • Read secrets under secret/data/webapp/*
  • List secret keys under secret/metadata/webapp/*
  • Encrypt/decrypt using the webapp-key transit key
  • NOT access any other path
Show Answer
# webapp-policy.hcl
path "secret/data/webapp/*" {
  capabilities = ["read"]
}

path "secret/metadata/webapp/*" {
  capabilities = ["list", "read"]
}

path "transit/encrypt/webapp-key" {
  capabilities = ["update"]
}

path "transit/decrypt/webapp-key" {
  capabilities = ["update"]
}
# Deploy and test
vault policy write webapp webapp-policy.hcl
TEST_TOKEN=$(vault token create -policy=webapp -ttl=5m -field=token)

# Should succeed
VAULT_TOKEN=$TEST_TOKEN vault kv get secret/webapp/config

# Should fail
VAULT_TOKEN=$TEST_TOKEN vault kv get secret/admin/config
# Error: permission denied

vault token revoke $TEST_TOKEN

Exercise 2 — Team Isolation

Task: Create policies for two teams: alpha and bravo. Each team should:

  • Have full CRUD on their own path (secret/data/teams/<team>/*)
  • Be able to read shared secrets (secret/data/shared/*)
  • NOT be able to access the other team’s secrets
Show Answer
# team-alpha.hcl
path "secret/data/teams/alpha/*" {
  capabilities = ["create", "read", "update", "delete", "list"]
}
path "secret/metadata/teams/alpha/*" {
  capabilities = ["list", "read"]
}
path "secret/data/shared/*" {
  capabilities = ["read"]
}
path "secret/metadata/shared/*" {
  capabilities = ["list"]
}
path "secret/data/teams/bravo/*" {
  capabilities = ["deny"]
}
# team-bravo.hcl
path "secret/data/teams/bravo/*" {
  capabilities = ["create", "read", "update", "delete", "list"]
}
path "secret/metadata/teams/bravo/*" {
  capabilities = ["list", "read"]
}
path "secret/data/shared/*" {
  capabilities = ["read"]
}
path "secret/metadata/shared/*" {
  capabilities = ["list"]
}
path "secret/data/teams/alpha/*" {
  capabilities = ["deny"]
}
vault policy write team-alpha team-alpha.hcl
vault policy write team-bravo team-bravo.hcl

# Test isolation
ALPHA_TOKEN=$(vault token create -policy=team-alpha -ttl=5m -field=token)
VAULT_TOKEN=$ALPHA_TOKEN vault kv put secret/teams/alpha/db password=abc
# Success!

VAULT_TOKEN=$ALPHA_TOKEN vault kv get secret/teams/bravo/db
# Error: permission denied

Exercise 3 — Templated Policy

Task: Write a single templated policy that gives every user access to their own namespace under secret/data/users/<username>/*.

Show Answer
# user-workspace.hcl
path "secret/data/users/{{identity.entity.name}}/*" {
  capabilities = ["create", "read", "update", "delete", "list"]
}

path "secret/metadata/users/{{identity.entity.name}}/*" {
  capabilities = ["list", "read"]
}
vault policy write user-workspace user-workspace.hcl

# Create users with the policy
vault write auth/userpass/users/alice password="pass" policies="user-workspace"
vault write auth/userpass/users/bob password="pass" policies="user-workspace"

# Login as alice
vault login -method=userpass username=alice password="pass"
vault kv put secret/users/alice/mykey value=mysecret
# Success!

vault kv get secret/users/bob/mykey
# Error: permission denied

Exercise 4 — Sudo and Deny

Task: Write an admin policy that can:

  • Manage all secrets
  • Manage policies
  • Enable/disable auth methods
  • But CANNOT seal Vault or step down the leader
Show Answer
# admin-restricted.hcl

# Manage all secrets
path "secret/*" {
  capabilities = ["create", "read", "update", "delete", "list"]
}

# Manage policies (requires sudo)
path "sys/policies/acl/*" {
  capabilities = ["create", "read", "update", "delete", "list", "sudo"]
}
path "sys/policies/acl" {
  capabilities = ["list"]
}

# Manage auth methods (requires sudo)
path "sys/auth/*" {
  capabilities = ["create", "read", "update", "delete", "list", "sudo"]
}
path "sys/auth" {
  capabilities = ["read"]
}

# Explicitly deny seal and step-down
path "sys/seal" {
  capabilities = ["deny"]
}
path "sys/step-down" {
  capabilities = ["deny"]
}
vault policy write admin-restricted admin-restricted.hcl
ADMIN_TOKEN=$(vault token create -policy=admin-restricted -ttl=1h -field=token)

# Can manage policies
VAULT_TOKEN=$ADMIN_TOKEN vault policy write test - <<EOF
path "secret/data/test/*" { capabilities = ["read"] }
EOF
# Success!

# Cannot seal vault
VAULT_TOKEN=$ADMIN_TOKEN vault operator seal
# Error: permission denied

Exercise 5 — Policy Debugging

Task: A user reports they can’t read secret/data/myapp/database. Debug the issue.

Given policy:

path "secret/data/myapp" {
  capabilities = ["read", "list"]
}

Why can’t they read secret/data/myapp/database?

Show Answer

The policy grants access to secret/data/myapp (exact path) but NOT secret/data/myapp/database (a sub-path).

Fix — add a glob wildcard:

path "secret/data/myapp/*" {
  capabilities = ["read", "list"]
}

Or to include both the path itself and sub-paths:

# The exact path
path "secret/data/myapp" {
  capabilities = ["read", "list"]
}

# Everything below it
path "secret/data/myapp/*" {
  capabilities = ["read", "list"]
}

Debug with:

vault token capabilities secret/data/myapp/database
# deny (no matching policy)

What’s Next?

With authentication and authorization covered, it’s time for the powerful feature that sets Vault apart — dynamic secrets.

Next Chapter: Dynamic Secrets