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
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:
| Order | Source | Set by |
|---|---|---|
| 1 | AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, optional AWS_SESSION_TOKEN | You, or Lambda for the execution role |
| 2 | AWS_WEB_IDENTITY_TOKEN_FILE and AWS_ROLE_ARN, exchanged with STS AssumeRoleWithWebIdentity | EKS IRSA |
| 3 | AWS_CONTAINER_CREDENTIALS_RELATIVE_URI | ECS task roles |
| 4 | AWS_CONTAINER_CREDENTIALS_FULL_URI with AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE | EKS 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
| Rule | Reason |
|---|---|
Body is exactly Action=GetCallerIdentity&Version=2011-06-15; we refuse unknown parameters | Ignoring an extra parameter is how CVE-2022-2385 worked against another AWS authenticator |
x-penv-cloud-org must appear in SignedHeaders, not merely be present | A present header can be fitted onto any captured signature |
| We match on account id plus the role's immutable unique id | Role 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 once | SigV4 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.RoleIdThe 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
| Field | Value |
|---|---|
| Partition | aws, or your partition |
| Account ID | The twelve-digit account the role belongs to |
| Role unique ID | The AROA... id |
| Expiry | How long the trust lasts |
Exchange result
We return a pck_ credential bound to one project and one environment, lasting fifteen minutes and
capped by the trust's expiry.
| Answer | Meaning |
|---|---|
401 unauthorized | The trust names another account or role id |
401 expired | The trust's expiry has passed |
403 forbidden | The request named another environment |
503 unavailable | We could not reach AWS STS; retry once |
All codes: our API answers.
Other CI platforms
Connect your pipeline through any of the 62 OIDC platforms in our catalog, hand the platform token to penv, or enter a trust by hand.
Enroll a bare host
Bind a keypair to your VPS when it has no signed metadata. Every login advances a counter you and we both hold; a copied key locks both out.