Skip to content

Rotation & Least-Privilege Access

Storing a secret correctly and deploying it safely aren't the end of the story — a secret that's correct today and never touched again is a growing liability. This page covers the two practices that keep it safe over time: rotation (replacing a secret before it becomes a long-lived risk) and least-privilege access (making sure any one credential can only do the narrow thing it needs to).

TL;DR — in 30 seconds:

  • Rotation means replacing a secret's value on a schedule (or after a suspected exposure) — it caps how long a leaked-but-undetected credential stays useful to whoever has it.
  • Least privilege means a credential is scoped to exactly what it needs — read-only where write isn't needed, one resource instead of a wildcard, revocable independently of every other credential.
  • Short-lived, dynamically issued credentials (Vault's dynamic secrets, cloud-issued temporary credentials) push both ideas further: instead of rotating a long-lived secret periodically, there's nothing long-lived to rotate in the first place.

1. Why rotation matters even without a known leak

flowchart LR
    A["Secret issued"] --> B["Time passes<br/>more places have touched it"]
    B --> C["Risk grows:<br/>logs, backups, ex-employees, forgotten scripts"]
    C --> D["Rotation resets the clock<br/>old value revoked"]

A secret's risk isn't static — the longer it exists unchanged, the more places it's likely been logged, cached, backed up, or seen by someone who's since left the team or project. None of that requires a dramatic "leak" — it's the ordinary accumulation of exposure over time. Rotation doesn't undo any of that history, but it caps how long any single value stays valid, which limits how much all of that accumulated exposure is actually worth to someone who has it.

  • Scheduled rotation — replacing a secret on a regular cadence regardless of any known incident (what AWS Secrets Manager automates for supported services).
  • Incident-driven rotation — replacing a secret immediately after a suspected or confirmed exposure (an accidental commit, a compromised laptop, an employee offboarding).
  • Rotation only works if every consumer of the secret picks up the new value — a rotation that updates the store but leaves a pod running with the old value cached isn't finished until that pod also refreshes.

2. What least-privilege access actually looks like

Least privilege is the practice of granting a credential the narrowest set of permissions it needs to do its job — nothing broader "to be safe" or "in case it's needed later."

  • Scope to a specific resource, not a wildcard: a policy naming one secret's exact path/ARN instead of secrets/*.
  • Scope to the specific action, not full access: GetSecretValue (read) instead of also granting PutSecretValue (write) or DeleteSecret when the consumer only ever reads.
  • Scope to a specific identity, not a shared one: a dedicated IAM role or Kubernetes service account per workload, so revoking or auditing one workload's access doesn't touch any other workload's.

The payoff shows up when something goes wrong: a narrowly-scoped, single-purpose credential that leaks exposes exactly one thing; a broad, shared credential that leaks exposes everything it touches, and figuring out the actual blast radius takes real investigation.

3. Short-lived credentials: rotation you don't have to remember

The strongest version of both ideas combined is a credential that's short-lived by design: Vault's dynamic secrets (covered in Secret stores) and cloud-issued temporary credentials (e.g. an AWS IAM role assumed via STS, expiring automatically) don't need a human or a schedule to rotate them — they expire on their own, typically within minutes to hours. There's no long-lived value sitting in a store waiting to become stale or leak; whatever leaks is only valid for a short, bounded window.

Common gotchas

  • Rotating the secret in the store but not restarting/reloading consumers. Fix: rotation isn't done until every pod/service actually picks up the new value — either via a restart, a live-reload mechanism, or (for K8s) letting External Secrets sync trigger a rollout.
  • Granting a wildcard permission "temporarily" during debugging and forgetting to narrow it back. Fix: treat any broadened grant as needing an explicit, tracked expiration or follow-up — "temporarily broad" has a way of becoming permanently broad if nothing forces a revisit.
  • Sharing one credential across multiple services "to keep it simple." Fix: one credential per workload costs a bit more setup, but it's what makes revoking or auditing a single compromised service possible without touching everything else that shared the credential.
Check yourself — a credential leaks but was scoped to read exactly one secret, read-only. A second scenario: the same leak, but the credential had secrets/* with read and write. What's actually different about the impact?

In the first case, the damage is bounded to whatever that one secret protects, and nothing can be modified. In the second, the leaked credential can read (and write/overwrite) every secret the wildcard covers — the blast radius and the possible damage are both dramatically larger for the exact same "a credential leaked" starting point.

Check yourself — why does a short-lived, dynamically issued credential reduce risk even if it's never detected as leaked?

Because it expires on its own, typically within minutes to hours — even an undetected leak is only useful to whoever has it for that short window, unlike a long-lived static secret that stays valid (and thus valuable to an attacker) until someone notices and rotates it manually.

You can defend this when you can explain why rotation caps risk even without a known leak, and can name the specific narrower scope (resource, action, identity) that a least-privilege fix would apply to a given overly-broad credential.