Skip to content

Add an option to request session tags in the OIDC token#18

Merged
yob merged 3 commits into
mainfrom
session-tags
Feb 18, 2025
Merged

Add an option to request session tags in the OIDC token#18
yob merged 3 commits into
mainfrom
session-tags

Conversation

@yob
Copy link
Copy Markdown

@yob yob commented Feb 16, 2025

Session tags are a more secure way for customers to define which tokens can assume an IAM role. Rather than use the error prone sub claim with colon separators, the trust policy of an IAM Role can target specific claims and the required values (like organization_slug, pipeline_slug, and build_branch.

Unfortunately any OIDC token with session tags needs an additional permission in the trust policy of the role they are assuming (sts:TagSession, see below). Automatically adding session tags would break role assuming for customers with existing roles and trust policies, so for now we default the option to blank and request no session tags.

Maybe in a v2.0.0 release we could invert the behaviour and default to a few commonly used session tags? I'd suggest

  • organization_slug
  • pipeline_slug
  • build_branch

For now, users can opt in to session tags like so, using any valid claim from the Buildkite OIDC docs.

steps:
  - command: aws sts get-caller-identity
    plugins:
      - aws-assume-role-with-web-identity#v1.1.0:
          role-arn: arn:aws:iam::AWS-ACCOUNT-ID:role/SOME-ROLE
          session-tags:
          - organization_slug
          - pipeline_slug
          - build_branch

The resulting token duplicates the requested claims into a nested https://aws.amazon.com/tags claim:

{
  "iss": "https://agent.buildkite.com",
  "sub": "organization:example-org:pipeline:example-pipeline:ref:refs/heads/main:commit:cb9c1decb3b7c3c0fcd8a4368bb6a492e631b571:step:",
  "aud": "https://sts.amazonaws.com",
  "iat": 1739694861,
  "nbf": 1739694861,
  "exp": 1739695161,
  "organization_slug": "example-org",
  "pipeline_slug": "example-pipeline",
  "build_number": 2,
  "build_branch": "main",
  "build_tag": null,
  "build_commit": "cb9c1decb3b7c3c0fcd8a4368bb6a492e631b571",
  "step_key": null,
  "job_id": "01950de4-b414-4ef4-a604-6599c16ec9d9",
  "agent_id": "01950de4-bef7-4802-ad00-b509d14f66ab",
  "build_source": "ui",
  "runner_environment": "buildkite-hosted",
  "https://aws.amazon.com/tags": {
    "principal_tags": {
      "organization_slug": [
        "example-org"
      ],
      "pipeline_slug": [
        "example-pipeline"
      ],
      "build_branch": [
        "main"
      ]
    }
  }
}

Here's an example trust policy for a role that can only be assumed by main branch builds in the https://buildkite.com/example-org/example-pipeline pipeline

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Federated": "arn:aws:iam::111111111111:oidc-provider/agent.buildkite.com"
            },
            "Action": [
                "sts:AssumeRoleWithWebIdentity",
                "sts:TagSession"
            ],
            "Condition": {
                "StringEquals": {
                    "agent.buildkite.com:aud": "sts.amazonaws.com"
                },
                "ForAnyValue:StringEquals": {
                    "aws:RequestTag/organization_slug": "example-org",
                    "aws:RequestTag/pipeline_slug": "example-pipeline",
                    "aws:RequestTag/build_branch": "main"
                }
            }
        }
    ]
}

Note the addition of the sts:TagSession action, which we previously haven't documented as required. Without this, any call to AssumeRoleWithWebIdentity with a token that has session tags will fail. These docs were useful in highlighting this requirement.

@yob yob requested a review from a team as a code owner February 16, 2025 12:23
@yob yob requested a review from sj26 February 16, 2025 12:23
@yob
Copy link
Copy Markdown
Author

yob commented Feb 16, 2025

Hmm. It would be good to mention that session tags are captured in cloudtrail logs too. Maybe with an example?

@wolfeidau
Copy link
Copy Markdown

@yob I think it would be good to demo using an org identifier as well, given org slug can be changed.

@yob yob force-pushed the session-tags branch 3 times, most recently from 764dcd8 to cf00f74 Compare February 17, 2025 05:41
@yob
Copy link
Copy Markdown
Author

yob commented Feb 17, 2025

@wolfeidau how's this?

@sj26
Copy link
Copy Markdown
Collaborator

sj26 commented Feb 17, 2025

Nice!

Would folks expect to be able to use arrays too? Doesn't have to be a right-now thing.

steps:
  - command: aws sts get-caller-identity
    plugins:
      - aws-assume-role-with-web-identity#v1.1.0:
          role-arn: arn:aws:iam::AWS-ACCOUNT-ID:role/SOME-ROLE
          session-tags:
            - organization_slug
            - pipeline_slug
            - build_branch

@sj26
Copy link
Copy Markdown
Collaborator

sj26 commented Feb 17, 2025

I was thinking we could add buildkite-agent oidc request-token ... --no-default-claims option if the claims became too big and heavy. Core JWT attributes would always stay, including sub, to identify where the token came from. But if you only care about a couple of session tags then the rest of our default claims might be noise.

@yob yob force-pushed the session-tags branch 4 times, most recently from 1337458 to 84a761e Compare February 17, 2025 06:59
@yob
Copy link
Copy Markdown
Author

yob commented Feb 17, 2025

I was thinking we could add buildkite-agent oidc request-token ... --no-default-claims option if the claims became too big and heavy

I like it. Since making session tags the default behaviour is a breaking change, and that flag doesn't exist yet: maybe we can mull over ideas for a future v2 release that use session tags by default and has an option to disable the default claims if they're not needed. Or even, turn off the default claims and go all in on session tags 🤔

Would folks expect to be able to use arrays too? Doesn't have to be a right-now thing.

Hmm. The array syntax definitely reads nicer. I'll look into it!

@yob yob force-pushed the session-tags branch 4 times, most recently from 0f45e3f to 1d5c63f Compare February 17, 2025 08:47
@yob
Copy link
Copy Markdown
Author

yob commented Feb 17, 2025

I've switched the syntax to accept claims for array instead of CSV string

James Healy added 3 commits February 17, 2025 22:16
Session tags are a more secure way for customers to define which tokens
can assume an IAM role. Rather than use the error prone `sub` claim with
colon separators, the trust policy of an IAM Role can target specific
claims and the required values (like `organization_slug`,
`pipeline_slug`, and `build_branch`.

Unfortunately any OIDC token with session tags needs an additional
permission in the trust policy of the role they are assuming
(`sts:TagSession`, see below). Automatically adding session tags would
break role assuming for customers with existing roles and trust
policies, so for now we default the option to blank and request no
session tags.

Maybe it a v2.0.0 release we could invert the behaviour and default to a
few commonly used session tags? I'd suggest

* organization_slug
* pipeline_slug
* build_branch

For now users can opt in to session tags like so, using any valid claim
from the Buildkite OIDC docs[1].

```yaml
steps:
  - command: aws sts get-caller-identity
    plugins:
      - aws-assume-role-with-web-identity#v1.1.0:
          role-arn: arn:aws:iam::AWS-ACCOUNT-ID:role/SOME-ROLE
          session-tags: "organization_slug,pipeline_slug,build_branch"
```

The resulting token duplicates the requested claims into a nested
`https://aws.amazon.com/tags` claim:

```json
{
  "iss": "https://agent.buildkite.com",
  "sub": "organization:example-org:pipeline:example-pipeline:ref:refs/heads/main:commit:cb9c1decb3b7c3c0fcd8a4368bb6a492e631b571:step:",
  "aud": "https://sts.amazonaws.com",
  "iat": 1739694861,
  "nbf": 1739694861,
  "exp": 1739695161,
  "organization_slug": "example-org",
  "pipeline_slug": "example-pipeline",
  "build_number": 2,
  "build_branch": "main",
  "build_tag": null,
  "build_commit": "cb9c1decb3b7c3c0fcd8a4368bb6a492e631b571",
  "step_key": null,
  "job_id": "01950de4-b414-4ef4-a604-6599c16ec9d9",
  "agent_id": "01950de4-bef7-4802-ad00-b509d14f66ab",
  "build_source": "ui",
  "runner_environment": "buildkite-hosted",
  "https://aws.amazon.com/tags": {
    "principal_tags": {
      "organization_slug": [
        "example-org"
      ],
      "pipeline_slug": [
        "example-pipeline"
      ],
      "build_branch": [
        "main"
      ]
    }
  }
}
```

Here's an example trust policy for a role that can only be assumed by
`main` branch builds in the https://buildkite.com/example-org/example-pipeline
pipeline

```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Federated": "arn:aws:iam::111111111111:oidc-provider/agent.buildkite.com"
            },
            "Action": [
                "sts:AssumeRoleWithWebIdentity",
                "sts:TagSession"
            ],
            "Condition": {
                "StringEquals": {
                    "agent.buildkite.com:aud": "sts.amazonaws.com"
                },
                "ForAnyValue:StringEquals": {
                    "aws:RequestTag/organization_slug": "example-org",
                    "aws:RequestTag/pipeline_slug": "example-pipeline",
                    "aws:RequestTag/build_branch": "main"
                }
            }
        }
    ]
}
```

Note the addition of the `sts:TagSession` action, which we previously
haven't documented as required. Without this, any call to
`AssumeRoleWithWebIdentity` with a token that has session tags will
fail. [2] was useful in highlighting this requirement.

[1] https://buildkite.com/docs/agent/v3/cli-oidc#request-oidc-token
[2] https://docs.aws.amazon.com/IAM/latest/UserGuide/id_session-tags.html
Suggested by Sam. I tried it out, and I like it a lot more. The bash
required to wrangle it is a pain, but the UX for customers is much
nicer.

Hat tip to these sources for bash help:

* docker plugin https://github.com/buildkite-plugins/docker-buildkite-plugin/blob/db8815a107fa6588d78855c8491dae969a15fefd/lib/shared.bash#L28-L48
* stackoverflow: https://stackoverflow.com/a/17841619
* https://linuxize.com/post/bash-functions/
Copy link
Copy Markdown
Contributor

@pzeballos pzeballos left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! I think is great that we'll ship this for now and work on a v2 with the breaking changes.

@yob yob merged commit a876e47 into main Feb 18, 2025
@yob yob deleted the session-tags branch February 18, 2025 00:44
buildkite-systems pushed a commit to buildkite/docs that referenced this pull request Feb 18, 2025
…t-token`

This flag was added in agent version v3.83.0 of the agent, and is useful
for the common use case of assuming an AWS IAM role using OIDC.

In [1] I've confirmed how the flag works and built support for it into
our official plugin for AWS+OIDC, but I thought it was worth leaving a
few breadcrumbs of documentation about the low level flag and command as
well. No doubt some customer will find it helpful.

I'd like to also expand the higher level documentation on using OIDC to
integrate with AWS [2], but I'll tackle that in a separate PR.

[1] buildkite-plugins/aws-assume-role-with-web-identity-buildkite-plugin#18
[2] https://buildkite.com/docs/pipelines/security/oidc/aws
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants