Penv Cloud
Start free
Docs
Guidesince cloud@2026-09-10

Authenticate an AWS workload

Your AWS workload signs sts:GetCallerIdentity with its role; we replay it to AWS to learn the caller. Console setup, exchange rules, and the beta.2 gap.

Not verified yet

This page is an outline. Nothing here has been confirmed against source, so do not follow it as product behavior.

Your workload on AWS signs one sts:GetCallerIdentity request with the role it runs as and sends the signed request to us; we replay it to AWS to learn the caller. Your secret key never leaves the machine. See machine identity.

Use penv 1.0.0-beta.3 or later. 1.0.0-beta.2 does not sign x-penv-cloud-org (your workspace id), and we refuse a request whose signed headers omit it, so every AWS exchange from beta.2 fails. On beta.2, pass a console-issued pck_ credential in PENV_TOKEN.

Where penv finds AWS credentials

After PENV_TOKEN, a login, a keypair and a platform OIDC token, penv tries AWS in the SDKs' order:

OrderSourceSet by
1AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, optional AWS_SESSION_TOKENYou, or Lambda for the execution role
2AWS_WEB_IDENTITY_TOKEN_FILE and AWS_ROLE_ARN, exchanged with STS AssumeRoleWithWebIdentityEKS IRSA
3AWS_CONTAINER_CREDENTIALS_RELATIVE_URIECS task roles
4AWS_CONTAINER_CREDENTIALS_FULL_URI with AWS_CONTAINER_AUTHORIZATION_TOKEN_FILEEKS Pod Identity

penv signs a regional STS host, resolving the region from AWS_REGION, then AWS_DEFAULT_REGION, then us-east-1.

On Lambda, the layer runs penv around your function: see serverless functions. Containers: Docker.

Exchange rules

RuleReason
Body is exactly Action=GetCallerIdentity&Version=2011-06-15; we refuse unknown parametersIgnoring an extra parameter is how CVE-2022-2385 worked against another AWS authenticator
x-penv-cloud-org must appear in SignedHeaders, not merely be presentA present header can be fitted onto any captured signature
We match on account id plus the role's immutable unique idRole names can be recreated; session names are caller-chosen
We match the STS host whole against a per-partition pattern (sts.amazonaws.com, sts.<region>.amazonaws.com, GovCloud, China)No substring test, which would accept amazonaws.com.cn.evil.com
We accept each signed request onceSigV4 has no nonce; our distributed guard records the request only after AWS answers, so a retry after an outage works

Set up the trust

Read the role's unique id

aws iam get-role --role-name YOUR_ROLE --query Role.RoleId

The AROA... id is not the ARN; a recreated role gets a new one.

Create the identity

Open Machine Identities then All Identities and create an identity bound to the project and environment it reads. Creating it needs step-up.

Add an AWS trust

FieldValue
Partitionaws, or your partition
Account IDThe twelve-digit account the role belongs to
Role unique IDThe AROA... id
ExpiryHow long the trust lasts

Run

penv run -- <command>

Since 1.0.0-beta.3, penv signs your workspace id in x-penv-cloud-org. Run it with no PENV_TOKEN and no login on the host, so the AWS role is the credential it uses. penv run reads the bound environment. Confirm in the audit log that the read names the new identity.

Exchange result

We return a pck_ credential bound to one project and one environment, lasting fifteen minutes and capped by the trust's expiry.

AnswerMeaning
401 unauthorizedThe trust names another account or role id
401 expiredThe trust's expiry has passed
403 forbiddenThe request named another environment
503 unavailableWe could not reach AWS STS; retry once

All codes: our API answers.

Enroll a bare host