Skip to content

Commit

Permalink
wip! GitHub OIDC → Cognito Identity → AssumeRoleWithWebSession
Browse files Browse the repository at this point in the history
Let's us map GitHub token claims to principal tags, which is great.

But ultimately not workable because GitHub OIDC doesn't include the
repository name _sans_ owner in its token claims (e.g. "zika" for
"nextstrain/zika"), so we don't have any policy variable to interpolate
into policy conditions.

I thought maybe we could (ab)use role session names for this and pass
the unqualified repo name via it ourselves, but that hit a wall because
while sts:RoleSessionName is available in the AssumeRoleWithWebIdentity
request, it's not available on subsequent requests which we need to be
able to use in policy conditions.

Then I thought maybe we could use sts:SourceIdentity instead in the role
assumption request which is available as aws:SourceIdentity in
subsequent requests, except that GitHub's token would have to include an
"https://aws.amazonaws.com/source_identity" claim, and it ofc does not
provide any way to do that.  If it did, it would likely also let us
provide a custom "https://aws.amazonaws.com/tags" claim at which point
all of this nonsense would be moot anyway.

Finally, I thought maybe we could use a custom audience ("aud" claim)
which GitHub _does_ let you specify.  This is available in AWS requests
as the "token.actions.githubusercontent.com:aud" key and we can use it
as a policy variable.  \o/  But to use it we'd need to cut out Cognito
Identity (e.g. go back to where we started without it) and lose the
token claims → principal tags mapping.  So we'd need to customize the
GitHub token's "sub" claim and match against it.  We'd also need to
explicitly list out all possible valid "aud" on the AWS OIDC IdP
configuration (as "client_id_list"), and that would be frustrating.

This all feels more complex than it's worth at this point.
  • Loading branch information
tsibley committed May 17, 2024
1 parent 9a30261 commit ffe1302
Show file tree
Hide file tree
Showing 4 changed files with 123 additions and 55 deletions.
22 changes: 22 additions & 0 deletions env/production/aws-cognito-identity.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# XXX FIXME: describe what we're doing here
# <https://catnekaise.github.io/github-actions-abac-aws/cognito-identity/>
# <https://catnekaise.github.io/github-actions-abac-aws/detailed-explanation/>
# <https://awsteele.com/blog/2023/10/25/aws-role-session-tags-for-github-actions.html>
resource "aws_cognito_identity_pool" "github-actions" {
identity_pool_name = "github-actions"
allow_unauthenticated_identities = false
allow_classic_flow = true
openid_connect_provider_arns = [aws_iam_openid_connect_provider.github-actions.arn]
}

resource "aws_cognito_identity_pool_provider_principal_tag" "github-actions" {
identity_pool_id = aws_cognito_identity_pool.github-actions.id
identity_provider_name = aws_iam_openid_connect_provider.github-actions.id
use_defaults = false
principal_tags = {
# tag name = OIDC token claim
repository_owner = "repository_owner"
repository = "repository"
job_workflow_ref = "job_workflow_ref"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,36 @@ resource "aws_iam_role" "GitHubActionsRoleNextstrainBatchJobs" {
{
"Effect": "Allow",
"Principal": {
"Federated": aws_iam_openid_connect_provider.github-actions.arn
}
"Action": "sts:AssumeRoleWithWebIdentity",
"Federated": "cognito-identity.amazonaws.com"
},
"Action": [
"sts:AssumeRoleWithWebIdentity",
"sts:TagSession",
],
"Condition": {
"StringEquals": {
"cognito-identity.amazonaws.com:aud": aws_cognito_identity_pool.github-actions.id,
"aws:RequestTag/repository_owner": "nextstrain",
"aws:RequestTag/respository": [
"nextstrain/$${sts:RoleSessionName}",
"nextstrain/$${sts:RoleSessionName}-ingest", # for ncov and ncov-ingest
],
},
"StringLike": {
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com",
"token.actions.githubusercontent.com:sub": "repo:nextstrain/*:*:job_workflow_ref:nextstrain/.github/.github/workflows/pathogen-repo-build.yaml@refs/heads/*",
}
"aws:RequestTag/job_workflow_ref": "nextstrain/.github/.github/workflows/pathogen-repo-build.yaml@*",
},
"ForAllValues:StringLike": {
"cognito-identity.amazonaws.com:amr": [
"authenticated",
"token.actions.githubusercontent.com",
"${aws_iam_openid_connect_provider.github-actions.arn}:*",
],
},
# Ensures that a missing/empty amr claim doesn't pass the
# ForAllValues condition above.
"Null": {
"cognito-identity.amazonaws.com:amr": "false",
},
},
}
]
Expand Down
103 changes: 73 additions & 30 deletions env/production/aws-iam-role-GitHubActionsRoleNextstrainBuilds.tf
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,7 @@ resource "aws_iam_role" "GitHubActionsRoleNextstrainBuilds" {

max_session_duration = 43200 # seconds (12 hours)

assume_role_policy = jsonencode({
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": aws_iam_openid_connect_provider.github-actions.arn
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringLike": {
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com",
"token.actions.githubusercontent.com:sub": "repo:nextstrain/*:*:job_workflow_ref:nextstrain/.github/.github/workflows/pathogen-repo-build.yaml@refs/heads/*",
}
},
}
]
})
assume_role_policy = aws_iam_role.GitHubActionsRoleNextstrainBatchJobs.assume_role_policy

# This role provides a superset of the permissions expected to actually be
# required by any individual Nextstrain pathogen build. In practice, we
Expand Down Expand Up @@ -60,9 +43,18 @@ resource "aws_iam_role" "GitHubActionsRoleNextstrainBuilds" {
"Resource": [
"arn:aws:s3:::nextstrain-data",
"arn:aws:s3:::nextstrain-data-private",
"arn:aws:s3:::nextstrain-ncov-private",
"arn:aws:s3:::nextstrain-staging",
],
"Condition": {
"StringLike": {
"s3:prefix": [
"$${sts:RoleSessionName}.json",
"$${sts:RoleSessionName}_*.json",
"files/workflows/$${sts:RoleSessionName}/*",
"files/datasets/$${sts:RoleSessionName}/*",
]
}
}
},
{
"Sid": "ReadWrite",
Expand All @@ -78,21 +70,72 @@ resource "aws_iam_role" "GitHubActionsRoleNextstrainBuilds" {
# but NOT s3:DeleteObjectVersion so objects can't be completely wiped
],
"Resource": [
"arn:aws:s3:::nextstrain-data/*.json",
"arn:aws:s3:::nextstrain-data/files/workflows/*",
"arn:aws:s3:::nextstrain-data/files/datasets/*",

"arn:aws:s3:::nextstrain-data-private/*.json",
"arn:aws:s3:::nextstrain-data-private/files/workflows/*",
"arn:aws:s3:::nextstrain-data-private/files/datasets/*",
# Auspice dataset JSONs
"arn:aws:s3:::nextstrain-data/$${sts:RoleSessionName}.json",
"arn:aws:s3:::nextstrain-data/$${sts:RoleSessionName}_*.json",
"arn:aws:s3:::nextstrain-staging/$${sts:RoleSessionName}.json",
"arn:aws:s3:::nextstrain-staging/$${sts:RoleSessionName}_*.json",
"arn:aws:s3:::nextstrain-staging/trial_*_$${sts:RoleSessionName}.json",
"arn:aws:s3:::nextstrain-staging/trial_*_$${sts:RoleSessionName}_*.json",

# Associated data files
# <https://docs.nextstrain.org/en/latest/reference/data-files.html>
"arn:aws:s3:::nextstrain-data/files/workflows/$${sts:RoleSessionName}/*",
"arn:aws:s3:::nextstrain-data/files/datasets/$${sts:RoleSessionName}/*",
"arn:aws:s3:::nextstrain-data-private/files/workflows/$${sts:RoleSessionName}/*",
"arn:aws:s3:::nextstrain-data-private/files/datasets/$${sts:RoleSessionName}/*",
"arn:aws:s3:::nextstrain-staging/files/workflows/$${sts:RoleSessionName}/*",
"arn:aws:s3:::nextstrain-staging/files/datasets/$${sts:RoleSessionName}/*",
],
},
{
"Sid": "NcovPrivateList",
"Effect": "Allow",
"Action": [
"s3:ListBucket",
"s3:ListBucketVersions",
"s3:GetBucketLocation",
"s3:GetBucketVersioning",
],
"Resource": [
"arn:aws:s3:::nextstrain-ncov-private",
],
"Condition": {
"StringEquals": {
"aws:PrincipalTag/repository": [
"nextstrain/ncov",
"nextstrain/ncov-ingest",
# XXX TODO: forecasts-ncov?
],
},
}
},
{
"Sid": "NcovPrivateReadWrite",
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:GetObjectTagging",
"s3:GetObjectVersion",
"s3:GetObjectVersionTagging",
"s3:PutObject",
"s3:PutObjectTagging",
"s3:DeleteObject",
# but NOT s3:DeleteObjectVersion so objects can't be completely wiped
],
"Resource": [
# This bucket is akin to nextstrain-data-private/files/{workflows,datasets}/ncov/.
"arn:aws:s3:::nextstrain-ncov-private/*",

"arn:aws:s3:::nextstrain-staging/*.json",
"arn:aws:s3:::nextstrain-staging/files/workflows/*",
"arn:aws:s3:::nextstrain-staging/files/datasets/*",
],
"Condition": {
"StringEquals": {
"aws:PrincipalTag/repository": [
"nextstrain/ncov",
"nextstrain/ncov-ingest",
# XXX TODO: forecasts-ncov?
]
},
},
},
]
})
Expand Down
19 changes: 0 additions & 19 deletions env/production/github-oidc.tf

This file was deleted.

0 comments on commit ffe1302

Please sign in to comment.