HashiCorp Vault — Dynamic Secrets Exercises
Practice dynamic secrets with these hands-on exercises.
Exercise 1 — PostgreSQL Dynamic Credentials
Task: Set up dynamic PostgreSQL credentials and test the full lifecycle.
- Start a PostgreSQL container
- Enable the database secrets engine
- Configure the PostgreSQL connection
- Create a read-only role
- Generate credentials and connect to the database
- Verify you can SELECT but cannot INSERT
- Revoke the lease and verify access is denied
Show Answer
# 1. Start PostgreSQL
docker run -d --name pg-test \
-e POSTGRES_USER=vault_admin \
-e POSTGRES_PASSWORD=vault_pass \
-e POSTGRES_DB=testdb \
-p 5432:5432 postgres:16
# Create a test table
psql -h 127.0.0.1 -U vault_admin -d testdb -c \
"CREATE TABLE users (id SERIAL, name TEXT); INSERT INTO users (name) VALUES ('Alice');"
# 2. Enable database engine
vault secrets enable database
# 3. Configure connection
vault write database/config/pg-test \
plugin_name="postgresql-database-plugin" \
allowed_roles="pg-ro" \
connection_url="postgresql://:@127.0.0.1:5432/testdb?sslmode=disable" \
username="vault_admin" \
password="vault_pass"
# 4. Create read-only role
vault write database/roles/pg-ro \
db_name="pg-test" \
creation_statements="CREATE ROLE \"\" WITH LOGIN PASSWORD '' VALID UNTIL ''; GRANT SELECT ON ALL TABLES IN SCHEMA public TO \"\";" \
revocation_statements="REVOKE ALL ON ALL TABLES IN SCHEMA public FROM \"\"; DROP ROLE IF EXISTS \"\";" \
default_ttl="10m" \
max_ttl="1h"
# 5. Generate credentials
CREDS=$(vault read -format=json database/creds/pg-ro)
USERNAME=$(echo $CREDS | jq -r .data.username)
PASSWORD=$(echo $CREDS | jq -r .data.password)
LEASE_ID=$(echo $CREDS | jq -r .lease_id)
# 6. Test access
psql -h 127.0.0.1 -U "$USERNAME" -d testdb -c "SELECT * FROM users;"
# Should succeed
psql -h 127.0.0.1 -U "$USERNAME" -d testdb -c "INSERT INTO users (name) VALUES ('Bob');"
# Should fail: permission denied
# 7. Revoke lease
vault lease revoke "$LEASE_ID"
psql -h 127.0.0.1 -U "$USERNAME" -d testdb -c "SELECT 1;"
# Should fail: role does not exist / password authentication failed
Exercise 2 — Lease Lifecycle
Task: Explore lease renewal and expiration behavior.
- Generate database credentials with TTL=2m, max_ttl=5m
- Look up the lease
- Wait 1 minute, then renew the lease
- Try to renew past the max_ttl
- Let the lease expire and verify credentials stop working
Show Answer
# Create a role with short TTL for testing
vault write database/roles/pg-short \
db_name="pg-test" \
creation_statements="CREATE ROLE \"\" WITH LOGIN PASSWORD '' VALID UNTIL ''; GRANT SELECT ON ALL TABLES IN SCHEMA public TO \"\";" \
revocation_statements="DROP ROLE IF EXISTS \"\";" \
default_ttl="2m" \
max_ttl="5m"
# 1. Generate credentials
CREDS=$(vault read -format=json database/creds/pg-short)
LEASE_ID=$(echo $CREDS | jq -r .lease_id)
# 2. Lookup lease
vault lease lookup "$LEASE_ID"
# ttl: ~2m
# 3. Wait 1 minute, then renew
sleep 60
vault lease renew "$LEASE_ID"
# TTL reset to ~2m from now
# 4. After a few minutes, try to renew past max_ttl
sleep 120
vault lease renew -increment=10m "$LEASE_ID"
# Will be capped at max_ttl (5m from original issue)
# 5. Wait for expiration
# After max_ttl (5m from original issue), the credentials are revoked
# Test with psql — should fail
Exercise 3 — AWS Dynamic Credentials
Task: (Conceptual — requires AWS account) Set up dynamic AWS IAM credentials.
- Enable the AWS secrets engine
- Configure root credentials
- Create a role for S3 read-only access
- Generate credentials
- Use them with the AWS CLI
Show Answer
# 1. Enable AWS engine
vault secrets enable aws
# 2. Configure
vault write aws/config/root \
access_key="YOUR_ACCESS_KEY" \
secret_key="YOUR_SECRET_KEY" \
region="us-east-1"
# 3. Create S3 read-only role
vault write aws/roles/s3-reader \
credential_type="iam_user" \
policy_document=-<<EOF
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": ["s3:GetObject", "s3:ListBucket"],
"Resource": ["arn:aws:s3:::*"]
}]
}
EOF
# 4. Generate credentials
vault read aws/creds/s3-reader
# Returns access_key, secret_key
# 5. Use with AWS CLI
export AWS_ACCESS_KEY_ID="<returned_access_key>"
export AWS_SECRET_ACCESS_KEY="<returned_secret_key>"
aws s3 ls
# 6. Cleanup
vault lease revoke -prefix aws/creds/s3-reader
Exercise 4 — Prefix Revocation
Task: Practice emergency credential revocation.
- Generate 5 sets of database credentials
- List all active leases
- Revoke all leases for the role using prefix revocation
- Verify all credentials are invalidated
Show Answer
# 1. Generate 5 credentials
for i in $(seq 1 5); do
vault read database/creds/pg-ro
echo "---"
done
# 2. List active leases
vault list sys/leases/lookup/database/creds/pg-ro
# Shows 5 lease IDs
# Count them
vault list -format=json sys/leases/lookup/database/creds/pg-ro | jq '. | length'
# 5
# 3. Revoke all by prefix
vault lease revoke -prefix database/creds/pg-ro
# 4. Verify
vault list sys/leases/lookup/database/creds/pg-ro
# No keys found (empty)
# All 5 database users have been dropped
Exercise 5 — Static Roles
Task: Set up a static role for password rotation (no new users, just rotating an existing user’s password).
- Create a PostgreSQL user manually
- Configure a static role for that user with 1-minute rotation
- Read the current credentials
- Wait for rotation
- Read again and verify the password changed
Show Answer
# 1. Create user manually
psql -h 127.0.0.1 -U vault_admin -d testdb -c \
"CREATE ROLE app_user WITH LOGIN PASSWORD 'initial_password'; GRANT SELECT ON ALL TABLES IN SCHEMA public TO app_user;"
# 2. Configure static role
vault write database/static-roles/app-user \
db_name="pg-test" \
username="app_user" \
rotation_period="60" \
rotation_statements="ALTER ROLE \"\" WITH PASSWORD '';"
# 3. Read current credentials
vault read database/static-creds/app-user
# Note the password
# 4. Wait for rotation
sleep 65
# 5. Read again
vault read database/static-creds/app-user
# Password has changed!
# The old password no longer works
What’s Next?
You’ve mastered dynamic secrets. Next, we’ll explore encryption as a service with the Transit secrets engine.
Next Chapter: Transit Secrets Engine