Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Roles for our pathogen-repo-build GitHub Actions workflow, take 3 #8

Merged
merged 3 commits into from
May 21, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ Make those changes so:
> You'll need ambiently-configured AWS credentials with broad admin-level
> access to read (and optionally modify) resources in our account.
>
> You'll also need a `GITHUB_TOKEN` in the environment with the `actions:write`
> fine-grained token permission on our repos.
>
> Please step cautiously and be careful when using them!


Expand Down
24 changes: 24 additions & 0 deletions env/production/.terraform.lock.hcl

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

75 changes: 75 additions & 0 deletions env/production/[email protected]
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# Per-pathogen policy, granting access to a single pathogen's data
resource "aws_iam_policy" "NextstrainPathogen" {
for_each = local.pathogen_repos

name = "NextstrainPathogen@${each.key}"
description = "Provides permissions to upload datasets, workflow files, etc. for a Nextstrain pathogen"

policy = jsonencode({
"Version": "2012-10-17",
"Statement": [
# Technically we don't need to include the public buckets
# nextstrain-data and nextstrain-staging in this statement since they
# already allow a superset of this with their bucket policies, but it's
# good to be explicit about what permissions we require.
# -trs, 16 Feb 2024
{
"Sid": "List",
"Effect": "Allow",
"Action": [
"s3:ListBucket",
"s3:ListBucketVersions",
"s3:GetBucketLocation",
"s3:GetBucketVersioning",
],
"Resource": [
"arn:aws:s3:::nextstrain-data",
"arn:aws:s3:::nextstrain-data-private",
"arn:aws:s3:::nextstrain-staging",
],
"Condition": {
"StringLike": {
"s3:prefix": [
"${each.key}.json",
"${each.key}_*.json",
"files/workflows/${each.key}/*",
"files/datasets/${each.key}/*",
]
}
}
},
{
"Sid": "ReadWrite",
"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": [
# Auspice dataset JSONs
"arn:aws:s3:::nextstrain-data/${each.key}.json",
"arn:aws:s3:::nextstrain-data/${each.key}_*.json",
"arn:aws:s3:::nextstrain-staging/${each.key}.json",
"arn:aws:s3:::nextstrain-staging/${each.key}_*.json",
"arn:aws:s3:::nextstrain-staging/trial_*_${each.key}.json",
"arn:aws:s3:::nextstrain-staging/trial_*_${each.key}_*.json",

# Associated data files
# <https://docs.nextstrain.org/en/latest/reference/data-files.html>
"arn:aws:s3:::nextstrain-data/files/workflows/${each.key}/*",
"arn:aws:s3:::nextstrain-data/files/datasets/${each.key}/*",
"arn:aws:s3:::nextstrain-data-private/files/workflows/${each.key}/*",
"arn:aws:s3:::nextstrain-data-private/files/datasets/${each.key}/*",
"arn:aws:s3:::nextstrain-staging/files/workflows/${each.key}/*",
"arn:aws:s3:::nextstrain-staging/files/datasets/${each.key}/*",
],
},
]
})
}
43 changes: 43 additions & 0 deletions env/production/aws-iam-policy-NextstrainPathogenNcovPrivate.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Single-pathogen policy, special-case for the historical reason that
# nextstrain-ncov-private predates the more general nextstrain-data-private.
resource "aws_iam_policy" "NextstrainPathogenNcovPrivate" {
name = "NextstrainPathogen@ncov+private"
description = "Provides permissions to upload datasets, workflow files, etc. to the ncov-private bucket for the Nextstrain ncov pathogen"

policy = jsonencode({
"Version": "2012-10-17",
"Statement": [
{
"Sid": "NcovPrivateList",
"Effect": "Allow",
"Action": [
"s3:ListBucket",
"s3:ListBucketVersions",
"s3:GetBucketLocation",
"s3:GetBucketVersioning",
],
"Resource": [
"arn:aws:s3:::nextstrain-ncov-private",
],
},
{
"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/*",
],
},
]
})
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ import {
id = "GitHubActionsRoleNextstrainBatchJobs"
}

# Multi-repo role, granting access to Batch
resource "aws_iam_role" "GitHubActionsRoleNextstrainBatchJobs" {
name = "GitHubActionsRoleNextstrainBatchJobs"
description = "Provides permissions to run jobs on AWS Batch via the Nextstrain CLI to select GitHub Actions OIDC workflows."
description = "Provides permissions to launch and monitor jobs on AWS Batch via the Nextstrain CLI to select GitHub Actions OIDC workflows."

max_session_duration = 43200 # seconds (12 hours)

Expand All @@ -21,7 +22,10 @@ resource "aws_iam_role" "GitHubActionsRoleNextstrainBatchJobs" {
"Condition": {
"StringLike": {
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com",
"token.actions.githubusercontent.com:sub": "repo:nextstrain/.github:*"
"token.actions.githubusercontent.com:sub": [
for repo in keys(local.repo_pathogens):
"repo:nextstrain/${repo}:*:job_workflow_ref:nextstrain/.github/.github/workflows/pathogen-repo-build.yaml@*"
]
}
},
}
Expand Down
43 changes: 43 additions & 0 deletions env/production/[email protected]
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Per-repo role, granting access to pathogens
resource "aws_iam_role" "GitHubActionsRoleNextstrainRepo" {
for_each = local.repo_pathogens

name = "GitHubActionsRoleNextstrainRepo@${each.key}"
description = "Provides permissions to upload datasets, workflow files, etc. for a Nextstrain pathogen to select repos and select GitHub Actions OIDC workflows."

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/${each.key}:*:job_workflow_ref:nextstrain/.github/.github/workflows/pathogen-repo-build.yaml@*"
}
},
}
]
})

managed_policy_arns = flatten([
# Pathogen-specific permissions to standard public/private buckets
[for p in each.value: aws_iam_policy.NextstrainPathogen[p].arn],

# Special-case permissions to nextstrain-ncov-private bucket
contains(each.value, "ncov")
? [aws_iam_policy.NextstrainPathogenNcovPrivate.arn]
: [],

# Builds inside the AWS Batch runtime need access to the jobs bucket.
aws_iam_policy.NextstrainJobsAccessToBucket.arn,
])

inline_policy {}
}
15 changes: 15 additions & 0 deletions env/production/github-oidc.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
resource "github_actions_repository_oidc_subject_claim_customization_template" "nextstrain" {
for_each = toset(keys(local.repo_pathogens))
repository = each.key

# <https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect>
use_default = false
include_claim_keys = [
# The GitHub default…
"repo",
"context",

# …plus the <org>/<repo>/<path>@<ref> of the workflow obtaining the token, if any.
"job_workflow_ref",
]
}
32 changes: 32 additions & 0 deletions env/production/locals.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
locals {
# By design our repo names are usually equal to the pathogen names, but
# they're two separate things/namespaces and linkages don't always line up
# 1:1. Some resources (roles, policies, etc) are more naturally oriented
# per-pathogen (logical), some per-repo (physical). Use two maps to support
# this more easily. This will likely evolve in the future to better support
# our needs.
# -trs, 20 May 2024

pathogen_repos = tomap({
# pathogen name = [repo name, …]
"dengue" = ["dengue"],
"forecasts-ncov" = ["forecasts-ncov"],
"measles" = ["measles"],
"mpox" = ["mpox"],
"ncov" = ["ncov", "ncov-ingest"],
"rsv" = ["rsv"],
"seasonal-flu" = ["seasonal-flu"],
"zika" = ["zika"],
})
Comment on lines +10 to +20
Copy link
Contributor

Choose a reason for hiding this comment

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

So for any future pathogens, we would add them to this map then deploy the terraform changes with terraform apply.

Should we auto-deploy the changes on merge into the main branch?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yep, add to that map then terraform apply.

We could definitely auto-deploy… but that does require having very broad admin level access to AWS available in GitHub Actions, which makes me a little nervous. I'd say stick with manual deploys for now.

Copy link
Member Author

Choose a reason for hiding this comment

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

When we do want to auto-deploy, the best way to gain that admin-level access would be via OIDC again, scoped down tightly to this repo (and ditto for auto-deploying nextstrain.org's Terraform config).


repo_pathogens = merge(
# repo name = [pathogen name, …]
transpose(local.pathogen_repos),

tomap({
# For testing. Ensures a role exists but without any pathogen-specific
# permissions.
".github" = [],
}),
)
}
9 changes: 9 additions & 0 deletions env/production/terraform.tf
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ terraform {
source = "registry.terraform.io/hashicorp/aws"
version = "~> 4.32"
}
github = {
source = "integrations/github"
version = "~> 6.0"
}
}

backend "s3" {
Expand All @@ -25,3 +29,8 @@ terraform {
provider "aws" {
region = "us-east-1"
}

provider "github" {
# Authn is via GITHUB_TOKEN
owner = "nextstrain"
}