CoursesAdvanced secrets managementIdentities, entities & templated policies

Identities, entities & templated policies

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

Advanced35 min · lesson 7 of 15

Alice signs into Vault through OIDC (OpenID Connect, the single sign-on standard behind every “log in with Google” button) on Monday morning. After lunch she runs a script with a command-line token. Vault treats those two logins as two strangers. Nobody notices until an incident, when three near-identical policies all need the same edit. The thing that falls over first at scale is not storage and not encryption. It is policy sprawl: thousands of hand-written rules that differ by one word. The fix has two halves. First, an identity model that gives every human and workload one record no matter which door they came through. Second, policy templating, a handful of rules that fill in the path based on who is asking.

In plain terms
Without entities, Vault counts doors instead of people. OIDC Alice, Kubernetes Alice, and long-lived-token Alice look like three unrelated accounts until somebody links them. An entity is the name tag that says: this is Alice, whichever door she walked through.

Entities and aliases: one person, many front doors

Your bank keeps one customer record for you. The debit card, the mobile app, and the phone line are three ways to reach that record, not three customers. Vault is built the same way. The entity is the person or the workload; the aliases are the ways they prove who they are. A developer who signs in with OIDC at 9am and uses a command-line token at 4pm is one entity with two aliases. A service that authenticates through Kubernetes in one cluster and AWS IAM (Amazon's identity and access management service) in another is still one entity. Policy, metadata, and audit attribution all hang off the entity, so “what can Alice reach” and “everything Alice touched” have answers that cover every login method at once.

Disable the entity and every alias goes dark in the same instant. That is the revocation unit you want for someone who left on Friday, or for a service you decommissioned last sprint. When HR (human resources) closes out an employee, you flip one entity off instead of hunting tokens across six auth mounts and hoping you found them all.

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 both live at the entity. The alias is only a doorway into it.

Templated policies: write one rule, cover every tenant

A templated policy is a mail-merge letter. You write “Dear {{first_name}}” once and the printer stamps a different name onto every copy. Vault does that to paths. The policy holds a variable, and Vault substitutes the caller's own identity into it on every single request. So instead of one policy per team pointing at that team's folder, you write one policy whose path is stamped out fresh for whoever is asking. Cross-tenant access stops being something you have to remember to prevent, because the path can only ever resolve to the caller's own space.

That collapses thousands of near-identical files into one, and it deletes an entire category of copy-paste bug: the day somebody pasted the payments path into the search team's policy. The template engine fills in {{identity.entity.id}}, {{identity.entity.name}}, {{identity.groups.names}}, and metadata fields as the request arrives. Get a variable wrong and the path does not exist for that caller, so the request is denied rather than pointed somewhere it should never have gone.

policy.hcl — templated per-entity tree
# each identity can only ever reach its own tree
path "secret/data/users/{{identity.entity.id}}/*" {
capabilities = ["create", "read", "update", "delete", "list"]
}
path "secret/data/teams/{{identity.groups.names.0}}/*" {
capabilities = ["read", "list"]
}
# path resolves at request time; cross-tenant access is impossible.

Let your identity provider own the team list

The answer to “who is on the payments team” already lives in one system, and that system is not Vault. It is your IdP (identity provider, whatever handles company logins: Okta, Microsoft Entra, Google Workspace). External groups map a claim from the IdP, an Okta group or an OIDC groups claim, onto a Vault group that carries policy. Access then tracks joiners, movers, and leavers for free. Take someone out of the payments group in the IdP and they lose the payments policy at their next login, with nobody touching Vault at all.

That is how least privilege stays true a year later instead of silting up with grants nobody remembers issuing. Configure the group-claim mapping on the OIDC auth role and let Vault create external group aliases the first time each person logs in. Group membership maintained by hand inside Vault holds up to about a dozen humans, and then it quietly rots.

terminal
vault write auth/oidc/role/engineers \
bound_audiences=vault \
user_claim=sub \
groups_claim=groups \
policies=default oidc_discovery_url=https://idp.acme.internal
vault list identity/group/name
output
Success! Data written to: auth/oidc/role/engineers
Keys
----
payments-team
platform-team
engineering
terminal
vault read identity/entity/name/alice
vault write identity/entity/id/ENTITY_ID metadata=team=payments
output
aliases [oidc-abc123, token-def456]
policies [payments-read, default]
Success! Metadata updated
# audit now attributes alice across OIDC and token aliases

Where the template's data comes from decides whether it is safe

A template is only as honest as the field it reads. If a policy path is stamped from a team metadata value, the safety of every tenant rests on one question: who is allowed to write that value? Fill templating metadata from claims the auth method itself vouches for, or from an admin-only process. Never from a field the caller can edit. A tenant that can relabel itself payments has written itself a key into the payments tree, and your policy file will look perfectly correct the whole time it happens.

Audit logs name the entity rather than the token, as long as the aliases merged the way you expected. Spend the time on merge rules while you are setting up each auth method, because the alternative is an incident responder at 2am stitching three token accessors back into one person. During an investigation, vault list -format=json auth/token/accessors | jq is the first thing you reach for; jq is a small command-line tool for filtering JSON output.

What policy review looks like once templates do the work

Review changes shape entirely. The question stops being “did somebody paste the wrong path into policy number 847” and becomes “does this one template leak?” That is a question a human can actually answer in a meeting. Keep policies in Git, run vault policy fmt in the pipeline so formatting arguments never reach a reviewer, and raise an alarm whenever a new policy sidesteps the template for “only this one case.”

Book a quarterly audit and count three things: how many policies exist, how many entities exist, and how many policies hang directly off tokens instead of groups. Sprawl creeps back one sprint exception at a time. Treat those exceptions the way you treat a firewall hole: written down, owned by a named person, and carrying an expiry date.

terminal
vault policy read payments-read
vault token capabilities database/creds/payments-ro
output
path "database/creds/payments-ro" { capabilities = ["read"] }
["read"] # confirms effective capability for current token

Identity tokens, and when to demand a second factor

An identity token is minted against an entity directly, without walking back through an auth mount to log in again. It is a day pass issued off your existing employee record rather than a whole new badge application. It inherits the entity's policies and carries its metadata, so templated paths resolve exactly as they would after a normal login. Give it the same TTL (time to live, how long a token stays valid) discipline you give workload tokens: short, renewable, never committed to Git.

Turning on MFA (multi-factor authentication, the second proof on top of a password) does not break entity merging. The aliases still fold into one entity. What changes is that Sentinel guardrails, Vault Enterprise's policy-as-code layer, can demand that second factor on the paths that deserve it even when the ACL (access control list, the plain allow-or-deny rules) already says yes. That pairing is how humans get a fast morning login without a single-factor browser session being enough to read production secrets.

terminal
vault token create -identity-entity-id=$ENTITY_ID -ttl=30m -policy=payments-read
vault token lookup $IDENTITY_TOKEN | grep -E "display_name|ttl|policies"
output
token hvs.CAE...
display_name entity-abc123
ttl 30m
policies [payments-read]
# short-lived identity token — not a durable API key

Test your merge rules on purpose. Create two aliases for the same person through two different auth mounts, then read the audit log and confirm it shows one entity name. An incident search by entity ID that silently misses half a developer's activity, because OIDC and Kubernetes auth each minted their own entity, is worse than no search at all. It hands you a clean-looking answer that happens to be wrong.

Group changes from the IdP land at the next login for humans, not the moment HR clicks the button. Plan session length around that gap. Someone who walked out this morning can still be carrying a six-hour OIDC session with the old group claims baked into it. Pair IdP revocation webhooks (the automatic notification your identity provider fires the moment an account is disabled) with a token max_ttl that matches whatever your offboarding SLA (service level agreement, the response time you promised) actually says.

Somebody has to own the identity schema, and it should be the platform team: which metadata fields exist, which auth mounts are allowed to populate them, and which templates read them. Metadata keys invented ad hoc by individual services become cross-tenant footguns on the day a policy author templates on a field nobody ever declared trustworthy.

For the quarterly access review, export the entity list with its attached policies and lay it next to the HR headcount. Every orphaned entity still holding a production policy is a joiner-mover-leaver process failure, not a Vault bug, and it will keep reappearing until the process behind it is fixed.

Disable the Vault entity first, then the IdP account. Order matters here. Do it the other way round and a session that is still alive can keep refreshing tokens for an hour after the IdP is locked.

Export an entity report monthly and put it in front of leadership. Active Vault entities against headcount reads the same way a cloud spend anomaly does, and the gap gets attention from the people who can fix the process behind it.

terminal
vault list identity/entity/name | wc -l
vault list identity/entity-alias/id | wc -l
# aliases should not vastly exceed entities — duplicates hint at missing merge rules
output
248
312
# investigate entities with >2 aliases — may need merge or duplicate accounts

Those two counts are your health check. If aliases outnumber entities by a wide margin, you have duplicates: the same human or service landing in Vault twice because two auth mounts each minted their own entity. Two aliases per person is ordinary, a browser login and a command-line one. Five is a merge rule nobody ever wrote, and it means “who is this token really?” has no single answer.

Reach for identity groups when access is team-shaped, and for entity policies only when one person genuinely needs an exception. Send humans through OIDC so joiners and leavers ride in from the IdP on their own. Put a second factor on the operations that warrant it, decryption and root-like actions, and leave it off routine reads so nobody trains themselves to tap approve on every prompt without reading it.

Try this

Log in with OIDC or userpass (Vault's built-in username-and-password method), then look up your own entity and group memberships. You are checking one thing: that your access arrives through Identity, and not through some token handed to you once that nobody can account for.

terminal
vault token lookup -format=json | jq ".data.meta, .data.identity_policies, .data.policies"
vault read -format=json identity/entity/id/<entity_id> | jq ".data.name, .data.group_ids"
vault list identity/group/id
output
meta: {"role":"payments-dev"}
identity_policies: ["payments-read"]
policies: ["default"]
["8f2a...","91bc..."]
Keys
----
8f2a...
91bc...
# group policies attach here — revoke group membership to cut access

Takeaway

Entities turn a pile of login methods into durable objects you can group, govern, and switch off in one move. Humans belong on OIDC with a second factor guarding the sensitive paths; machines belong on platform auth with tight bindings. Templated paths keep tenants apart only while the metadata feeding them stays out of the caller's reach.

Next: find one leftover userpass account that duplicates somebody's SSO (single sign-on) login, delete it, and move its policies onto an IdP-backed identity group instead.

A template is a trust boundary, so check what feeds it
Every variable Vault substitutes into a policy path is a decision made by whoever controls that data. If the caller can write their own team label, they can aim the path wherever they like, and the policy file will still read as correct. Source templating metadata from auth-method claims or from an admin-only process. Treat any user-writable field as untrusted input, the same way you would treat a query parameter on a web request.
Quick check
01Alice logs in through OIDC on her laptop and with a command-line token from a build box. What do aliases give you?
Correct — The aliases are the doors. The entity holds the policy and the attribution.
Incorrect — Encryption is a storage concern. An alias is an identity abstraction.
Incorrect — A second factor is an auth-method setting, and it is unrelated to entities.
Incorrect — Replication is a cluster feature with nothing to do with identity.
02The path secret/data/teams/{{identity.groups.names.0}}/* cannot hand one team another team's secrets. Why not?
Incorrect — The isolation comes from how the path resolves, not from per-tenant keys.
Correct — A caller has no way to substitute another team's name into their own request.
Incorrect — Root is beside the point. Templating is what scopes ordinary identities.
Incorrect — External groups feed group names into the template. The ACL rules still run.
03Where should the membership list behind external groups come from?
Incorrect — Hand-kept lists drift within weeks. The IdP is the source of truth.
Incorrect — Self-set metadata destroys the trust a template depends on.
Correct — Joiners, movers, and leavers then flow in without anyone editing Vault.
Incorrect — Workable for workloads, but this lesson's pattern for humans is IdP-driven.

Related