Skip to content

[env-kvstore]: tool to retrieve CI/CD secrets and vars from AWS Secrets#460

Open
bpdohall wants to merge 25 commits intomainfrom
brendan/env-kvstore-initial-commit
Open

[env-kvstore]: tool to retrieve CI/CD secrets and vars from AWS Secrets#460
bpdohall wants to merge 25 commits intomainfrom
brendan/env-kvstore-initial-commit

Conversation

@bpdohall
Copy link
Contributor

@bpdohall bpdohall commented Mar 3, 2026

Initial PR to add a shared workflow enabling replacement of GitHub secrets and variables with a key-value store backed by AWS Secrets Manager.

The first commit here adds the token exchange behaviour which allows a GitHub workflow to obtain an AWS role session having session tags that match the OIDC claims from a GitHub token.

RFD reference: https://github.com/gravitational/cloud/blob/master/rfd/0193-Replace-GitHub-Secrets.md

Example run output: https://github.com/gravitational/bpdohall-platformops-gha/actions/runs/22505998282/job/65204598612#step:6:135

@bpdohall bpdohall requested review from a team as code owners March 3, 2026 13:54
// accepts an optional map pointer to return specific claims by key.
func parseClaims(label, token string, returnClaims *map[string]any) error {
claims := jwt.MapClaims{}
// Signature will be verified by Cognito or STS, we can skip verification
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you elaborate? Why is it safe to trust the claims in the JWT without first verifying the signature?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The only GHA token claim values we use are as follows:

  1. runID and sha are concatenated to create a unique session name used during role assumption. AWS IAM policies require these match role session tags.
  2. enterprise, repository and environment are used to generate names of Secrets Manager secrets that will be retrieved. Access to retrieve the secrets is gated by an IAM Role policy that requires a tagged session.

In both cases, a role session with tags is a prerequisite for accessing anything in AWS. Role assumption requires a Cognito token from a specific Identity Pool. If the Cognito Identity pool is presented with a forged GitHub token it will fail to produce a Cognito token so no tagged role session can be obtained.

Fundamentally, this GitHub action implements a compliant client, rather than "enforce" any rules.

The TokenExchanger conforms with the rule "SessionName must be runID@sha". Enforcement is through the IAM role trust policy.

{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "",
"Effect": "Allow",
"Principal": {
"Federated": "cognito-identity.amazonaws.com"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"aws:RequestTag/enterprise": "${GITHUB_ORG}$",
"cognito-identity.amazonaws.com:aud": "us-west-2:12345678-1234-1234-1234-1234567890ab",
"sts:RoleSessionName": "${aws:RequestTag/run_id}@${aws:RequestTag/sha}"
},
"StringNotEquals": {
"aws:RequestTag/event_name": "pull_request"
}
}
},
{
"Sid": "",
"Effect": "Allow",
"Principal": {
"Federated": "cognito-identity.amazonaws.com"
},
"Action": "sts:TagSession",
"Condition": {
"StringEquals": {
"cognito-identity.amazonaws.com:aud": "us-west-2:12345678-1234-1234-1234-1234567890ab"
}
}
}
]
}

The KVStore getter will conform with the rule on how Secrets Manager secrets are named for each repository. Enforcement relies on the IAM Role Policy (ABAC).

{
"Statement": [
{
"Sid": "AllowReadSecretsBasedOnSessionTags"
"Action": "secretsmanager:GetSecretValue",
"Effect": "Allow",
"Resource": [
"arn:aws:secretsmanager:us-west-2:278576220453:secret:${aws:PrincipalTag/enterprise}/repo/${aws:PrincipalTag/repository}/secrets",
"arn:aws:secretsmanager:us-west-2:278576220453:secret:${aws:PrincipalTag/enterprise}/repo/${aws:PrincipalTag/repository}/variables",
"arn:aws:secretsmanager:us-west-2:278576220453:secret:${aws:PrincipalTag/enterprise}/repo/${aws:PrincipalTag/repository}/env/${aws:PrincipalTag/environment}/*",
],
}
],
}

Separately, the Cognito Identity Pool enforces the rule that the token needs to be from our GitHub organization because it relies on the Identity Provider.

resource "aws_cognito_identity_pool_provider_principal_tag" "gha" {
identity_provider_name = "arn:aws:iam::${AWS_ACCOUNT_ID}$:oidc-provider/token.actions.githubusercontent.com/${GITHUB_ORG}"
identity_pool_id = aws_cognito_identity_pool.gha.id
use_defaults = false
principal_tags = {
"repository" = "repository"
"enterprise" = "enterprise"
"environment" = "environment"
"event_name" = "event_name"
"run_id" = "run_id"
"actor" = "actor"
"sha" = "sha"
"workflow" = "workflow"
}
}
```

bpdohall and others added 12 commits March 3, 2026 18:53
Co-authored-by: Zac Bergquist <zac.bergquist@goteleport.com>
Co-authored-by: Zac Bergquist <zac.bergquist@goteleport.com>
Co-authored-by: Zac Bergquist <zac.bergquist@goteleport.com>
Co-authored-by: Taras <9948629+taraspos@users.noreply.github.com>
infer account, region from role, idPool
@bpdohall bpdohall requested review from fheinecke, taraspos and zmb3 March 6, 2026 19:40
Copy link
Contributor

@taraspos taraspos left a comment

Choose a reason for hiding this comment

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

Approved left bunch of small nits and suggestions.

@bpdohall bpdohall requested a review from logand22 March 10, 2026 15:19
@bpdohall bpdohall force-pushed the brendan/env-kvstore-initial-commit branch from 383e12a to 1467298 Compare March 10, 2026 15:43
Copy link

@logand22 logand22 left a comment

Choose a reason for hiding this comment

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

Don't want to continue to delay the release of this over command line stuff. So here's an approval. It's just that the current usage of cobra doesn't really add much or follow the pattern.

In my earlier feedback I didn't realize you were only creating a single command. In which I case I don't think you need to introduce additional dependencies at all.

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.

5 participants