Skip to content

PATTERN Cited by 1 source

OIDC role assumption for cross-cloud auth

Pattern

A workload running in platform A (Fly.io, GitHub Actions, a Kubernetes cluster, etc.) needs to call a service in platform B (AWS, GCP, Azure) without a long-lived credential shared between them. Set up the two platforms as OIDC IdP + OIDC relying party:

  1. Platform A runs an OIDC IdP that can issue JWTs asserting per-workload identity (per-org issuer, structured sub claim, standard discovery endpoint).
  2. Platform B registers the IdP once as an Identity Provider (AWS IAM: iam:CreateOpenIDConnectProvider).
  3. A Role is created in platform B with an AssumeRoleWith… trust policy that names the IdP as Federated principal and constrains aud + sub claims.
  4. The workload obtains an OIDC token from platform A's token endpoint at runtime.
  5. The workload (or the platform's SDK) calls platform B's token- exchange API (AssumeRoleWithWebIdentity for AWS, Workload Identity Federation for GCP) with the OIDC token.
  6. Platform B verifies the token against the IdP's discovery endpoint, checks the trust-policy conditions, and vends short- lived native credentials.
  7. The workload uses those native credentials for the intended call.

No long-lived credential ever exists on platform A.

Canonical wiki instance

sources/2024-06-19-flyio-aws-without-access-keys — Fly.io → AWS via systems/oidc-fly-ioSTS AssumeRoleWithWebIdentity → S3. Full twelve-step walkthrough.

Twelve-step flow, cited verbatim

  1. init detects AWS_ROLE_ARN is set as an environment variable.
  2. init sends a request to /v1/tokens/oidc via /.api/proxy.
  3. init writes the response to /.fly/oidc_token.
  4. init sets AWS_WEB_IDENTITY_TOKEN_FILE and AWS_ROLE_SESSION_NAME.
  5. The entrypoint boots, and (say) runs aws s3 get-object.
  6. The AWS SDK runs through the credential provider chain
  7. The SDK sees that AWS_WEB_IDENTITY_TOKEN_FILE is set and calls AssumeRoleWithWebIdentity with the file contents.
  8. AWS verifies the token against https://oidc.fly.io/example/.well-known/openid-configuration, which references a key Fly.io manages on isolated hardware.
  9. AWS vends STS credentials for the assumed Role.
  10. The SDK uses the STS credentials to access the S3 bucket.
  11. AWS checks the Role's IAM policy to see if it has access to the S3 bucket.
  12. AWS returns the contents of the bucket object.

(Source: sources/2024-06-19-flyio-aws-without-access-keys)

Steps 1–4 are the init-as- credential-broker side; steps 5–12 are the AWS SDK's standard credential-provider-chain behaviour, undiluted.

Canonical trust-policy shape

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Federated": "arn:aws:iam::123456123456:oidc-provider/oidc.fly.io/example"
            },
            "Action": "sts:AssumeRoleWithWebIdentity",
            "Condition": {
              "StringEquals": {
                "oidc.fly.io/example:aud": "sts.amazonaws.com"
              },
              "StringLike": {
                "oidc.fly.io/example:sub": "example:weather-cat:*"
              }
            }
        }
    ]
}

Two load-bearing conditions:

  • aud equals sts.amazonaws.com — STS will only honour tokens that Fly.io deliberately issued for STS. Without this, a token minted for a different audience could be cross-used.
  • sub matches <org>:<app>:* — the scoping pivot. Narrow further for per-Machine scoping.

Properties

  • No secret in the workload. The workload holds an identifier (Role ARN) and gets tokens on demand.
  • Short-lived credentials fall out automatically. Platform B's token exchange vends minutes-lifetime credentials. See concepts/short-lived-credential-auth.
  • Declarative trust-policy audit. Who can assume the Role is encoded as JSON in the Role's trust policy; easy to review, grep, diff.
  • ARN is not a secret. Can be shared in email / chat / code.
  • Pattern generalises. "Our OIDC support on the platform and in Fly Machines will set arbitrary OIDC audience strings, so you can use it to authenticate to any OIDC-compliant cloud provider." GCP / Azure / Vault / Snowflake / etc. all implement some form of OIDC-federated token exchange.

When to use

  • Workload in one platform needs to call APIs in another platform.
  • The call pattern is stable (you can pre-register the Role / trust policy) but the workloads may rotate / scale / ephemeralise rapidly (no chance to distribute long-lived keys).
  • The downstream platform supports some variant of AssumeRoleWithWebIdentity (AWS STS, GCP WIF, Azure workload- identity federation, Vault's OIDC auth backend).

When not to use

  • The workload needs to act on behalf of a human user with delegated credentials — use a user-scoped auth flow (SSO, OAuth2 authorization code) instead. OIDC federation is for workload identity, not user identity.
  • The counterparty doesn't support federated identity at all and will only accept static credentials — OIDC federation isn't an option.
  • You need attribute-based authorization beyond what trust-policy conditions support (e.g., dynamic attributes that change per-request) — you may still use OIDC federation for the outer authn, but add an inner authz layer.

Adjacent patterns

Seen in

  • sources/2024-06-19-flyio-aws-without-access-keys — canonical wiki instance. Full twelve-step flow, trust-policy shape, and honest positioning ("asymptotically approach the security properties of Macaroon tokens" — better than long-lived keys, not equal to Macaroons).
Last updated · 200 distilled / 1,178 read