HashiCorp Vault — Capabilities Deep Dive

2 minute read

Each capability maps to an HTTP operation. Understanding them precisely is critical for writing least-privilege policies.


Capability to HTTP Mapping

Capability HTTP Method Vault CLI Equivalent
create POST (new resource) vault write, vault kv put (new)
read GET vault read, vault kv get
update POST/PUT (existing) vault write, vault kv put (update)
delete DELETE vault delete, vault kv delete
list LIST (GET with ?list=true) vault list, vault kv list
sudo — Access to sudo-protected endpoints
deny — Explicit deny (highest priority)

create vs update

For most Vault endpoints, create and update both map to the POST method:

# Allow creating new secrets but NOT updating existing ones
# (Not enforceable for most endpoints — both use POST)
# In practice, grant both together:
path "secret/data/myapp/*" {
  capabilities = ["create", "update"]
}

For KV v2 with Check-and-Set (CAS), you can enforce create-only:

# Create new secrets only (CAS = 0 means "create only if not exists")
path "secret/data/myapp/*" {
  capabilities = ["create"]
  required_parameters = ["options"]
}

The sudo Capability

Certain Vault paths are protected and require sudo:

# System paths that require sudo
path "sys/auth/*" {
  capabilities = ["create", "read", "update", "delete", "list", "sudo"]
}

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

# Root token generation
path "sys/generate-root/*" {
  capabilities = ["create", "read", "update", "delete", "list", "sudo"]
}

Common sudo-protected paths:

Path Operation
sys/auth/* Enable/disable auth methods
sys/policies/acl/* Manage policies
sys/mounts/* Enable/disable secrets engines
sys/seal Seal Vault
sys/step-down Force leader step-down
auth/token/create-orphan Create orphan tokens

The deny Capability

deny is the highest priority — it overrides any other policy:

# Allow reading all secrets
path "secret/data/*" {
  capabilities = ["read", "list"]
}

# But explicitly deny the admin path
path "secret/data/admin/*" {
  capabilities = ["deny"]
}

Use cases for deny:

  • Block access to sensitive paths even if a broad policy grants it
  • “All except” patterns
  • Emergency lockout policies

The list Capability

list must be granted on separate paths for KV v2:

# List secret keys — requires metadata path
path "secret/metadata/*" {
  capabilities = ["list"]
}

# Read secret values — requires data path
path "secret/data/*" {
  capabilities = ["read"]
}

Without the list capability on metadata, users can read secrets they know the name of, but cannot browse or discover secret names.


Checking Capabilities

# Check what capabilities your token has on a path
vault token capabilities secret/data/myapp/config
# create, read, update

# Check another token's capabilities
vault token capabilities -accessor abc123 secret/data/myapp/config

# API equivalent
curl -s --header "X-Vault-Token: $VAULT_TOKEN" \
     --request POST \
     --data '{"path": "secret/data/myapp/config"}' \
     "$VAULT_ADDR/v1/sys/capabilities-self" | jq

Fine-Grained Controls

Required Parameters

path "secret/data/myapp/*" {
  capabilities = ["create", "update"]
  required_parameters = ["data"]
}

Allowed Parameters

# Only allow setting specific keys in a secret
path "secret/data/myapp/config" {
  capabilities = ["create", "update"]
  allowed_parameters = {
    "data" = {
      "db_host"     = []
      "db_port"     = []
      "db_name"     = []
    }
  }
}

Denied Parameters

# Allow all parameters except "password"
path "secret/data/myapp/config" {
  capabilities = ["create", "read", "update"]
  denied_parameters = {
    "data" = {
      "password" = []
    }
  }
}

Min/Max Wrapping TTL

# Force response wrapping for sensitive paths
path "auth/approle/role/webapp/secret-id" {
  capabilities = ["create", "update"]
  min_wrapping_ttl = "60s"
  max_wrapping_ttl = "300s"
}

Quick Reference

Capability Use When
create Client needs to create new resources
read Client needs to read data
update Client needs to modify existing data
delete Client needs to remove data
list Client needs to browse/discover keys
sudo Accessing protected system paths
deny Explicitly blocking access

Next: Policy Examples