Privileged Access Management (PAM) is the discipline of controlling access to sensitive systems, accounts, and secrets. Static credentials—passwords written in config files or shared among teams—are a leading cause of security breaches. HashiCorp Vault solves this by providing centralized secrets management, dynamic credential generation, and fine-grained access policies. In this lab, you'll deploy Vault, configure secrets engines, implement dynamic database credentials, and build a just-in-time (JIT) access workflow.
Privileged Access Management addresses the security risks associated with elevated credentials—database passwords, API keys, SSH keys, certificates, and administrative accounts. Traditional approaches of storing secrets in configuration files, environment variables, or spreadsheets create massive security risks: secrets sprawl, lack of audit trails, inability to rotate credentials, and no way to revoke access instantly. HashiCorp Vault provides a unified solution for managing secrets throughout their lifecycle, from secure storage to automatic rotation to detailed audit logging.
Deploy and initialize HashiCorp Vault in development and production modes
Configure secrets engines for different credential types
Implement dynamic credentials that eliminate static secrets
Design access policies using Vault's policy language
Build authentication workflows for humans and applications
Create just-in-time access patterns for privileged operations
🏢 Enterprise Scenario: Eliminating Secrets Sprawl
You're the Security Engineer at FinanceApp Inc., and a recent security audit revealed disturbing findings:
"We found database credentials in 47 different locations—Git repositories, Jenkins configs, developers' laptops, and even Slack messages. The production database password hasn't been changed in 3 years because 'too many things would break.' We have no idea who has access to what. This is a breach waiting to happen."
Centralize all secrets in a single, auditable system
Eliminate static credentials with dynamic, short-lived secrets
Enable automatic rotation without breaking applications
Create audit trails showing every secret access
Implement least privilege with fine-grained policies
🎯 Skills You Will Gain
Secrets Management
Centralized secret storage patterns applicable to all enterprise environments.
Dynamic Credentials
Generate on-demand secrets for databases, cloud providers, and services.
Policy Design
Write access control policies using HashiCorp Configuration Language.
PKI Management
Build certificate authorities and issue dynamic TLS certificates.
AppRole Authentication
Secure machine-to-machine authentication for CI/CD and applications.
Audit & Compliance
Track every secret access for security and regulatory requirements.
📚 Vault Architecture & Core Concepts
Before deploying Vault, you must understand its architecture and key concepts. Vault is designed around the principle that secrets should be difficult to access, easy to audit, and possible to revoke instantly.
📖 What is HashiCorp Vault?
Vault is a secrets management tool that provides:
Secure Secret Storage: Encrypted storage for sensitive data
Dynamic Secrets: Generate credentials on-demand with automatic expiration
Data Encryption: Encrypt data without storing it (Encryption as a Service)
Leasing & Renewal: All secrets have a lease; applications must renew or re-fetch
Revocation: Revoke single secrets, trees of secrets, or entire authentication methods
Vault Core Components
🔐 Secrets Engines
Plugins that store, generate, or encrypt data. Examples: KV (key-value), Database, PKI, AWS, SSH. Each engine is mounted at a path.
🔑 Auth Methods
Plugins that verify identity. Examples: Token, UserPass, LDAP, OIDC, AppRole, AWS IAM. Successful auth returns a Vault token.
📋 Policies
Rules defining what secrets a token can access. Written in HCL or JSON. Default deny—must explicitly grant access.
🎫 Tokens
Primary authentication mechanism. All requests require a token. Tokens have policies attached defining capabilities.
⏱️ Leases
Time-bound validity for secrets. When lease expires, secret is revoked. Applications must renew or re-authenticate.
📝 Audit Devices
Log all requests and responses. Every operation is recorded. Essential for compliance and incident response.
▼ VAULT ARCHITECTURE ▼
1
Client Authentication
Client authenticates using Auth Method (token, userpass, LDAP, OIDC, AppRole)
↓
2
Token Issuance
Vault issues token with attached policies defining access capabilities
↓
3
Secret Request
Client requests secret from Secrets Engine path using token
↓
4
Policy Check
Vault checks token's policies against requested path and operation
↓
5
Secret Delivery
If allowed, secret is returned with lease information. Audit log records the access.
Secrets Engine Types
Engine
Type
Use Case
KV (Key-Value)
Static
Store arbitrary secrets (API keys, configs)
Database
Dynamic
Generate time-limited database credentials
AWS
Dynamic
Generate IAM credentials or STS tokens
PKI
Dynamic
Issue X.509 certificates
SSH
Dynamic
Sign SSH keys or generate OTP
Transit
Encryption
Encrypt/decrypt data without storing it
📋 Prerequisites & Lab Environment
This lab requires Docker for running Vault and PostgreSQL. We'll deploy Vault in dev mode first, then progress to a more production-like configuration.
Device Badges Legend
Local MachineDocker host / terminal
Vault CLI/UIHashiCorp Vault
PostgreSQLDatabase server
Web BrowserVault UI
🔐 Module 1: Deploy HashiCorp Vault
Module 1: Vault Server Deployment
Deploy Vault in development mode and understand initialization concepts.
⏱️ 30-45 minutes🎯 5 steps📍 Local Machine
Vault has two operational modes: dev mode (for learning, automatically unsealed) and production mode (requires unsealing with master key shares). We'll start with dev mode to learn concepts, then understand production deployment patterns.
1
Create Lab Directory
Local Machine
mkdir -p ~/lab7-vault/{config,data,policies}
cd ~/lab7-vault
echo "Vault lab directory created"
cd ~/lab7-vault
docker compose up -d
echo "Waiting for services to start..."
sleep 10
docker compose ps
echo ""
echo "Vault UI available at: http://localhost:8200"
echo "Root Token: root-token"
4
Install Vault CLI
Install the Vault CLI for interacting with the server from your terminal.
Local Machine
# Option 1: Using Docker exec (no local install needed)
alias vault='docker exec -e VAULT_ADDR=http://127.0.0.1:8200 -e VAULT_TOKEN=root-token vault vault'
# Test the alias
vault status
# Option 2: Install locally (choose based on your OS)
# macOS: brew install hashicorp/tap/vault
# Ubuntu:
# wget -O- https://apt.releases.hashicorp.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/hashicorp-archive-keyring.gpg
# echo "deb [signed-by=/usr/share/keyrings/hashicorp-archive-keyring.gpg] https://apt.releases.hashicorp.com $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/hashicorp.list
# sudo apt update && sudo apt install vault
5
Configure Environment Variables
Local Machine
# Set environment variables for Vault CLI
export VAULT_ADDR='http://127.0.0.1:8200'
export VAULT_TOKEN='root-token'
# Verify Vault is running and unsealed
vault status
# Expected output shows:
# Sealed: false
# Cluster Name: vault-cluster-xxx
✅ Module 1 Complete!
Vault is running in dev mode. In Module 2, we'll store our first secrets.
🗄️ Module 2: Static Secrets with KV Engine
Module 2: Key-Value Secrets Engine
Store, retrieve, and version static secrets using the KV secrets engine.
⏱️ 30-45 minutes🎯 5 steps📍 Vault CLI
The KV (Key-Value) secrets engine stores arbitrary secrets. KV v2 (default in Vault) supports versioning, allowing you to retrieve previous versions of secrets and see when they were modified.
6
Explore Default KV Engine
Vault CLI
# In dev mode, a KV v2 engine is mounted at 'secret/'
# List all secrets engines
vault secrets list
# Expected output shows 'secret/' with type 'kv'
7
Store a Secret
Vault CLI
# Store a database credential
vault kv put secret/database/prod \
username="app_user" \
password="SuperSecretP@ssw0rd!" \
host="db.example.com" \
port="5432"
# Store an API key
vault kv put secret/api/stripe \
api_key="sk_live_xxx123456789" \
webhook_secret="whsec_xxx987654321"
echo "Secrets stored successfully"
8
Retrieve Secrets
Vault CLI
# Read the database secret
vault kv get secret/database/prod
# Get just the password field
vault kv get -field=password secret/database/prod
# Output in JSON format (useful for scripting)
vault kv get -format=json secret/database/prod
# List secrets at a path
vault kv list secret/
9
Update and Version Secrets
Vault CLI
# Update the password (creates version 2)
vault kv put secret/database/prod \
username="app_user" \
password="NewRotatedP@ssw0rd!" \
host="db.example.com" \
port="5432"
# Get current version
vault kv get secret/database/prod
# Get version 1 (original)
vault kv get -version=1 secret/database/prod
# View metadata (all versions)
vault kv metadata get secret/database/prod
10
Delete and Undelete Secrets
Vault CLI
# Soft delete (can be recovered)
vault kv delete secret/database/prod
# Try to read - returns no data
vault kv get secret/database/prod
# Undelete version 2
vault kv undelete -versions=2 secret/database/prod
# Read again - data is back
vault kv get secret/database/prod
# Permanently destroy a version
vault kv destroy -versions=1 secret/database/prod
# Destroy all versions and metadata
# vault kv metadata delete secret/database/prod
✅ Module 2 Complete!
You've mastered the KV secrets engine. In Module 3, we'll eliminate static secrets entirely with dynamic credentials.
⚡ Module 3: Dynamic Database Credentials
Module 3: On-Demand Database Credentials
Generate short-lived PostgreSQL credentials that automatically expire.
⏱️ 45-60 minutes🎯 6 steps📍 Vault + PostgreSQL
Dynamic secrets are Vault's killer feature. Instead of storing a static database password that everyone shares, Vault generates unique credentials for each request with automatic expiration. When an application needs database access, it requests credentials from Vault, uses them, and they automatically expire—eliminating credential sprawl.
📖 How Dynamic Database Credentials Work
Admin configures Vault with database connection and root credentials
Admin creates roles defining what SQL statements create users
Application requests credentials from Vault
Vault connects to database, creates temporary user with role's SQL
Vault returns username/password to application with TTL
When lease expires, Vault revokes the user from the database
11
Enable Database Secrets Engine
Vault CLI
# Enable the database secrets engine
vault secrets enable database
# Verify it's enabled
vault secrets list | grep database
12
Configure PostgreSQL Connection
Vault CLI
# Configure the database connection
vault write database/config/postgres \
plugin_name="postgresql-database-plugin" \
allowed_roles="readonly","readwrite" \
connection_url="postgresql://{{username}}:{{password}}@vault-postgres:5432/appdb?sslmode=disable" \
username="postgres" \
password="postgres123"
# Note: Vault will use these root credentials to create/revoke users
# The {{username}} and {{password}} are templates Vault fills in
13
Create Database Roles
Roles define what SQL statements Vault uses to create users. Different roles can have different permissions.
Vault CLI
# Create a read-only role
vault write database/roles/readonly \
db_name="postgres" \
creation_statements="CREATE ROLE \"{{name}}\" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}'; \
GRANT SELECT ON ALL TABLES IN SCHEMA public TO \"{{name}}\";" \
default_ttl="1h" \
max_ttl="24h"
# Create a read-write role
vault write database/roles/readwrite \
db_name="postgres" \
creation_statements="CREATE ROLE \"{{name}}\" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}'; \
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO \"{{name}}\";" \
default_ttl="1h" \
max_ttl="24h"
echo "Database roles created"
14
Generate Dynamic Credentials
Vault CLI
# Request read-only credentials
vault read database/creds/readonly
# Output shows:
# - lease_id: Used to renew or revoke
# - lease_duration: How long credentials are valid
# - username: Dynamically generated (e.g., v-root-readonly-xxx)
# - password: Random, unique password
# Request another set - different credentials each time!
vault read database/creds/readonly
# Request read-write credentials
vault read database/creds/readwrite
15
Test Database Connection
PostgreSQL
# Get credentials and save them
CREDS=$(vault read -format=json database/creds/readonly)
DB_USER=$(echo $CREDS | jq -r '.data.username')
DB_PASS=$(echo $CREDS | jq -r '.data.password')
echo "Username: $DB_USER"
echo "Password: $DB_PASS"
# Test connection with dynamic credentials
docker exec -it vault-postgres psql -U "$DB_USER" -d appdb -c "SELECT current_user, now();"
16
Manage Leases
Vault CLI
# Get credentials and capture lease ID
CREDS=$(vault read -format=json database/creds/readonly)
LEASE_ID=$(echo $CREDS | jq -r '.lease_id')
echo "Lease ID: $LEASE_ID"
# Renew the lease (extend TTL)
vault lease renew $LEASE_ID
# Revoke early (immediately invalidates credentials)
vault lease revoke $LEASE_ID
# Try connecting again - should fail!
# The user no longer exists in the database
✅ Module 3 Complete!
You've implemented dynamic database credentials! No more static passwords in config files. In Module 4, we'll configure authentication methods.
🔑 Module 4: Authentication Methods
Module 4: Configure Multiple Auth Methods
Set up authentication for humans (userpass) and machines (AppRole).
⏱️ 45-60 minutes🎯 6 steps📍 Vault CLI
Vault supports multiple authentication methods for different use cases. Human users might authenticate with username/password or LDAP, while applications use AppRole or cloud-native methods like AWS IAM. Each auth method returns a Vault token with attached policies.
17
Enable UserPass Auth Method
Vault CLI
# Enable userpass authentication
vault auth enable userpass
# List auth methods
vault auth list
18
Create Users
Vault CLI
# Create a developer user
vault write auth/userpass/users/alice \
password="alice123" \
policies="developer"
# Create an ops user
vault write auth/userpass/users/bob \
password="bob123" \
policies="ops"
# Create an admin user
vault write auth/userpass/users/admin \
password="admin123" \
policies="admin"
echo "Users created"
19
Test UserPass Authentication
Vault CLI
# Login as alice
vault login -method=userpass username=alice password=alice123
# Check current token info
vault token lookup
# Login back as root for remaining exercises
export VAULT_TOKEN='root-token'
20
Enable AppRole Auth Method
AppRole is designed for machine authentication—applications and CI/CD pipelines.
Vault CLI
# Enable AppRole
vault auth enable approle
# Create an AppRole for a web application
vault write auth/approle/role/webapp \
token_policies="webapp" \
token_ttl=1h \
token_max_ttl=4h \
secret_id_ttl=10m \
secret_id_num_uses=1
echo "AppRole created for webapp"
21
Authenticate with AppRole
Vault CLI
# Get the Role ID (like a username - can be shared)
ROLE_ID=$(vault read -field=role_id auth/approle/role/webapp/role-id)
echo "Role ID: $ROLE_ID"
# Get a Secret ID (like a password - single use)
SECRET_ID=$(vault write -f -field=secret_id auth/approle/role/webapp/secret-id)
echo "Secret ID: $SECRET_ID"
# Login with AppRole credentials
vault write auth/approle/login \
role_id="$ROLE_ID" \
secret_id="$SECRET_ID"
# Reset to root token
export VAULT_TOKEN='root-token'
22
Understanding AppRole Security
Vault CLI
AppRole Best Practices:
1. Role ID: Semi-public, can be baked into configs
- Identifies WHAT the application is
2. Secret ID: Private, delivered securely at runtime
- Proves the application is AUTHORIZED
- Should be single-use or time-limited
3. Delivery Methods:
- CI/CD: Inject Secret ID as build variable
- Kubernetes: Use Vault Agent sidecar
- Cloud: Use response wrapping
4. Response Wrapping: Wrap Secret ID so only intended
recipient can unwrap it (prevents man-in-middle)
✅ Module 4 Complete!
You've configured authentication for both humans and machines. In Module 5, we'll create policies to control access.
📋 Module 5: Policies & Access Control
Module 5: Fine-Grained Access Policies
Write policies that control exactly who can access which secrets.
⏱️ 45-60 minutes🎯 5 steps📍 Vault CLI
Vault policies use a default-deny model—users have no access unless explicitly granted. Policies are written in HCL (HashiCorp Configuration Language) and attached to tokens via auth methods. Understanding policy design is crucial for implementing least privilege.
23
Create Developer Policy
Local Machine
cat > ~/lab7-vault/policies/developer.hcl << 'EOF'
# Developer Policy
# Allows read access to development secrets and database credentials
# Read development secrets
path "secret/data/dev/*" {
capabilities = ["read", "list"]
}
# Generate database credentials (read-only role only)
path "database/creds/readonly" {
capabilities = ["read"]
}
# List available database roles
path "database/roles" {
capabilities = ["list"]
}
# Deny access to production secrets
path "secret/data/prod/*" {
capabilities = ["deny"]
}
EOF
# Write policy to Vault
vault policy write developer ~/lab7-vault/policies/developer.hcl
echo "Developer policy created"
cat > ~/lab7-vault/policies/webapp.hcl << 'EOF'
# Web Application Policy
# Minimal permissions for production app
# Read only specific secrets the app needs
path "secret/data/prod/webapp" {
capabilities = ["read"]
}
# Generate database credentials
path "database/creds/readwrite" {
capabilities = ["read"]
}
# Renew own token
path "auth/token/renew-self" {
capabilities = ["update"]
}
# Lookup own token
path "auth/token/lookup-self" {
capabilities = ["read"]
}
EOF
vault policy write webapp ~/lab7-vault/policies/webapp.hcl
echo "WebApp policy created"
26
Test Policy Enforcement
Vault CLI
# Create test secrets
vault kv put secret/dev/api key="dev-key-123"
vault kv put secret/prod/api key="prod-key-456"
vault kv put secret/prod/webapp db_url="postgres://prod:5432"
# Login as developer (alice)
vault login -method=userpass username=alice password=alice123
# Try to read dev secret - should work
vault kv get secret/dev/api
# Try to read prod secret - should be DENIED
vault kv get secret/prod/api
# Error: permission denied
# Try to get database credentials
vault read database/creds/readonly # Should work
vault read database/creds/readwrite # Should be DENIED
# Reset to root
export VAULT_TOKEN='root-token'
27
View All Policies
Vault CLI
# List all policies
vault policy list
# Read a specific policy
vault policy read developer
# Check capabilities for a path
vault token capabilities secret/data/prod/api
✅ Module 5 Complete!
You've implemented fine-grained access control with Vault policies.
📜 Module 6: PKI Secrets Engine (Certificates)
Module 6: Build a Certificate Authority
Issue dynamic TLS certificates for services and applications.
⏱️ 45-60 minutes🎯 5 steps📍 Vault CLI
The PKI secrets engine generates X.509 certificates dynamically. Instead of manually managing certificates with long validity periods, Vault can issue short-lived certificates on demand. This is essential for zero-trust architectures and service mesh deployments.
28
Enable PKI Engine
Vault CLI
# Enable PKI secrets engine
vault secrets enable pki
# Tune the engine for longer max lease (1 year for CA)
vault secrets tune -max-lease-ttl=8760h pki
29
Generate Root CA
Vault CLI
# Generate internal root CA
vault write -field=certificate pki/root/generate/internal \
common_name="TechStart Root CA" \
ttl=8760h > ~/lab7-vault/ca_cert.crt
echo "Root CA certificate generated"
cat ~/lab7-vault/ca_cert.crt
# Configure CA and CRL URLs
vault write pki/config/urls \
issuing_certificates="http://127.0.0.1:8200/v1/pki/ca" \
crl_distribution_points="http://127.0.0.1:8200/v1/pki/crl"
30
Create Certificate Role
Vault CLI
# Create a role for issuing server certificates
vault write pki/roles/server-cert \
allowed_domains="techstart.local,localhost" \
allow_subdomains=true \
allow_localhost=true \
max_ttl=720h \
key_type="rsa" \
key_bits=2048
echo "Certificate role created"
31
Issue a Certificate
Vault CLI
# Issue a certificate for api.techstart.local
vault write pki/issue/server-cert \
common_name="api.techstart.local" \
ttl=24h
# The output includes:
# - certificate: The issued certificate
# - private_key: The private key (only shown once!)
# - ca_chain: The CA certificate chain
# - serial_number: For revocation
# Save certificate to file
vault write -format=json pki/issue/server-cert \
common_name="web.techstart.local" \
ttl=24h > ~/lab7-vault/web_cert.json
echo "Certificate issued and saved"
32
Revoke a Certificate
Vault CLI
# Get the serial number from issued cert
SERIAL=$(cat ~/lab7-vault/web_cert.json | jq -r '.data.serial_number')
# Revoke the certificate
vault write pki/revoke serial_number="$SERIAL"
# View CRL (Certificate Revocation List)
curl -s http://127.0.0.1:8200/v1/pki/crl/pem
✅ Module 6 Complete!
You've built a Certificate Authority with Vault. Certificates are now dynamic secrets!
⏱️ Module 7: Just-in-Time Access Patterns
Module 7: Implement JIT Privileged Access
Create approval-based workflows for temporary elevated access.
⏱️ 30-45 minutes🎯 4 steps📍 Vault CLI
Just-in-Time (JIT) access is a PAM pattern where users don't have standing privileges. Instead, they request access when needed, access is granted for a limited time, and all requests are logged. This dramatically reduces the attack surface compared to always-on admin access.
📖 JIT Access Principles
No Standing Privileges: Users don't have admin access by default
Request-Based: Access must be explicitly requested
Time-Bounded: Access expires automatically after a short period
# Create a policy for emergency database access
cat > ~/lab7-vault/policies/emergency-db.hcl << 'EOF'
# Emergency Database Access
# Short-lived full database access for incident response
path "database/creds/readwrite" {
capabilities = ["read"]
}
path "secret/data/prod/*" {
capabilities = ["read"]
}
EOF
vault policy write emergency-db ~/lab7-vault/policies/emergency-db.hcl
# Create a token role that issues short-lived tokens
vault write auth/token/roles/emergency \
allowed_policies="emergency-db" \
orphan=true \
token_period=0 \
token_explicit_max_ttl=1h \
token_no_default_policy=true
34
Request Emergency Access
Vault CLI
# Simulate: On-call engineer requests emergency access
# In production, this might go through an approval system
# Generate a short-lived emergency token
EMERGENCY_TOKEN=$(vault token create \
-role=emergency \
-ttl=30m \
-display-name="incident-12345" \
-metadata="requestor=alice" \
-metadata="reason=database outage" \
-format=json | jq -r '.auth.client_token')
echo "Emergency Token: $EMERGENCY_TOKEN"
echo "Valid for: 30 minutes"
echo "Metadata includes requestor and reason for audit"
# Use the emergency token
VAULT_TOKEN=$EMERGENCY_TOKEN vault read database/creds/readwrite
# Reset to root
export VAULT_TOKEN='root-token'
35
Enable Audit Logging
Vault CLI
# Enable file audit device
vault audit enable file file_path=/vault/logs/audit.log
# All requests are now logged!
# View audit log
docker exec vault cat /vault/logs/audit.log | jq '.'
# Each log entry includes:
# - Time
# - Auth info (who)
# - Request (what they asked for)
# - Response (what they got)
# - Errors (if any)
36
Review Access Audit
Vault CLI
# View recent audit entries
docker exec vault tail -20 /vault/logs/audit.log | jq '.request.path, .auth.display_name'
# In production, audit logs would be:
# - Shipped to SIEM (Splunk, ELK, etc.)
# - Retained for compliance
# - Monitored for anomalies
# - Used for access reviews
✅ Module 7 Complete!
You've implemented Just-in-Time access patterns for privileged operations.
🛡️ PAM Vulnerabilities & Best Practices
PAM systems are high-value targets because they protect the keys to the kingdom. Understanding common vulnerabilities helps you design more secure implementations.
CRITICAL
Root Token Exposure
The root token has unlimited privileges. If exposed, attackers have complete control.
Policies granting broad access violate least privilege.
🛡️ Mitigation
Start with deny-all, add specific permissions
Use path templating for user-specific paths
Regular policy audits
HIGH
Long-Lived Tokens
Tokens with long TTLs or no expiration increase exposure window.
🛡️ Mitigation
Set appropriate token TTLs (hours, not days)
Implement token renewal for long-running apps
Use periodic tokens that must be renewed
✅ Vault Security Best Practices
✅ Enable TLS for all Vault communication
✅ Use auto-unseal with cloud KMS in production
✅ Enable audit logging to multiple destinations
✅ Implement least privilege policies
✅ Use dynamic secrets instead of static where possible
✅ Set appropriate TTLs for all tokens and leases
✅ Use AppRole or cloud auth for applications
✅ Regularly rotate root credentials for connected systems
✅ Monitor audit logs for anomalies
✅ Implement disaster recovery procedures
🎓 Key Takeaways
You've built a complete Privileged Access Management solution with HashiCorp Vault, eliminating static secrets and implementing enterprise-grade access controls.
Skills Mastered
Vault Deployment: Dev and production configuration patterns
Static Secrets: KV engine with versioning and access control
Dynamic Secrets: On-demand database credentials with automatic expiration
Authentication: UserPass for humans, AppRole for machines
Policy Design: Fine-grained access control using HCL