Home/Blog/HashiCorp Vault Policies: Complete ACL and Authorization Guide
Secrets Management

HashiCorp Vault Policies: Complete ACL and Authorization Guide

Master Vault policies and ACLs with HCL syntax, capabilities, path patterns, wildcards, and policy examples. Complete guide to Vault authorization and access control.

By InventiveHQ Team
HashiCorp Vault Policies: Complete ACL and Authorization Guide

Vault policies are the foundation of access control in HashiCorp Vault. They determine what paths each token can access and what operations it can perform. This guide covers everything you need to write effective policies, from basic syntax to advanced patterns.

Understanding Vault Policies

Vault follows a deny-by-default model: if a policy doesn't explicitly grant access to a path, access is denied. This secure-by-default approach ensures that permissions must be consciously granted.

Key Concepts

  • Path: A location in Vault's API (e.g., secret/data/myapp)
  • Capabilities: Operations allowed on a path (read, write, delete, etc.)
  • Policy: A document defining path-capability mappings
  • Token: Has one or more policies attached, determining its permissions
┌─────────────────┐
│     Token       │
│  ┌───────────┐  │     ┌─────────────────┐     ┌─────────────────┐
│  │ Policies  │──│────▶│  Policy Rules   │────▶│  Path Access    │
│  │ [a, b, c] │  │     │  (Capabilities) │     │  (Allow/Deny)   │
│  └───────────┘  │     └─────────────────┘     └─────────────────┘
└─────────────────┘

Policy Syntax

Policies are written in HCL (HashiCorp Configuration Language) or JSON.

Basic Structure

# Policy granting read access to a specific path
path "secret/data/myapp" {
  capabilities = ["read"]
}

# Policy granting full access to a path prefix
path "secret/data/team/*" {
  capabilities = ["create", "read", "update", "delete", "list"]
}

Capabilities Explained

CapabilityHTTP VerbDescription
createPOSTCreate new data (path doesn't exist)
readGETRead existing data
updatePOST/PUTModify existing data
deleteDELETERemove data
listLISTList keys at a path
sudo-Access root-protected endpoints
deny-Explicitly deny access (overrides allows)

Note: For KV v2 secrets engine, create and update are both needed for writing secrets, as the API uses POST for both operations.

Common Capability Combinations

# Read-only access
path "secret/data/readonly/*" {
  capabilities = ["read", "list"]
}

# Full CRUD access
path "secret/data/full/*" {
  capabilities = ["create", "read", "update", "delete", "list"]
}

# Write-only (no read back)
path "secret/data/writeonly/*" {
  capabilities = ["create", "update"]
}

# Admin access (includes sudo)
path "sys/*" {
  capabilities = ["create", "read", "update", "delete", "list", "sudo"]
}

Path Patterns and Wildcards

Glob Patterns (*)

The * wildcard matches any characters within a single path segment:

# Matches: secret/data/app1, secret/data/app2
# Does NOT match: secret/data/app1/config
path "secret/data/*" {
  capabilities = ["read"]
}

Segment Wildcards (+)

The + wildcard matches exactly one path segment:

# Matches: secret/data/app1/config, secret/data/app2/config
# Does NOT match: secret/data/config
path "secret/data/+/config" {
  capabilities = ["read"]
}

Prefix Matching

Paths without wildcards match exactly that path:

# Only matches exactly: secret/data/myapp
path "secret/data/myapp" {
  capabilities = ["read"]
}

Path Examples

# All secrets under a team namespace
path "secret/data/team-alpha/*" {
  capabilities = ["create", "read", "update", "delete", "list"]
}

# All metadata paths (for KV v2)
path "secret/metadata/*" {
  capabilities = ["list", "read"]
}

# Specific application config
path "secret/data/+/database" {
  capabilities = ["read"]
}

# All auth method configurations
path "auth/+/config" {
  capabilities = ["read"]
}

Built-in Policies

Vault has two built-in policies that cannot be deleted:

Default Policy

Attached to all tokens automatically (unless -no-default-policy used):

# Allow tokens to look up their own properties
path "auth/token/lookup-self" {
  capabilities = ["read"]
}

# Allow tokens to renew themselves
path "auth/token/renew-self" {
  capabilities = ["update"]
}

# Allow tokens to revoke themselves
path "auth/token/revoke-self" {
  capabilities = ["update"]
}

# Allow looking up own capabilities
path "sys/capabilities-self" {
  capabilities = ["update"]
}

Root Policy

Grants unlimited access to everything. Only attached to root tokens:

# Implicit: allows everything
path "*" {
  capabilities = ["create", "read", "update", "delete", "list", "sudo"]
}

Creating and Managing Policies

Write a Policy from File

# Create policy file
cat > readonly.hcl << 'EOF'
path "secret/data/*" {
  capabilities = ["read", "list"]
}
EOF

# Write to Vault
vault policy write readonly readonly.hcl

Write a Policy Inline

vault policy write developer - << 'EOF'
# Developer policy
path "secret/data/dev/*" {
  capabilities = ["create", "read", "update", "delete", "list"]
}

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

List Policies

# List all policy names
vault policy list

# Output:
# default
# developer
# readonly
# root

Read a Policy

vault policy read developer

Update a Policy

# Simply write again with same name
vault policy write developer updated-developer.hcl

Changes take effect immediately for all tokens using that policy.

Delete a Policy

vault policy delete developer

Warning: Existing tokens retain the policy name but lose all associated permissions.

Attaching Policies to Tokens

Direct Token Creation

# Create token with specific policies
vault token create -policy=developer -policy=readonly

# Create token with no default policy
vault token create -policy=minimal -no-default-policy

Auth Method Configuration

# AppRole
vault write auth/approle/role/cicd \
  token_policies="ci-policy,readonly"

# LDAP group mapping
vault write auth/ldap/groups/developers \
  policies="developer,shared-readonly"

# Userpass
vault write auth/userpass/users/alice \
  password="secret" \
  policies="developer"

Testing Policies

Check Token Capabilities

# Test current token's capabilities on a path
vault token capabilities secret/data/myapp

# Output: create, read, update, delete, list

# Test specific token's capabilities
vault token capabilities <token> secret/data/myapp

Capabilities API

# Check via API
curl -H "X-Vault-Token: $VAULT_TOKEN" \
  -X POST \
  -d '{"paths": ["secret/data/myapp", "secret/data/other"]}' \
  $VAULT_ADDR/v1/sys/capabilities-self

Policy Simulation

Test a policy before deploying:

# Create test token with the policy
vault token create -policy=test-policy -ttl=5m

# Try operations with the test token
VAULT_TOKEN=<test-token> vault kv get secret/myapp

Advanced Policy Features

Allowed/Denied Parameters

Control which parameters can be used:

path "secret/data/restricted" {
  capabilities = ["create", "update"]

  # Only allow specific keys
  allowed_parameters = {
    "data" = ["allowed_key1", "allowed_key2"]
  }
}

path "secret/data/safe" {
  capabilities = ["create", "update"]

  # Deny dangerous parameters
  denied_parameters = {
    "data" = ["admin_password", "root_key"]
  }
}

Required Parameters

Ensure certain parameters are always provided:

path "secret/data/audit-required" {
  capabilities = ["create", "update"]

  required_parameters = ["data"]
}

Min/Max Wrapping TTL

Control response wrapping:

path "secret/data/sensitive" {
  capabilities = ["read"]

  # Require wrapping between 1 minute and 1 hour
  min_wrapping_ttl = "1m"
  max_wrapping_ttl = "1h"
}

MFA Requirements (Enterprise)

path "secret/data/mfa-protected/*" {
  capabilities = ["read"]

  mfa_methods = ["totp"]
}

Policy Examples

Read-Only User

# read-only.hcl
# Can read secrets but not modify them

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

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

Application-Specific Policy

# app-myservice.hcl
# Full access to own namespace, read shared config

path "secret/data/apps/myservice/*" {
  capabilities = ["create", "read", "update", "delete", "list"]
}

path "secret/metadata/apps/myservice/*" {
  capabilities = ["read", "list", "delete"]
}

path "secret/data/shared/config" {
  capabilities = ["read"]
}

path "database/creds/myservice-db" {
  capabilities = ["read"]
}

CI/CD Pipeline Policy

# ci-policy.hcl
# Read deployment secrets, no write access

path "secret/data/deploy/*" {
  capabilities = ["read"]
}

path "secret/data/ci/+/config" {
  capabilities = ["read"]
}

# Read database credentials
path "database/creds/deploy-readonly" {
  capabilities = ["read"]
}

# No access to production secrets
path "secret/data/prod/*" {
  capabilities = ["deny"]
}

Team Admin Policy

# team-admin.hcl
# Manage team secrets and limited user management

# Full access to team namespace
path "secret/data/team-alpha/*" {
  capabilities = ["create", "read", "update", "delete", "list"]
}

path "secret/metadata/team-alpha/*" {
  capabilities = ["create", "read", "update", "delete", "list"]
}

# Create tokens for team members
path "auth/token/create" {
  capabilities = ["update"]

  allowed_parameters = {
    "policies" = ["team-alpha-member", "team-alpha-readonly"]
  }
}

# Look up team tokens
path "auth/token/lookup" {
  capabilities = ["update"]
}

Vault Administrator Policy

# vault-admin.hcl
# Full administrative access (use sparingly)

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

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

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

path "auth/*" {
  capabilities = ["create", "read", "update", "delete", "list"]
}

# System configuration
path "sys/config/*" {
  capabilities = ["create", "read", "update", "delete"]
}

# Audit logging
path "sys/audit/*" {
  capabilities = ["create", "read", "update", "delete", "list", "sudo"]
}

# Cannot manage other admins (separation of duties)
path "auth/userpass/users/admin-*" {
  capabilities = ["deny"]
}

Best Practices

1. Implement Least Privilege

# BAD: Too broad
path "secret/*" {
  capabilities = ["create", "read", "update", "delete", "list"]
}

# GOOD: Specific to application
path "secret/data/apps/myapp/*" {
  capabilities = ["read"]
}

2. Use Explicit Deny for Sensitive Paths

# Deny access to production even if other rules might allow
path "secret/data/prod/*" {
  capabilities = ["deny"]
}

# Then allow specific production access where needed
path "secret/data/prod/public-config" {
  capabilities = ["read"]
}

3. Separate Read and Write Policies

# team-reader.hcl
path "secret/data/team/*" {
  capabilities = ["read", "list"]
}

# team-writer.hcl
path "secret/data/team/*" {
  capabilities = ["create", "update"]
}

# Assign both to users who need write access

4. Use Descriptive Policy Names

# Good naming convention
vault policy write app-payments-readonly payments-readonly.hcl
vault policy write team-platform-admin platform-admin.hcl
vault policy write ci-deploy-staging ci-deploy-staging.hcl

5. Version Control Your Policies

# Store policies in git
git add policies/
git commit -m "Add payment service policy"

# Apply via CI/CD
for policy in policies/*.hcl; do
  name=$(basename "$policy" .hcl)
  vault policy write "$name" "$policy"
done

6. Audit Policy Changes

# Enable audit logging
vault audit enable file file_path=/var/log/vault/audit.log

# Policy changes appear in audit log

Troubleshooting

Permission Denied Errors

# Check what policies your token has
vault token lookup

# Check capabilities on specific path
vault token capabilities secret/data/myapp

# Read the policy to see what's allowed
vault policy read <policy-name>

Policy Not Taking Effect

  1. Check token policies: vault token lookup
  2. Verify policy content: vault policy read <name>
  3. Check path format: KV v2 uses secret/data/ prefix
  4. Check for deny rules: Deny overrides allow

Debugging Path Issues

# For KV v2, remember the path structure:
# - secret/data/* for reading/writing secrets
# - secret/metadata/* for metadata operations
# - secret/delete/* for soft delete
# - secret/undelete/* for recovery
# - secret/destroy/* for permanent deletion

Command Reference

CommandDescription
vault policy listList all policies
vault policy read <name>Show policy content
vault policy write <name> <file>Create/update policy
vault policy delete <name>Delete policy
vault policy fmt <file>Format policy file
vault token capabilities <path>Check current token's access
vault token capabilities <token> <path>Check specific token's access

Next Steps

For more Vault security guides, explore our complete HashiCorp Vault series.

Need Expert IT & Security Guidance?

Our team is ready to help protect and optimize your business technology infrastructure.