The question this answers
Whose identity should an application present when it calls a cloud service?
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.
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.
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.
# 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# 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.
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.
- 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
- 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.
- • 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.
- • 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.
- • 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.
- • 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.
- • 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.
- • 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.
- • 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.
- • 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.
- • 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
The key is in a secret manager, so it is fine.
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.
It is only staging.
Staging configuration is copied to production more often than anyone admits, and staging credentials frequently have production read access.
Workload identity is complicated to set up.
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.