HashiCorp Vault — Advanced Exercises

3 minute read

Practice advanced Vault features with these exercises.


Exercise 1 — Response Wrapping

Task: Securely share a database password using response wrapping.

  1. Store a database password in KV
  2. Read it with a wrapping TTL
  3. Verify the wrapping token status (lookup)
  4. Unwrap the secret
  5. Verify the token cannot be reused
Show Answer
# Store secret
vault kv put secret/prod/db password=super-s3cret

# Wrap the read with 1-hour TTL
vault kv get -wrap-ttl=1h secret/prod/db
# wrapping_token: hvs.CAES...

# Lookup wrapping token (without unwrapping)
vault write sys/wrapping/lookup token=hvs.CAES...
# Shows creation time, TTL, path — confirms it's valid

# Unwrap
VAULT_TOKEN=hvs.CAES... vault unwrap
# Reveals: password=super-s3cret ✅

# Try to unwrap again
VAULT_TOKEN=hvs.CAES... vault unwrap
# Error: wrapping token is not valid ❌ (single-use!)

Exercise 2 — Identity Entities and Groups

Task: Create entities and groups to unify access across auth methods.

  1. Create two entities: alice and bob
  2. Create an internal group developers with both entities
  3. Attach a policy to the group
  4. Verify group policies apply to members
Show Answer
# Create entities
vault write identity/entity name="alice" policies="personal-alice"
vault write identity/entity name="bob" policies="personal-bob"

# Get entity IDs
ALICE_ID=$(vault read -field=id identity/entity/name/alice)
BOB_ID=$(vault read -field=id identity/entity/name/bob)

# Create a policy for the group
vault policy write developers - <<EOF
path "secret/data/dev/*" {
  capabilities = ["create", "read", "update", "delete", "list"]
}
EOF

# Create group
vault write identity/group name="developers" \
    policies="developers" \
    member_entity_ids="$ALICE_ID,$BOB_ID"

# Verify
vault read identity/group/name/developers
# Should show both members and the "developers" policy

# When alice authenticates, she gets:
# - "personal-alice" (from entity)
# - "developers" (from group membership)

Exercise 3 — Wrapped AppRole Secret ID

Task: Securely deliver an AppRole secret_id using response wrapping.

  1. Create an AppRole ci-cd
  2. Generate a wrapped secret_id
  3. Simulate the CI/CD pipeline unwrapping and authenticating
Show Answer
# Create AppRole
vault auth enable approle 2>/dev/null
vault policy write ci-cd - <<EOF
path "secret/data/ci/*" { capabilities = ["read"] }
EOF

vault write auth/approle/role/ci-cd \
    secret_id_ttl=10m token_ttl=30m policies=ci-cd

# Get role_id (not sensitive — can be embedded)
ROLE_ID=$(vault read -field=role_id auth/approle/role/ci-cd/role-id)

# Generate WRAPPED secret_id (single-use delivery)
WRAPPED=$(vault write -wrap-ttl=5m -f -field=wrapping_token \
    auth/approle/role/ci-cd/secret-id)

echo "Role ID: $ROLE_ID"
echo "Wrapped Secret ID: $WRAPPED"

# --- CI/CD pipeline does this ---

# Unwrap to get the actual secret_id
SECRET_ID=$(VAULT_TOKEN=$WRAPPED vault unwrap -field=secret_id)

# Authenticate
vault write auth/approle/login \
    role_id=$ROLE_ID \
    secret_id=$SECRET_ID
# Returns a Vault token with ci-cd policy ✅

Exercise 4 — Identity Templated Policy

Task: Create a policy that restricts users to their own secret path using identity templates.

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

path "secret/metadata/users/{{identity.entity.name}}/*" {
  capabilities = ["list", "read", "delete"]
}

# Deny access to other users' paths
path "secret/data/users/*" {
  capabilities = ["deny"]
}
vault policy write user-workspace user-workspace.hcl

# Assign to all entities or a group
vault write identity/group/name/all-users \
    policies="user-workspace" \
    member_entity_ids="$ALICE_ID,$BOB_ID"

# Alice can write to secret/users/alice/*
# Alice CANNOT write to secret/users/bob/*

Exercise 5 — Cubbyhole Pattern

Task: Use the cubbyhole secrets engine for temporary secret storage.

Show Answer
# Cubbyhole is per-token — private storage
# Create a token for the app
APP_TOKEN=$(vault token create -policy=default -ttl=1h -field=token)

# Store a secret in cubbyhole (only this token can read it)
VAULT_TOKEN=$APP_TOKEN vault write cubbyhole/my-temp-secret \
    api_key=temp-12345

# Read it back (same token)
VAULT_TOKEN=$APP_TOKEN vault read cubbyhole/my-temp-secret
# ✅ Works

# Try with a different token
vault read cubbyhole/my-temp-secret
# ❌ Empty — cubbyhole is per-token

# When the token expires, cubbyhole is destroyed
vault token revoke $APP_TOKEN
# Cubbyhole and its secrets are gone

Key takeaway: Cubbyhole is scoped to a single token. When the token is revoked or expires, the cubbyhole is destroyed. Response wrapping uses cubbyhole internally.


What’s Next?

Advanced features covered. Now let’s put it all together for Production Hardening — security best practices, backup strategies, and operational procedures.

Next Chapter: Production Hardening