configure-aws-credentials: AWS cannot filter for many claim keys in trust policies

I’m trying to match the GITHUB_ACTOR in my IAM trust relationship policy and cannot make it work. Is this supposed to work? The trust policy I have is:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Federated": "arn:aws:iam::00000000:oidc-provider/token.actions.githubusercontent.com"
      },
      "Action": "sts:AssumeRoleWithWebIdentity",
      "Condition": {
        "StringEquals": {
          "token.actions.githubusercontent.com:actor": "tve",
          "token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
        }
      }
    }
  ]
}

The error I get is:

Run aws-actions/configure-aws-credentials@master
Error: Not authorized to perform sts:AssumeRoleWithWebIdentity

In my workflow I print ${{ github.actor }} and it matches what I have in the trust policy. Is there a way to get a log of the actual JWT token that IAM receives?

About this issue

  • Original URL
  • State: open
  • Created 3 years ago
  • Reactions: 12
  • Comments: 36 (4 by maintainers)

Commits related to this issue

Most upvoted comments

I mixed up StringEquals and StringLike

Doesn’t work:

"Condition": {
  "StringEquals": {
    "token.actions.githubusercontent.com:sub": "repo:ORG/REPO:*"
  }
}

Works:

"Condition": {
  "StringLike": {
    "token.actions.githubusercontent.com:sub": "repo:ORG/REPO:*"
  }
}

Reopening for visibility, but more importantly to track that this issue is related to a limitation in AWS (maybe we can push this internally)

Google brought me here. Thanks @mungojam for finding the AWS documentation on a list of supported claims.

The GitHub doc should be improved because ForAllValues:StringEquals is an insecure operator for Allow statements. A non-existent / non-supported key (such as repository_owner) always evaulates to true. This makes ALL GitHub users be able to assume your IAM role.

One should always use StringEquals or StringLike. This way, even they accidentally specified an unsupported key, they will immediately find that out, instead of thinking “it works”, while actually letting everyone in.

We now have a section in our docs with what’s now the most up-to-date information on the topic. Being able to customize the sub claim key should be able to help with most customization needs

This snippet might be useful for anyone getting this working. It will print out all the info in the token. Just use it in a private repo and not in a live setting though

    steps:     
      - uses: actions/github-script@v6
        with:
          script: |
            const token = await core.getIDToken("hello");
            const [, payloadB64] = token.split('.');
            const payloadJson = atob(payloadB64);
            const payload = JSON.parse(payloadJson);
            console.log(`issuer is ${payload.iss}`);
            console.log(payload);
``

@lukas-hetzenecker You are right! I’ve just tested the following-

.input.json

{
    "use_default": false,
    "include_claim_keys": ["repo", "actor"]
}

And AWS Trust Relationship - repo:unfor19/gha-play-private:actor:unfor19

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Federated": "arn:aws:iam::123456789012:oidc-provider/token.actions.githubusercontent.com"
            },
            "Action": "sts:AssumeRoleWithWebIdentity",
            "Condition": {
                "StringEquals": {
                    "token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
                },
                "StringLike": {
                    "token.actions.githubusercontent.com:sub": "repo:unfor19/gha-play-private:actor:unfor19"
                }
            }
        }
    ]
}

Thanks for the tip, updated my solution

@JMoserCricut I tried what you offered, and it seems to be working 😄

Here’s my setup-

  1. Created AWS S3 Bucket - unfor19-gha-play-private
  2. Created AWS IAM OIDC Provider
    • Provider URL: token.actions.githubusercontent.com
    • Provider aud: sts.amazonaws.com
  3. Created IAM Policy - unfor19-gha-play-private-policy
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": "s3:ListBucket",
            "Resource": "arn:aws:s3:::*"
        },
        {
            "Sid": "VisualEditor1",
            "Effect": "Allow",
            "Action": "s3:PutObject",
            "Resource": "arn:aws:s3:::unfor19-gha-play-private/*"
        },
        {
            "Sid": "VisualEditor2",
            "Effect": "Allow",
            "Action": "s3:ListAllMyBuckets",
            "Resource": "*"
        }
    ]
}
  1. Created IAM Role (unfor19-gha-play-private-role) with the following trust relationship and assigned the above IAM Policy to it
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Federated": "arn:aws:iam::123456789012:oidc-provider/token.actions.githubusercontent.com"
            },
            "Action": "sts:AssumeRoleWithWebIdentity",
            "Condition": {
                "StringEquals": {
                    "token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
                },
                "StringLike": {
                    "token.actions.githubusercontent.com:sub": "repo:unfor19/gha-play-private:actor:unfor19"
                }
            }
        }
    ]
}
  1. Updated custom subject OIDC claims with GitHub CLI

Created input file for PUT request body -.input.json

{
    "use_default": false,
    "include_claim_keys": ["repo", "actor"]
}

Used GitHub REST API to PUT custom subject OIDC claims

gh api -X PUT repos/unfor19/gha-play-private/actions/oidc/customization/sub --input .input.json

Used GitHub REST API to get GET custom subject OIDC claims (to verify)

gh api -X GET repos/unfor19/gha-play-private/actions/oidc/customization/sub

Response:

{
  "use_default": false,
  "include_claim_keys": [
    "repo",
    "actor"
  ]
}

So far, I’m all set; now it’s time to set the workflow-

.github/workflows/oidc.yml

name: AWS example workflow
on:
    workflow_dispatch: {}
env:
  BUCKET_NAME: unfor19-gha-play-private
  AWS_REGION: eu-west-1
  ROLE_TO_ASSUME_ARN: arn:aws:iam::123456789012:role/unfor19-gha-play-private-role

permissions:
  id-token: write # This is required for requesting the JWT
  contents: read # This is required for actions/checkout
jobs:
  S3PackageUpload:
    runs-on: ubuntu-latest
    steps:
      - name: Git clone the repository
        uses: actions/checkout@v3
      - name: configure aws credentials
        uses: aws-actions/configure-aws-credentials@v2
        with:
          role-to-assume: ${{ env.ROLE_TO_ASSUME_ARN }}
          role-session-name: samplerolesession
          aws-region: ${{ env.AWS_REGION }}
      # Upload a file to AWS s3
      - name: Copy index.html to s3
        run: |
          date > index.html
          aws s3 cp ./index.html s3://${{ env.BUCKET_NAME }}/

The above setup works; @lukas-hetzenecker, thanks for the tip!

Arkadaşlar siz uçmuşssunuz bilmiyorum sizi yakalayabilirmiyimde inanın doktorların yazdığı reçete gibi konuşuyorsunuz. Hiç bir kelimenizi anlayamıyorum. Bari konudan bahsederken ne işe yaradığını düzeltme veya kodu yazınca nasıldı hangi işi pratikte görebileceğini bunlarıda açıklarsanız inanın sevinirim.

Hi y’all 👋 Just wanted to let y’all know there’s a workaround for this issue, but it comes with some big caveats, namely, the fact that you’ll need to utilize AWS Cognito rather than STS directly, which means it would almost certainly require some changes to this project in order to get working (disclaimer: I don’t actually use this Github Action, I was just pointed here by an altruistic coworker).

In a nutshell, the idea is this:

  1. Create a Cognito Identity Pool and connect it to the AWS IAM Open ID Connect Provider you are using now (i.e. arn:aws:iam::00000000:oidc-provider/token.actions.githubusercontent.com)
  2. Create a role mapping rule that checks whatever claim you would like to verify and assigns the role you’d like based on that value
  3. Modify the trust policy of the role you want to assume so that it can be assumed via role mapping, or better yet, create a whole new one so you don’t break your existing stuff
  4. Congrats. You now have a Cognito Identity Pool that you can fetch temporary credentials from that checks the value of custom claims before allowing users to assume a role 🎉

Now you’ll have to actually interface with AWS Cognito instead of just straight up going straight to STS. It’s a quick 1-2 punch that goes like this:

  1. Use your AWS account ID, the ID of the Cognito Identity Pool, and the OIDC token from Github Actions to get an ID
  2. Use the ID you got from the previous step, the ID of the Cognito Identity Pool, and the same OIDC token from Github Actions you used in the previous step to get credentials for that ID

Again, this would almost certainly require changes to this project, but I thought it’d be worth offering up as a potential workaround if anyone felt particularly ambitious! I tested to make sure all of this works using the AWS CLI and I can confirm that it does, albeit with a bit of additional cost to the user.

Woops, you are correct! ForAllValues behaves as you said.

After testing more thoroughly, I could not find a way to make it work correctly with repository_owner. I ended up having to switch to using StringLike with the sub claim. There is definitely something wrong here.

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Federated": "arn:aws:iam::[xxxxxxx]:oidc-provider/token.actions.githubusercontent.com"
            },
            "Action": "sts:AssumeRoleWithWebIdentity",
            "Condition": {
                "ForAllValues:StringEquals": {
                    "token.actions.githubusercontent.com:repository_owner": "ianling",
                    "token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
                }
            }
        }
    ]
}

Works for me!

I think it isn’t doing what you think it is. Try changing the owner condition to something random and it will still let you in (so you are currently very insecure).

From the AWS Docs:

ForAllValues – Tests whether the value of every member of the request set is a subset of the condition key set. The condition returns true if every key value in the request matches at least one value in the policy. It also returns true if there are no keys in the request, or if the key values resolve to a null data set, such as an empty string.

I think it is for testing a different type of request that has multiple sets of key/values in. The name seems really confusing.

Was there further guidance on getting the actor conditional claim to work? I’m equally trying with workflow to no avail.

I’m having the same problem with token.actions.githubusercontent.com:repository_owner. To me it seems that it’s a bug in AWS itself. I can see the property in the token itself when I decode it, but IAM doesn’t appear to think it exists. You can verify that by changing the condition to StringEqualsIfExists which then passes because IAM doesn’t see it for some reason.