Policies are how Vault grants and denies operations. They are written in HCL, attached to tokens, and evaluated on every request. Mastery of policies is the difference between a Vault deployment that's actually secure and one that's just on.
Policy Structure
# Allow reading the database static credentials
path "secret/data/database/*" {
capabilities = ["read"]
}
# Allow generating dynamic credentials
path "database/creds/web-api" {
capabilities = ["read"]
}
# Allow self-renew of the token
path "auth/token/renew-self" {
capabilities = ["update"]
}
Capabilities
| Capability | What it allows |
|---|---|
create | POST without existing data |
read | GET |
update | POST / PUT modifying existing data |
delete | DELETE |
list | LIST operations |
sudo | Required to call certain root-protected paths |
deny | Explicit deny — overrides all allows |
You almost always want the minimum set. The most common mistake: granting create, read, update, delete, list when only read is needed.
Path Globbing
Two wildcard styles:
*at the end of a path — match anything after that point at the same level+— match exactly one path segment
path "secret/data/team-a/*" { capabilities = ["read"] } # everything under team-a/
path "secret/data/+/shared" { capabilities = ["read"] } # any team's shared/ subdir
Effective Permissions
A token can carry multiple policies. The effective permission for a (path, capability) pair is:
- If any attached policy denies, the request is denied.
- Otherwise, if any attached policy allows, the request is allowed.
- Otherwise, denied (Vault is default-deny).
Parameter Constraints
Beyond capabilities, you can restrict which parameter values a request may include:
path "auth/token/create" {
capabilities = ["update"]
allowed_parameters = {
"policies" = ["web-api", "web-api-readonly"]
"ttl" = [] # any value allowed
}
denied_parameters = {
"no_default_policy" = []
}
}
Useful when you give a service the right to create tokens but only with a specific policy attached.
Policy Templating
You can reference identity attributes in path expressions:
path "secret/data/users/{{identity.entity.name}}/*" {
capabilities = ["create", "read", "update", "delete", "list"]
}
This single policy gives every user access only to their own subtree. Perfect for self-service personal secrets.
Common Policy Idioms
Read-only across one engine
path "secret/data/*" {
capabilities = ["read", "list"]
}
path "secret/metadata/*" {
capabilities = ["read", "list"]
}
Allow token self-management
path "auth/token/lookup-self" { capabilities = ["read"] }
path "auth/token/renew-self" { capabilities = ["update"] }
path "auth/token/revoke-self" { capabilities = ["update"] }
Almost every policy needs these — apps need to look up their own token to know when to renew.
Operations team
path "sys/policies/acl" { capabilities = ["list"] }
path "sys/policies/acl/*" { capabilities = ["read", "create", "update", "delete"] }
path "sys/audit" { capabilities = ["read", "sudo"] }
path "sys/audit/*" { capabilities = ["create", "update", "delete", "sudo"] }
path "sys/auth" { capabilities = ["read", "list"] }
path "sys/mounts" { capabilities = ["read", "list"] }
Default and Root Policies
- default: Auto-attached to every token; allows token lookup/renew and a few other harmless paths. Don't disable it; refine it instead.
- root: Implicit unlimited access. Only the initial root token and the regenerated root tokens have it. Never attach it to anything else.
Attaching Policies
Policies are attached when the token is created:
# Via an auth role
vault write auth/approle/role/web-api token_policies="web-api,default"
# Via an explicit token creation
vault token create -policy=web-api -ttl=1h
# Via identity group (Enterprise)
vault write identity/group name=engineering policies="developer"
Testing Policies
vault token capabilities <token> secret/data/database/master
# returns: read
vault policy fmt my-policy.hcl
vault policy write my-policy my-policy.hcl
Test policies in dev before deploying to prod. A broken policy on production Vault locks out legitimate apps.
Sentinel and EGPs (Enterprise)
Vault Enterprise adds Sentinel — a policy-as-code language for cross-cutting policies that simple ACLs can't express:
- Role-Governing Policies (RGPs): Constraints on what a specific identity can do
- Endpoint-Governing Policies (EGPs): Constraints on a specific path regardless of identity
Example EGP: "No write to kv/data/critical/* outside business hours." This is impossible with HCL ACLs alone.
Least-Privilege Workflow
- Start with a policy that denies everything (no paths)
- Add paths one at a time as the app fails with 403s
- Review the final list — every line should map to a concrete need
- Tag policies with the application name in the path:
app-policy/web-api - Audit the policy in PR review like any other code
Policies are the contract between an application and its secrets. Treat them with the rigor of any other security control.