# Least privilege

> Give each person, service and job only the access it needs, for only as long as it needs it.

Concept · Application & cloud security · https://levelcode.org/concepts/least-privilege

The **principle of least privilege** says every identity (a person, a service, a CI job, a container, an AI agent) should have only the permissions its task requires, and only for as long as it needs them. You can't prevent every compromise. What you *can* control is the **blast radius**: what an attacker can do with the identity they took over.

## In practice

| Where | Too broad | Least privilege |
|---|---|---|
| Cloud IAM | `AdministratorAccess` on the app's role | `s3:GetObject` on `arn:aws:s3:::invoices-prod/*` only |
| Database | App connects as the owner/superuser | App user can `SELECT/INSERT/UPDATE` its tables; migrations use a separate role |
| Kubernetes | Pod uses a ServiceAccount bound to `cluster-admin` | Namespaced `Role` with the few verbs it needs; `automountServiceAccountToken: false` when unused |
| CI | Workflow token with write access to everything | `permissions: contents: read` by default, elevated per job |
| Containers | Runs as root with all capabilities | Non-root user, `drop: [ALL]`, read-only root filesystem ([namespaces and cgroups](https://levelcode.org/concepts/container-isolation)) |
| AI agents | A tool that runs arbitrary SQL or shell | Narrow, read-only tools scoped to the current user ([the tool-use loop](https://levelcode.org/concepts/tool-use-loop)) |

## The time dimension

Privilege that never expires gets stolen eventually. Prefer:

- **Short-lived credentials** issued on demand over long-lived keys. See [short-lived credentials with OIDC](https://levelcode.org/concepts/workload-identity).
- **Just-in-time elevation** for humans: request production access for an hour, with a reason and an audit trail, instead of having it permanently.
- **Regular cleanup:** remove unused roles, keys and permissions. Cloud providers can report which granted permissions were never used, so you can trim from real usage instead of guessing.

## Getting there without breaking things

1. **Start from nothing and add what's needed,** rather than starting from `*` and planning to tighten later. Later rarely comes.
2. **Separate identities per workload.** If five services share one role, each one has the union of all five's permissions.
3. **Separate read from write, and production from everything else.**
4. **Use policy as code and review it** like any other change. Lint for wildcards like `Action: "*"` and `Resource: "*"`.
5. **Log every use of high privilege** and alert on the unusual ones.

Least privilege is the standard answer to the "E" in [STRIDE](https://levelcode.org/concepts/stride), and it's what turns a stolen token from an incident into an inconvenience.
