|
| 1 | +--- |
| 2 | +title : "Create an Assumable Identity to Authenticate from AWS" |
| 3 | +linktitle: "AWS" |
| 4 | +lead: "" |
| 5 | +description: "Tutorial outlining how to create a Chainguard identity that can be assumed by an AWS user or role using outbound identity federation." |
| 6 | +type: "article" |
| 7 | +date: 2026-01-05T09:00:00+00:00 |
| 8 | +lastmod: 2026-01-05T09:00:00+00:00 |
| 9 | +draft: false |
| 10 | +tags: ["Chainguard Containers"] |
| 11 | +images: [] |
| 12 | +weight: 011 |
| 13 | +--- |
| 14 | + |
| 15 | +Chainguard's [*assumable identities*](/chainguard/administration/assumable-ids/assumable-ids/) are identities that can be assumed by external applications or workflows in order to access Chainguard resources or perform certain actions. |
| 16 | + |
| 17 | +This tutorial outlines how to create a Chainguard identity that can be assumed by an AWS IAM user or IAM role using [AWS IAM outbound identity federation](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_outbound.html). |
| 18 | + |
| 19 | +## Prerequisites |
| 20 | + |
| 21 | +To complete this guide, outbound identity federation must be enabled for your AWS account. Follow [the official AWS documentation](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_outbound_getting_started.html#enable-outbound-federation) to set this up. |
| 22 | + |
| 23 | +You will also need the following tools. |
| 24 | + |
| 25 | +* The AWS CLI. Review the official documentation for information on [how to install or update to the latest version of the tool](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html). |
| 26 | +* To create the assumable identity, you will need one of the following tools: |
| 27 | + * [`chainctl`](/chainguard/chainctl-usage/getting-started-with-chainctl/) — the Chainguard command line interface tool. Follow our guide on [How to Install `chainctl`](/chainguard/chainctl-usage/how-to-install-chainctl/) to set this up. |
| 28 | + * [`terraform`](https://developer.hashicorp.com/terraform) — an Infrastructure as Code tool developed by Hashicorp. Follow [the official Terraform documentation](https://developer.hashicorp.com/terraform/tutorials/aws-get-started/install-cli) for instructions on installing the tool. |
| 29 | + |
| 30 | +## Retrieve Token Issuer URL |
| 31 | + |
| 32 | +Each AWS account has a different issuer URL for outbound identity federation. You can retrieve it from the AWS Console UI by navigating to `IAM > Account settings > STS` as described in [the official getting started guide](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_outbound_getting_started.html). |
| 33 | + |
| 34 | +The token issuer URL will align with the following format: |
| 35 | + |
| 36 | +``` |
| 37 | +https://<uuid>.tokens.sts.global.api.aws |
| 38 | +``` |
| 39 | + |
| 40 | +## Create the Identity |
| 41 | + |
| 42 | +This guide outlines two methods for creating an identity that can be assumed by an AWS user or IAM role: one using `chainctl` over a command-line interface, and another using Terraform. |
| 43 | + |
| 44 | +### CLI |
| 45 | + |
| 46 | +Firstly, attach a policy to your AWS role or user that allows it to call `sts:GetWebIdentityToken` for the `https://issuer.enforce.dev` audience. The following example is a minimal policy that allows this: |
| 47 | + |
| 48 | +```json |
| 49 | +{ |
| 50 | + "Version": "2012-10-17", |
| 51 | + "Statement": [ |
| 52 | + { |
| 53 | + "Effect": "Allow", |
| 54 | + "Action": "sts:GetWebIdentityToken", |
| 55 | + "Resource": "*", |
| 56 | + "Condition": { |
| 57 | + "ForAnyValue:StringEquals": { |
| 58 | + "sts:IdentityTokenAudience": "https://issuer.enforce.dev" |
| 59 | + }, |
| 60 | + "NumericLessThanEquals": { |
| 61 | + "sts:DurationSeconds": 300 |
| 62 | + } |
| 63 | + } |
| 64 | + } |
| 65 | + ] |
| 66 | +} |
| 67 | +``` |
| 68 | + |
| 69 | +Then, run this command which uses `chainctl` to create a Chainguard identity and assign it the `registry.pull` role. Substitute `<identity-name>`, `<issuer-url>` and `<aws-arn>` with the name you want to give the identity, the issuer URL you just retrieved and the ARN of the AWS user or role you want to assume the identity with, respectively. |
| 70 | + |
| 71 | +```shell |
| 72 | +chainctl iam id create <identity-name> --identity-issuer=<issuer-url> --subject=<aws-arn> --role=registry.pull |
| 73 | +``` |
| 74 | + |
| 75 | +This command should return the identity's [UIDP (unique identity path)](/chainguard/administration/cloudevents/events-reference/#uidp-identifiers). Note this value down, as you'll need it to assume the identity later. |
| 76 | + |
| 77 | +If you need to retrieve the UIDP later on, you can always run the following `chainctl` command to list the identity. |
| 78 | + |
| 79 | +```sh |
| 80 | +chainctl iam identities list --name=<identity-name> |
| 81 | +``` |
| 82 | + |
| 83 | +If you're unsure which ARN or issuer URL to provide to `chainctl iam id create`, you can issue a token with `aws sts get-web-identity-token` and inspect the claims with `jwt`. To complete this command, you must install `jwt` as described on [this page](https://github.com/mike-engel/jwt-cli#installation). |
| 84 | + |
| 85 | +```shell |
| 86 | +aws sts get-web-identity-token \ |
| 87 | + --audience=https://issuer.enforce.dev \ |
| 88 | + --signing-algorithm=ES384 \ |
| 89 | + --query WebIdentityToken \ |
| 90 | + --output text \ |
| 91 | + | jwt decode -j - \ |
| 92 | +``` |
| 93 | + |
| 94 | +The output will look like this: |
| 95 | + |
| 96 | +```json |
| 97 | +{ |
| 98 | + "header": { |
| 99 | + "typ": "JWT", |
| 100 | + "alg": "ES384", |
| 101 | + "kid": "EC384_0" |
| 102 | + }, |
| 103 | + "payload": { |
| 104 | + "aud": "https://issuer.enforce.dev", |
| 105 | + "exp": 1766915543, |
| 106 | + "https://sts.amazonaws.com/": { |
| 107 | + "aws_account": "123456789012", |
| 108 | + "org_id": "o-anih79mrvn", |
| 109 | + "original_session_exp": "2025-12-28T10:45:29Z", |
| 110 | + "ou_path": [ |
| 111 | + "o-anih79mrvn/r-amv9/ou-amv9-ndi0nlgq/" |
| 112 | + ], |
| 113 | + "principal_id": "arn:aws:iam::123456789012:role/example", |
| 114 | + "source_region": "us-west-2" |
| 115 | + }, |
| 116 | + "iat": 1766915243, |
| 117 | + "iss": "https://00000000-0000-0000-0000-000000000000.tokens.sts.global.api.aws", |
| 118 | + "jti": "00000000-0000-0000-0000-000000000000", |
| 119 | + "sub": "arn:aws:iam::123456789012:role/example" |
| 120 | + } |
| 121 | +} |
| 122 | +``` |
| 123 | + |
| 124 | +The `sub` and `iss` claims are the values you should provide to `--subject` and `--identity-issuer`, respectively. |
| 125 | + |
| 126 | +### Terraform |
| 127 | + |
| 128 | +You can also create an assumable identity with the [Chainguard Terraform provider](https://registry.terraform.io/providers/chainguard-dev/chainguard/latest). The following example demonstrates creating an identity that can be assumed by an IAM role. It binds the `registry.pull` role to the identity. |
| 129 | + |
| 130 | +Substitute your Chainguard organization name for `<org-name>` and the issuer URL for `<issuer-url>`. |
| 131 | + |
| 132 | +```hcl |
| 133 | +terraform { |
| 134 | + required_providers { |
| 135 | + aws = { source = "hashicorp/aws" } |
| 136 | + chainguard = { source = "chainguard-dev/chainguard" } |
| 137 | + } |
| 138 | +} |
| 139 | +
|
| 140 | +data "aws_caller_identity" "current" {} |
| 141 | +
|
| 142 | +resource "aws_iam_role" "example" { |
| 143 | + name = "example-role" |
| 144 | +
|
| 145 | + # Configuration omitted for brevity |
| 146 | +} |
| 147 | +
|
| 148 | +resource "aws_iam_role_policy" "example_policy" { |
| 149 | + name = "web-identity-token-policy" |
| 150 | + role = aws_iam_role.example.id |
| 151 | +
|
| 152 | + policy = jsonencode({ |
| 153 | + Version = "2012-10-17" |
| 154 | + Statement = [ |
| 155 | + { |
| 156 | + Effect = "Allow" |
| 157 | + Action = "sts:GetWebIdentityToken" |
| 158 | + Resource = "*" |
| 159 | + Condition = { |
| 160 | + "ForAnyValue:StringEquals" = { |
| 161 | + "sts:IdentityTokenAudience" = "https://issuer.enforce.dev" |
| 162 | + } |
| 163 | + "NumericLessThanEquals" : { |
| 164 | + "sts:DurationSeconds" : 300 |
| 165 | + } |
| 166 | + } |
| 167 | + } |
| 168 | + ] |
| 169 | + }) |
| 170 | +} |
| 171 | +
|
| 172 | +data "chainguard_group" "org" { |
| 173 | + name = "<org-name>" |
| 174 | +} |
| 175 | +
|
| 176 | +resource "chainguard_identity" "my_identity_name" { |
| 177 | + parent_id = data.chainguard_group.org.id |
| 178 | + name = "my-identity-name" |
| 179 | + claim_match { |
| 180 | + issuer = "<issuer-url>" |
| 181 | + subject = aws_iam_role.example.arn |
| 182 | + } |
| 183 | +} |
| 184 | +
|
| 185 | +data "chainguard_role" "registry_pull" { |
| 186 | + name = "registry.pull" |
| 187 | +} |
| 188 | +
|
| 189 | +resource "chainguard_rolebinding" "my_identity_name_registry_pull" { |
| 190 | + identity = chainguard_identity.my_identity_name.id |
| 191 | + role = data.chainguard_role.registry_pull.items[0].id |
| 192 | + group = data.chainguard_group.org.id |
| 193 | +} |
| 194 | +
|
| 195 | +output "my_identity_name_id" { |
| 196 | + value = chainguard_identity.my_identity_name.id |
| 197 | +} |
| 198 | +``` |
| 199 | + |
| 200 | +The `my_identity_name_id` output provides the identity’s [UIDP (unique identity path)](/chainguard/administration/cloudevents/events-reference/#uidp-identifiers). You’ll need this value to assume the identity later. |
| 201 | + |
| 202 | +## Assume the Identity |
| 203 | + |
| 204 | +After creating an identity with either method outlined previously, generate a token with `aws sts get-web-identity-token`: |
| 205 | + |
| 206 | +```shell |
| 207 | +TOK=$(aws sts get-web-identity-token --audience=https://issuer.enforce.dev --signing-algorithm=ES384 --query WebIdentityToken --output text) |
| 208 | +``` |
| 209 | + |
| 210 | +Then use the token to log in with `chainctl`. Provide the UIDP of the identity as `<identity-id>`: |
| 211 | + |
| 212 | +```shell |
| 213 | +chainctl auth login \ |
| 214 | + --identity <identity-id> \ |
| 215 | + --identity-token "${TOK}" |
| 216 | +``` |
| 217 | + |
| 218 | +Now you will be able to issue `chainctl` commands under this assumed identity. For instance, you could list the container image repositories available to your organization: |
| 219 | + |
| 220 | +```shell |
| 221 | +chainctl image repo list |
| 222 | +``` |
| 223 | + |
| 224 | +## Learn More |
| 225 | + |
| 226 | +By following this guide, you will have created a Chainguard identity that you can use to authenticate to Chainguard from AWS. For more information about how assumable identities work in Chainguard, check out our [conceptual overview of assumable identities](/chainguard/administration/assumable-ids/assumable-ids/). Additionally, we encourage you to read through the rest of our documentation on [Administering Chainguard resources](/chainguard/administration/). |
0 commit comments