Skip to main content
INS // Insights

AWS IAM Roles Anywhere: Kill Standing Keys

Updated September 2026 · 5 min read

OIDC from GitLab or GitHub kills deploy keys in CI. It does nothing for the Windows service in the office, the self-hosted runner that is not a cloud VM, or the on-prem job that still has an IAM user key in a config file. AWS IAM Roles Anywhere is the cutover we use for those hosts: the machine presents an X.509 certificate, STS returns temporary credentials, and the standing AWS key leaves the building.

This is credential elimination, not a new secrets manager. The certificate is still a secret-shaped object. The difference is lifetime, scope, and the fact that AWS never issues a long-lived access key to that workload.

Where AWS IAM Roles Anywhere Replaces the Leftover Keys

After a workload identity federation migration and a GitLab or Actions cutover, the leftover AKIAs are boring and dangerous:

  • A vendor sync daemon on a colocation VM
  • A reporting job on a self-hosted box that cannot be an EC2 instance role
  • A Windows scheduled task that pushes objects to S3
  • A self-hosted GitLab runner whose host is not in AWS

Those identities never see id_tokens. They also never see IMDS. Teams "solve" that with an IAM user and a key that last rotated when someone left. Rotating long-lived AWS credentials is the bridge. Roles Anywhere is the exit.

AWS documents the service in the IAM Roles Anywhere user guide. The guide will not inventory your keys, pick a CA, or write the trust policy that refuses a cert from the wrong OU. That is the delivery.

The X.509 to STS Path We Ship

Three AWS objects, plus a CA you actually control:

  1. Trust anchor — the CA certificate AWS will honor. We prefer a dedicated intermediate, not the company-wide Wi-Fi CA, so a help-desk cert cannot assume the prod role.
  2. Profile — which IAM roles a successful cert can request, session duration, and optional session policies that shrink the role further.
  3. IAM role — trust policy allowing rolesanywhere.amazonaws.com to call sts:AssumeRole / CreateSession, with conditions on the cert (subject, issuer, OU).

On the host, the credential helper (or an equivalent credential_process) signs a CreateSession request with the private key and writes temporary keys into the default chain. The application keeps using the SDK as if nothing changed. The env file that used to hold AKIA... goes away.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "rolesanywhere.amazonaws.com"
      },
      "Action": [
        "sts:AssumeRole",
        "sts:SetSourceIdentity",
        "sts:TagSession"
      ],
      "Condition": {
        "StringEquals": {
          "aws:PrincipalTag/x509Subject/OU": "payments-batch"
        }
      }
    }
  ]
}

We set source identity from the cert subject so CloudTrail names the workload, not a shared IAM user called onprem-sync. Session tags from the certificate become the audit join key.

What We Kill, In Order

Inventory first. Every IAM user access key that is not a human break-glass, every ~/.aws/credentials on a server, every systemd unit with AWS_SECRET_ACCESS_KEY. Rank by privilege, not by how embarrassing the filename is.

Then we dual-run:

  1. Issue a workload cert from the dedicated intermediate. Put it in the host store the OS understands (we do not leave PEMs in /tmp).
  2. Point credential_process at Roles Anywhere. Confirm the app can sts:GetCallerIdentity as the new role.
  3. Watch CloudTrail for CreateSession from that host and for the old IAM user going quiet.
  4. Deactivate the access key. Wait. Delete the key. Delete the IAM user if it exists only for that job.

If the app cannot use the default credential chain — a language runtime that only reads static env vars — we wrap the process: a sidecar refreshes env on a timer. That wrapper is technical debt we label as such. The upgrade is an SDK that can call credential_process.

Tradeoffs Versus OIDC and Versus "Just Use a Vault"

OIDC is cheaper when you have an issuer. GitLab jobs should use GitLab OIDC AWS, not a cert per pipeline. Roles Anywhere is for hosts that will never mint a JWT from a cloud IdP.

A vault still hands out long-lived keys if that is what you stored. Dynamic AWS creds from a vault are a valid pattern. They also mean another cluster to run and another lease to debug at 2 a.m. For a handful of on-prem jobs, Roles Anywhere plus a boring CA is fewer moving parts. For a hundred workloads already on vault, we federate the vault's own AWS client and leave the rest.

Certificates expire. That is the point. We set workload cert TTL in months, not years, and we monitor days-to-expiry. A cert that nobody renews is a visible outage. A standing key that nobody rotates is a silent finding.

The CA is now in the blast radius. Compromise of the intermediate is compromise of every role that trusts the anchor. We keep that intermediate offline-ish (HSM or a tightly gated private CA), issue only workload certs from it, and name OUs that map to roles. We will not hang Roles Anywhere off a public ACME cert or a laptop-user CA.

Self-hosted SaaS appliances sometimes cannot run credential_process. Then the honest options are: put a tiny sidecar in front, move the job, or accept a vault-issued short key with a documented exception. We write the exception. We do not hide an AKIA in the appliance and call the project done.

Evidence for the Sample

The packet is:

  • IAM credential report (or Access Analyzer) showing the old users have no active keys
  • CloudTrail CreateSession / AssumeRole from rolesanywhere.amazonaws.com with source identity
  • Trust anchor and profile ARNs, plus the CA policy that says who may issue
  • A one-line statement that remaining AKIAs are named break-glass only, with owners

This supports the same non-human identity story as CI OIDC. It is how we run production SaaS batch paths that still have to talk to AWS from outside the VPC.

IAM Roles Anywhere work is AWS cloud infrastructure plus the credential-elimination side of security automation.

Talk to us about your credential elimination pilot: rutagon.com/contact · 907-841-8407 · contact@rutagon.com.

Frequently Asked Questions

Is AWS IAM Roles Anywhere a replacement for GitLab or GitHub OIDC?

No. Use OIDC when the caller can mint a JWT from a trusted issuer. Use Roles Anywhere when the caller is a host that only has a certificate and a private key. Many environments need both.

Do we need AWS Private CA, or can we use our existing internal CA?

You can bring your own CA as the trust anchor if you can issue a dedicated intermediate for workloads. We avoid using the same CA that issues employee VPN certs so a stolen laptop cert cannot assume the S3 role.

What happens when the certificate expires mid-job?

STS sessions last until their duration ends; the next CreateSession fails. We alert on cert expiry before that day and treat a surprise expiry as a change-management miss, not a reason to put the AKIA back.

Can one certificate assume every IAM role in the account?

Only if you built the profile that way. We do not. Each workload OU maps to one role, and the profile lists that role only. Shared "on-prem admin" certs are the old IAM user with extra steps.

How is this different from putting access keys in Secrets Manager?

Secrets Manager stores whatever you give it. If you store a long-lived key, you still have a long-lived key. Roles Anywhere never creates that key. Temporary credentials are minted at session time and die with the session.