Least Privilege

Grant each identity the minimum permissions it needs and no more. The foundational identity-and-access pattern under IAM, RBAC, service accounts, and capability scoping.

Identity X needs to do Y. Grant only Y. When (not if) X is compromised, the attacker can only do Y. Status: STUB — promoted to OUTLINE in Y2 Phase 12.

What this pattern is

Least privilege is the foundational identity-and-access principle: each identity (user, service account, IAM role, API key) is granted only the permissions it actually needs for its function, and nothing more. The pattern operationalizes to scoped service accounts per workload (not one shared admin account), IAM policies that specify resources plus actions plus conditions explicitly (not wildcards), short-lived credentials (rotated automatically, not stored indefinitely), per-tenant plus per-namespace plus per-service authorization boundaries, and audit logs that record every privilege use.

The pattern sits alongside privilege-separation but plays a different role. Privilege-separation is the architectural shape (components have narrow capability boundaries). Least-privilege is the policy that enforces the shape (each component gets scoped credentials for exactly what it does). One is design; the other is operation. Both are needed.

The discipline is hard not because the concept is hard, but because every “make it work fast” shortcut tempts a broader grant. A service isn’t working — grant admin, see if it fixes it, forget to scope down later. An engineer is on-call — give them the god role, forget to remove it after the incident. A new integration needs some permissions — grant *:* to see what it actually calls, never revisit. Each shortcut individually is trivial; accumulated over five years, they compound into “we have a hundred wildcards in IAM policies and no one knows which are still needed.” The senior-IC habit is to grant minimum, observe what’s actually used, and tighten as the workload’s true needs become clear.

Concrete instances in the wild

  • Kubernetes RBAC. Each ServiceAccount gets a Role or ClusterRole that lists specific verbs on specific resources. Not verbs: ['*']. Not resources: ['*']. Named verbs on named resources.
  • AWS IAM roles per service. Each ECS task, Lambda function, or EC2 instance profile is a distinct role with a distinct policy. Not one “app-role” shared across everything.
  • Cloud IAM with conditions. aws:SourceIP, aws:RequestedRegion, aws:SecureTransport — conditions that tighten permissions to specific contexts. A backup role can only run from the backup account; a cross-region replicator only from specific regions.
  • Vault dynamic credentials. Vault generates database credentials on demand with short TTLs. Each caller gets its own credential that expires; long-lived shared credentials disappear.
  • Google IAP (Identity-Aware Proxy). Application access scoped to specific users plus groups. Each app’s IAP policy lists exactly which identities can reach it.
  • GitHub fine-grained personal access tokens. Tokens scoped to specific repositories and specific verbs (read, write, no admin). The 2023-era replacement for classic tokens with broad repo scope.
  • Linux capabilities. CAP_NET_BIND_SERVICE lets a process bind low ports without being root. CAP_SYS_ADMIN is a mistake most containers accidentally grant. Scoping to specific caps enforces least-privilege at the kernel layer.
  • OIDC scopes. OAuth 2.0 scope claims narrow tokens to specific operations (read:user, write:orders). Applications request only the scopes they need; users see what they’re granting.
  • Service mesh identity. Cilium mesh gives each pod a cryptographic identity; policies specify exactly which identities can talk to which. Not “any pod can reach any pod.”

Why this pattern matters

The 2019 Capital One breach: an SSRF vulnerability in a web app compromised an IAM role that had s3:* permissions on every bucket in the account. The compromise happened because of a code vulnerability. The catastrophic data loss happened because the compromised role had far more permission than the app needed. If the role had been scoped to only the specific buckets and read operations the app actually used, the compromise would have leaked one bucket’s contents instead of one company’s data.

Every real security incident since has some version of the same story. The compromise is inevitable; the blast radius is a design choice. Least-privilege turns “a bad thing will happen” from a corporate crisis into a bounded incident. The compromised service leaks the specific data it had access to; the compromised token can call the specific APIs it was scoped to; the compromised identity can do the specific actions the role permitted. Everything else is still protected.

The pattern’s operational cost is real. Fine-grained IAM policies are hard to author. Debugging “permission denied” errors is common. Adding a new capability requires updating a policy. Each service needs its own scoped role, not a shared one. Teams that try to skip these costs discover the reason at the first serious incident. Teams that pay them upfront discover that modern tooling (Terraform for IAM, Kubernetes RBAC, OPA policies) makes the cost bounded.

Modern platforms make least-privilege cheaper than the earlier era’s “one shared admin credential for the team.” Kubernetes service accounts, cloud IAM roles, service mesh identity, and Vault-managed secrets all lower the operational cost of scoping every credential narrowly. What was heroic engineering in 2010 is a checkbox in 2026. Skipping it is a choice, not a constraint.

Depth progression

STUB     ← you are here.
OUTLINE  Promoted when Y2 Phase 12 (auth) introduces RBAC + scoping; Y3 Phase 23
         (AWS IAM) deepens with cloud IAM.
DEEP     Promoted after Y3 end — every basecamp service running with scoped
         RBAC, no admin-tier service accounts, every IAM role explicitly bounded.

Preview: what OUTLINE will answer

When Y2 Phase 12 promotes this entry to OUTLINE, it will name:

  • PROBLEM. How do you bound the damage from any single credential compromise?
  • PRINCIPLES. Minimum required capability per identity. Named resources plus named verbs, no wildcards. Short-lived credentials with automatic rotation. Distinct identities per workload. Audit trail for every privilege use.
  • TRADE-OFFS. Static scoped policies (harder to author, safer) vs generic policies with dynamic conditions (easier to author, harder to reason about). Fine-grained (many roles, more configuration) vs coarse-grained (fewer roles, larger blast radius). Automated policy generation (from observed traffic) vs hand-authored (deliberate but slower).
  • TOOLS (time-stamped as of 2026-06): Kubernetes RBAC, AWS IAM, GCP IAM, Azure RBAC, Vault (dynamic secrets), service mesh identity (Cilium, Istio), Linux capabilities, OIDC scopes, GitHub fine-grained tokens.

The DEEP promotion, after Y3 with every basecamp service scoped properly, will add MASTERY (operating scoped credentials across the mesh, IAM, and Kubernetes for months), COMPARE (Kubernetes RBAC vs cloud IAM vs mesh identity as three ways to express the same boundary), OPERATE (incidents involving over-broad credentials or discovered wildcards), and CONTRIBUTE (a policy library contribution or a docs improvement to Vault or K8s RBAC).

Canonical references

  • Saltzer & Schroeder, The Protection of Information in Computer Systems (1975) — the original “principle of least privilege” paper. Free through ACM.
  • NIST SP 800-207, Zero Trust Architecture — the modern extension of least privilege to networks and identities.
  • AWS’s IAM documentation and IAM Analyzer — practical least-privilege at cloud scale.
  • Kubernetes RBAC documentation, particularly the “aggregated ClusterRoles” section for scoping strategies.
  • HashiCorp Vault documentation on dynamic secrets — the modern implementation of short-lived credentials.

Cross-references