Identity, Secrets & Encryption

Human vs Workload Identity

Applications should not borrow human credentials. A developer's access key in an environment variable gives the application the developer's permissions, records the developer as the actor in every audit log, and takes production down the day that developer is offboarded.

The question this answers

Infrastructure question

Whose identity should an application present when it calls a cloud service?

Application requirement

The payments service must read a secret and write to a queue. It needs an identity of its own — one whose permissions match the service, whose actions are attributable to the service, and whose lifetime is the service's lifetime, not an employee's.

What it provides

A first-class identity per workload: platform-issued credentials with no stored secret, permissions scoped to what the workload does, and an audit trail that names the service rather than a person.

Application RequirementInfrastructure RequirementComputeNetworkStorageIdentityDeploymentScalingReliabilityObservabilitySecurityCostTrade-offs

Three failures from one shortcut

It starts benignly. A developer needs the service to reach object storage from their laptop, so they export their own access key into .env. It works. The file is copied into the staging configuration, then into production, and eighteen months later the payments service is authenticating as a person. Every consequence of that flows from a single fact: the application is now indistinguishable from the human.

The permissions are wrong, in the dangerous direction. A developer can list buckets, read logs, restart instances, describe every resource in the account. The payments service needs to read one secret and write to one queue. Whatever the developer can do, a compromise of that service can now do. The blast radius of the workload has silently become the blast radius of a human administrator.

The audit trail lies. Every call the service makes is recorded as that person. During an incident the log says the developer deleted the objects at 03:14 — from an address in a data centre, while they were asleep. You cannot separate what the human did from what the service did, so you cannot answer the only question that matters during an investigation, and you have created a genuine unfairness for the person named in the record.

Offboarding becomes an outage. The developer leaves. Their access is disabled correctly and promptly, exactly as the runbook says. Production stops. Nothing in the offboarding process could have known that a person's credential was load-bearing infrastructure, and the incident is discovered by customers.

The third one is what usually forces the fix, and it is the least defensible reason to have waited.

The application borrows a person
# deployment config
env:
  - name: CLOUD_ACCESS_KEY_ID
    value: "AKIA...EXAMPLE"        # minted from a developer account
  - name: CLOUD_SECRET_ACCESS_KEY
    valueFrom: { secretKeyRef: { name: cloud-creds, key: secret } }

# consequences:
#   permissions = everything that developer can do
#   audit log   = "developer@example.com deleted 40,000 objects"
#   offboarding = production outage
The application is its own identity
# no credentials in the config at all
serviceAccountName: payments-service
# the platform projects a short-lived token; the workload exchanges it
# for cloud role credentials that expire in minutes and auto-renew.

# role: payments-service
#   allow secrets:GetSecretValue on secret/payments/db
#   allow queue:SendMessage    on queue/payments-events
#
# audit log   = "payments-service sent 4,200 messages"
# offboarding = irrelevant; the identity belongs to the workload

The second configuration contains no secret to leak, grants only what the service does, and produces an audit trail that names the service. The identity now has the same lifetime as the workload rather than the same lifetime as an employment contract.

How a workload gets a credential without holding a secret

The objection to workload identity is usually "but the application still needs *some* credential". It does — and the point is that it never has to store one. The platform is already in a position to attest to what the workload is, and that attestation is exchanged for a short-lived credential at runtime.

A virtual machine asks a link-local metadata service, which returns credentials for the role attached to the instance. A pod presents a projected, audience-scoped service-account token, which a cloud identity provider validates and exchanges for role credentials. A CI pipeline presents a signed assertion from its provider, federated into a role restricted to one repository and one branch. In all three the credential is minted on demand, expires in minutes, is renewed automatically, and never appears in a file, a variable or a repository.

That removes the entire secret-handling problem for the most common case. There is nothing to rotate, nothing to leak in a log, nothing to accidentally commit. The remaining secrets — third-party API keys, database passwords where the provider has no identity integration — are a smaller, better-understood set, and they belong in a secret manager (Secrets in Infrastructure).

It also gives the metadata endpoint a security significance worth stating plainly: anything that can make the workload issue an HTTP request to that address can potentially read its credentials. That is why server-side request forgery is a credential-theft vector in cloud environments, and why metadata services now require a session token by default. The Security domain covers the attack under SSRF; the infrastructure consequence is that your workload role's scope *is* the impact of an SSRF bug in your application.

Two identities, two very different footprints.PROVIDER-NEUTRAL
Developer (human)public— interactive login, MFA, broad read access
Virtual network
Private subnetprivate
payments-serviceprivate— no stored credential
Platform identity endpointinternal— issues short-lived role credentials
Role: payments-serviceinternal— one secret, one queue
Secret: payments/dbprivate
Queue: payments-eventsprivate
Human role: developerinternal— list, describe, read logs, restart instances
⚠ If an application authenticates as this identity, the application inherits all of it — and the audit trail names the person.
Developer (human)Human role: developer· interactive session + MFA
payments-servicePlatform identity endpoint· attest (no secret)
Platform identity endpointRole: payments-service· mint short-lived credential
Role: payments-serviceSecret: payments/db· GetSecretValue (this secret only)
Role: payments-serviceQueue: payments-events· SendMessage (this queue only)

The two policies, side by side

The gap is easiest to see when the borrowed identity is written out as a policy panel. Nobody would approve the panel below if it were proposed as a workload policy in a review. It gets approved anyway, because it is never proposed — it arrives as an environment variable.

That is the practical lesson. The security review that would have caught this looks at policies; the mistake lives in a deployment configuration. So the control that actually works is not a review but a rule: no long-lived cloud credentials in application configuration, at all — enforced by a scanner in the pipeline, because a rule nobody checks is a preference.

What the payments service is actually running as, when it borrows a developer key
payments-service, authenticating as developer@example.comhumandangerous
on the entire cloud account
Allowed
  • storage:* on every bucket
  • compute:Describe* and compute:RebootInstances on every instance
  • logs:* on every log group
  • secrets:GetSecretValue on every secret
  • db:Connect to every database the developer can reach
Actually needed
  • secrets:GetSecretValue on secret/payments/db
  • queue:SendMessage on queue/payments-events

Blast radius: A compromise of one container yields a human engineer's permissions across the whole account: read every bucket, read every secret, reach every database, restart instances. The audit trail attributes all of it to a person who was not involved, and disabling that person's account to contain the incident also takes payments down.

Key points

  • Applications should not borrow human credentials — the permissions, the audit trail and the lifetime are all wrong.
  • A borrowed developer key gives the workload the developer's blast radius, which is far larger than any workload needs.
  • The audit log becomes unusable: it names a person for actions a service performed, which breaks investigations and is unfair to the person.
  • Offboarding a human then becomes a production outage, and nothing in the offboarding process can predict it.
  • Workload identity removes the secret entirely: the platform attests to what the workload is and mints a short-lived credential on demand.
  • The workload role's scope is also the impact of an SSRF bug, because the metadata endpoint is reachable from inside the workload.

The loop, answered

Every field is required, which is why no lesson here can recommend something without saying what it costs and what simpler thing to consider first.

How it works
  • An identity is created for the workload itself — an instance role, a service account, an execution role, a federated CI identity.
  • The platform attests to the workload: instance metadata, a projected service-account token, or a signed CI assertion.
  • That attestation is exchanged for credentials scoped to the workload's role, valid for minutes and renewed automatically by the SDK.
  • The workload signs its API calls with those credentials; the audit trail records the workload identity as the actor.
  • Human identities remain separate, interactive, multi-factor and time-bounded, and are never used by an automated process.
What you still own
  • Own one identity per workload, not one shared identity per environment — sharing collapses the audit trail and merges blast radii.
  • Own a pipeline check that fails the build when a long-lived cloud credential appears in configuration or source.
  • Own the offboarding audit: verify that no human credential is referenced by any running system before the first time it matters.
  • Own the local-development story, or developers will invent one with their own keys. Give them a low-privilege role they can assume instead.
  • Own metadata-endpoint hardening, since it is the credential source that an SSRF bug tries to reach.
How it fails
  • Offboarding takes down production because a departed employee's key was in a deployment configuration.
  • An incident cannot be attributed: the audit log shows a human for actions performed by a service, and nobody can tell which calls were which.
  • A compromised container yields administrator-adjacent permissions because it was running as an engineer.
  • One shared service account across ten workloads, so the blast radius of the weakest is the blast radius of all ten.
  • A key rotation breaks four services nobody knew were using it, because the key was copied rather than referenced.
  • An SSRF vulnerability in an application reads workload credentials from the metadata endpoint and uses them from outside.
How it scales
  • Identity count grows with services × environments, which is correct and desirable — the alternative is sharing, and sharing is the failure.
  • This only stays manageable when identities and policies are generated from infrastructure code alongside the workload — see Infrastructure as Code.
  • What runs out is naming and ownership discipline, not any technical limit: an identity nobody owns is an identity nobody removes.
  • Federation scales best for CI, because it eliminates a stored credential per pipeline rather than adding one.
Security
  • This is the highest-leverage identity control in cloud infrastructure: it simultaneously shrinks blast radius, repairs attribution and removes a stored secret.
  • Human access should be interactive, multi-factor, time-bounded and rarely privileged; workload access should be non-interactive, short-lived and narrowly scoped.
  • Never let a human identity be used non-interactively, and never let a workload identity be used interactively — the second is how a debugging session inherits a service's production write permissions.
  • The metadata endpoint is a credential source reachable from inside the workload; require its session-token protection and treat SSRF as a credential-theft class of bug.
  • Break-glass human access to production should exist, be separate, be alerted on every use, and be reviewed afterwards. See Audit Trails.
Cost shape
  • Workload identities are free. The cost is a small, one-time engineering effort per workload to wire attestation instead of a key.
  • It removes cost elsewhere: fewer secrets to store, rotate, audit and leak.
  • The unpriced cost of the shortcut is the incident where attribution is impossible and containment means disabling a person's account.
What to watch
  • Which identities are actually calling which services — a workload appearing under a human identity is the signal, and it is visible in the audit trail today.
  • Long-lived access keys that exist at all, and their age. Any key older than your rotation policy is a finding by definition.
  • Interactive sessions from non-interactive sources: a human identity making calls from a data-centre address is the exact fingerprint of this mistake.
  • Credential-issuance rate from the metadata endpoint, which spikes in a specific way during credential theft.
  • The signal that lies: the application's success rate. It is identical whether it runs as a workload role or as a departed engineer.
Simpler alternatives
  • A secret manager holding a static key, when the platform offers no workload identity for that dependency — a third-party API, for example. Second best, and legitimate. See Secrets in Infrastructure.
  • A dedicated non-human service account with its own long-lived key, when nothing better is available. Still far better than a person's key: the permissions can be scoped and offboarding does not touch it.
  • For a single-developer prototype with no production traffic, a personal credential is an acceptable, explicitly temporary shortcut — as long as it never reaches a deployed environment.
  • Federation from an existing identity provider, when human access to cloud accounts is the problem rather than workload access.
What adopting this costs
  • Buys correct permissions, correct attribution and a credential-free deployment; costs a per-workload wiring step and a local-development story you must design.
  • One identity per workload buys small, independent blast radii; costs more identities to name, own and review.
  • Short-lived credentials buy limited exposure on leak; cost occasional confusion when a long-running job outlives its credential and the SDK is not renewing correctly.
  • Removing human keys from applications buys safe offboarding; costs the friction of denying the fastest thing a developer can do to make something work.

What people believe, and what is true

Claim

The key is in a secret manager, so it is fine.

Reality

A well-stored credential belonging to a human is still a human's permissions and a human's audit identity. Storage was never the problem.

Claim

It is only staging.

Reality

Staging configuration is copied to production more often than anyone admits, and staging credentials frequently have production read access.

Claim

Workload identity is complicated to set up.

Reality

It is a one-time wiring step per workload and it removes the entire secret lifecycle for that dependency. It is usually less total work.

Apply it