CoursesAdvanced secrets managementIdentity, policy & multi-tenancy

Identities, entities & templated policies

The entity/alias model, groups, and ACL templating that scales.

Advanced35 min · lesson 7 of 15

At scale, the thing that breaks first is not storage or crypto — it is policy sprawl. If every team, app, and environment needs its own hand-written rules, you end up with thousands of near-identical policies that nobody can audit. The fix is a proper identity model plus policy templating: represent every human and workload as one identity regardless of how they logged in, then write a small number of policies that adapt to that identity at request time.

Entities and aliases: one identity, many logins

Vault’s identity system separates the person or workload (an entity) from the ways they authenticate (aliases). A developer who logs in via OIDC in the morning and via a CLI token later is one entity with two aliases; a service that authenticates via Kubernetes in one cluster and AWS in another is still one entity. This matters because policy, metadata, and audit attribution attach to the entity — so "what can Alice do" and "everything Alice touched" are answerable across every auth method, and disabling the entity cuts off all of them at once.

The identity model
entity (the who)
one per human/workload
stable identity + metadata
carries policies
and group memberships
aliases (the how)
OIDC login
maps to the entity
Kubernetes / AWS auth
same entity, diff mount
groups
internal groups
assign shared policy
external groups
mapped from IdP claims
Attribution and revocation live at the entity — the alias is just a doorway into it.

Templated policies: one rule, every tenant

Policy templating lets a path in a policy contain identity variables that are substituted per request. Instead of writing one policy per team that grants access to that team’s path, you write a single policy whose path is parameterized by the caller’s identity — and it is mathematically impossible for it to grant cross-tenant access, because the path always resolves to the caller’s own space. This collapses thousands of policies into one and removes the copy-paste mistakes that leak tenants into each other.

policy.hcl — one templated policy for all users
# each identity can only ever reach its own tree — no per-user policy needed
path "secret/data/users/{{identity.entity.id}}/*" {
capabilities = ["create", "read", "update", "delete", "list"]
}
# group-scoped shared space, keyed by an IdP-mapped group name:
path "secret/data/teams/{{identity.groups.names.0}}/*" {
capabilities = ["read", "list"]
}
# the path resolves at request time to the caller; cross-tenant access is impossible.

Groups from your IdP, not in Vault

The scalable source of truth for "who is on which team" is your identity provider, not Vault. External groups map an IdP claim (an Okta or Entra group, an OIDC groups claim) to a Vault group that carries policy, so access follows joiners, movers, and leavers automatically — someone removed from the "payments" group in the IdP loses the payments policy on their next login with no Vault change. This is how you keep least privilege true over time instead of accumulating stale grants nobody remembers to remove.

Metadata in templates is only as trustworthy as its source
Templated policies that key off identity metadata (a team name, a project) are only safe if that metadata cannot be self-set by the caller. Populate templating metadata from trusted auth-method claims or an admin process, never from user-writable fields, or a tenant can rewrite its own team label and walk into another tenant’s path. Treat the substituted variables as a trust boundary.