Skip to main content

Least Privilege in Practice -- Not Aspirationally

What This Concept Is

Least privilege means each identity has exactly the permissions it actually uses -- and no more. Not "the role that works." Not "the role named app-role." Not "the managed policy closest to what it needs." The specific permissions the job performs, scoped to the specific resources it touches.

"In practice, not aspirationally" means measuring least privilege by experiment, not by intention. The test is mechanical: tighten the policy until something real breaks. Widen just enough to un-break it. Document the widening. The final policy is the smallest set of grants that still allows the service to do its job today.

Least privilege is also a blast-radius control. If an attacker gains your API's credentials, the damage is bounded by what that identity can do. A wide AmazonS3FullAccess policy means the blast radius is every bucket in the account. A narrow s3:PutObject on capstone-audit-logs/api/* means the blast radius is a single prefix in a single bucket. The ratio of those blast radii is the value of this practice.

In the cloud, identity is the new perimeter -- the NSA's Zero Trust Maturity Model and CISA's Zero Trust architecture both call this out explicitly. Network boundaries have eroded; the security boundary that still holds is who the caller is and what they are allowed to do. Least privilege is the enforcement of that boundary at the call site.

Why It Matters Here (In the Capstone)

Over-permissive roles are the highest-leverage foothold an attacker gets in the cloud. A leaked key for a role with s3:* on * is a breach; a leaked key for a role with s3:GetObject on one bucket prefix is an incident and a rotation. Same initial failure, different outcomes.

Capstones tend to drift wide because the fastest way to un-break a deploy is to add a permission. That drift is invisible until an audit, or until something goes wrong. Doing the tightening before the PRR turns the drift into a one-time event instead of a compounding liability. The PRR (concept 15) has a specific checklist row for this: "every runtime identity has a documented minimum-permissions policy."

Concrete Example -- from a real capstone

Webhook-handler capstone. Three identities we actually use:

  • api-runtime -- the role the API service assumes at runtime
  • worker-runtime -- the role the queue consumer assumes
  • ci-deployer -- the role CI uses to deploy new versions

Before (drifted, default)

api-runtime has the AWS managed policy AmazonS3FullAccess attached "because we needed to write audit logs to a bucket once."

Tightening experiment

Replace the managed policy with a narrow inline policy:

{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AuditLogsWriteOnly",
"Effect": "Allow",
"Action": ["s3:PutObject"],
"Resource": "arn:aws:s3:::capstone-audit-logs/api/*"
},
{
"Sid": "SecretsRead",
"Effect": "Allow",
"Action": ["secretsmanager:GetSecretValue"],
"Resource": "arn:aws:secretsmanager:us-east-1:*:secret:capstone/api/*"
}
]
}

Deploy to staging. Three minutes later an ingestion test fails with AccessDenied: the API reads a config blob from s3://capstone-config/* at startup.

Widen just enough

Add one statement, scoped to that exact prefix, read only:

{
"Sid": "ConfigReadOnly",
"Effect": "Allow",
"Action": ["s3:GetObject"],
"Resource": "arn:aws:s3:::capstone-config/api/*"
}

Redeploy. Tests pass. Commit. Write a one-line commit message: "api-runtime: scoped s3 access to audit write + config read per startup failure in 2026-04-22 staging run".

Document

In library/raw/iam.md:

RolePurposePermissions (summary)Why this scopeLast widened
api-runtimeAPI service runtimes3:PutObject to audit prefix; s3:GetObject to config prefix; secretsmanager:GetSecretValueWrites audit, reads startup config, reads signing secret2026-04-22
worker-runtimeQueue consumer runtimesqs:ReceiveMessage/DeleteMessage on one queue; rds-db:connect to one DB userConsumes queue, writes DB2026-04-20
ci-deployerCI deploy onlyecr:PushImage to one repo; lambda:UpdateFunctionCode on named functions; no read of runtime secretsBuild and ship2026-04-15

Three rows. Each defensible. Each was tightened until something broke.

Verification

Use aws iam simulate-principal-policy -- or the equivalent GCP gcloud iam policies analyze or Azure az role assignment list -- to answer "can this principal do X on resource Y?" in CI. One test case per row of the table turns your policy into a suite of assertions, and future policy drift fails CI the same way a type error would.

Common Confusion / Misconceptions

"We'll use managed policies; AWS knows best." AWS managed policies are designed for most customers, which means they are wider than you need. AmazonS3FullAccess is the canonical trap. Prefer narrow inline or customer-managed policies scoped to specific resources.

"The CI role needs to be wide so deploys don't break." The CI role needs to be wide for exactly the services CI deploys, not wide at all. A CI role with *:* can, on compromise, create new IAM roles for an attacker. Scope CI to the target resources. OIDC-federated short-lived tokens (see S9 M04 Cluster 5) reduce blast radius even further than long-lived keys do.

"Least privilege means read-only." No. It means exactly what is needed. Some roles legitimately need write. The point is the write is scoped to the resources they should be writing to -- not "the account."

"We'll get to it after launch." After launch, the role is in production and widening-by-accident has already happened. Do the tightening drill before PRR, on every runtime and CI identity, even if you only do it once.

"Wildcards are always wrong." Wildcards on resource names with a prefix (e.g., arn:aws:s3:::capstone-config/api/*) are fine. Wildcards on actions (s3:*) or on whole resources (Resource: "*") are the smell.

How To Use It (In Your Capstone)

  1. List every runtime identity in your capstone. Usually 3-6.
  2. For each, write down what it actually does in one sentence.
  3. Replace whatever policy it has with the minimum policy you think it needs.
  4. Deploy to staging. Watch it break.
  5. Widen one permission at a time, each scoped as narrowly as possible. Record each widening with a commit message that names the failing test.
  6. Once nothing breaks for a full test run, commit. Put the table in library/raw/iam.md and link it from the PRR.
  7. Add an aws iam simulate-principal-policy test to CI for each "MUST allow" and "MUST deny" assertion, so policy drift fails the build.

See also (integrative)

Check Yourself

  1. Why is Action: s3:* worse than Resource: "*" with a narrow action set? Construct a scenario where each fails differently.
  2. What is the argument for tightening CI / deploy roles more aggressively than runtime roles?
  3. Give one example of a legitimate wildcard in a least-privilege policy, and explain why it is acceptable.
  4. How does short-lived OIDC-federated credentials for CI reduce the impact of a leaked credential relative to static access keys?
  5. Why should the tightening of a role be the default operational move, and the widening the one that requires justification?
  6. What is the smallest evidence you would present at PRR to claim each runtime identity is least-privileged?

Mini Drill or Application (Capstone-scoped)

  1. Pick one runtime role in your capstone whose policy you did not personally write line-by-line. Diff its current effective permissions against what the service actually calls (CloudTrail, aws iam simulate-principal-policy, GCP Policy Troubleshooter, or equivalent).
  2. Replace the policy with a narrow inline version of the actually-used actions and resources.
  3. Deploy to staging, observe the breakage, widen narrowly.
  4. Add CI tests using simulate-principal-policy that assert the role can do its three main actions and cannot do three sensitive ones (e.g., iam:CreateUser, s3:DeleteBucket).
  5. Commit the before/after diff to the repo and log the exercise in library/raw/iam.md.
  6. Repeat for the CI deployer role. This is usually the wider one and the higher-impact win.

Source Backbone

Capstone operations applies security, reliability, and distributed-systems material. These books are the source backbone for readiness review.