AWS OIDC Warden: Validates GitHub Actions JWT tokens for secure, fine-grained AWS access control.
4.5K
Warning
This project is still under active development and may not be production-ready. Features and functionality are subject to change. Use with caution and check back for updates.

The AWS OIDC Warden is a secure, lightweight Go service that validates OpenID Connect (OIDC) tokens issued by GitHub Actions and other OIDC providers. It acts as a trusted intermediary between GitHub workflows and AWS resources, providing fine-grained access control based on repository, branch, actor, and other configurable constraints.
This service solves a critical security challenge: securely connecting CI/CD workflows to AWS resources without storing long-lived credentials. By validating OIDC tokens and applying customizable security policies, it enables organizations to implement the principle of least privilege while maintaining operational efficiency at scale.
Key Capabilities:
Caution
Not all OIDC claims can be trusted. See the great tool and table created [PaloAltoNetworks/GitHub OIDC Utils](https://github.com/PaloAltoNetworks/github-oidc-utils) for a comprehensive list of claims.This lambda allows you to include specific constraints for a repository before it can obtain credentials from a role. Choose wisely based on the table that Palo Alto Networks has provided in the repository linked above.
AWS OIDC Warden can be configured using environment variables, a YAML/JSON/TOML configuration file. For a complete reference of all configuration options, see the Configuration Documentation.
All configuration options can be set using environment variables with the AOW_ prefix:
# Core configuration
export AOW_ISSUER=https://token.actions.githubusercontent.com
export AOW_AUDIENCE=sts.amazonaws.com
export AOW_ROLE_SESSION_NAME=aws-oidc-warden
# Cache configuration
export AOW_CACHE_TYPE=dynamodb
export AOW_CACHE_TTL=1h
export AOW_CACHE_MAX_LOCAL_SIZE=20
export AOW_CACHE_DYNAMODB_TABLE=aws-oidc-warden-cache
# Logging configuration
export AOW_LOG_TO_S3=true
export AOW_LOG_BUCKET=your-log-bucket
export AOW_LOG_PREFIX=logs/
export LOG_LEVEL=debug # Note: This one doesn't use the AOW_ prefix
Alternatively, you can use a YAML, JSON or TOML configuration file:
issuer: https://token.actions.githubusercontent.com
audience: sts.amazonaws.com
role_session_name: aws-oidc-warden
cache:
type: dynamodb
ttl: 1h
max_local_size: 20
dynamodb_table: jwks-cache
log_to_s3: true
log_bucket: your-log-bucket
log_prefix: logs/
The most powerful feature of this tool is the ability to map GitHub repositories to AWS IAM roles with specific constraints. Here's an example configuration:
repo_role_mappings:
# Simple mapping: Only allow access from the main branch
- repo: "org/main-branch-repo" # Repository pattern (supports regex)
roles:
- arn:aws:iam::123456789012:role/github-actions-role
constraints:
branch: "refs/heads/main" # Only allow from main branch
# Apply a session policy from S3
- repo: "org/policy-from-s3" # Exact repository name
session_policy_file: "policies/restrict-to-dev.json"
roles:
- arn:aws:iam::123456789012:role/github-actions-role
constraints:
branch: "refs/heads/dev.*" # Regex pattern for dev branches
# Match multiple repositories with a pattern
- repo: "org/project-.*" # Match all repositories starting with project-
session_policy: >
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Deny",
"Action": ["iam:*"],
"Resource": "*"
}
]
}
roles:
- arn:aws:iam::123456789012:role/github-actions-role
You can apply multiple constraints to control when a role can be assumed. All constraints configured must be satisfied for access to be granted:
repo_role_mappings:
- repo: "org/multi-constraint-repo" # Repository pattern (supports regex)
roles:
- arn:aws:iam::123456789012:role/github-actions-role
constraints:
# Git reference constraints
branch: "refs/heads/main" # Branch pattern (supports regex)
ref: "refs/tags/v.*" # Reference pattern (supports regex)
ref_type: "tag" # Reference type (branch, tag)
# GitHub workflow constraints
event_name: "push" # GitHub event that triggered the workflow
workflow_ref: "deploy\\.ya?ml$" # Workflow file name pattern (supports regex)
environment: "production" # GitHub environment
# Actor constraints
actor_matches: # GitHub actors allowed to assume the role (supports regex)
- "admin-user"
- "authorized-.*"
To use the service, send a POST request with the GitHub OIDC token and the desired AWS role ARN:
{
"token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"role": "arn:aws:iam::123456789012:role/github-actions-role"
}
Here's an example of how to use this service in a GitHub Actions workflow:
name: AWS Deployment
jobs:
deploy:
runs-on: ubuntu-latest
permissions:
id-token: write # Required for OIDC token
contents: read # Required to check out repository
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Get AWS credentials via custom OIDC validator
id: aws-creds
run: |
# Get the OIDC token
TOKEN=$(curl -H "Authorization: bearer $ACTIONS_ID_TOKEN" \
-H "Accept: application/json; api-version=2.0" \
-H "Content-Type: application/json" \
-H "User-Agent: actions/oidc-client" \
"$ACTIONS_ID_TOKEN_REQUEST_URL" | jq -r '.value')
# Call the validator API to get AWS credentials
RESPONSE=$(curl -s -X POST \
-H "Content-Type: application/json" \
-d "{\"token\": \"$TOKEN\", \"role\": \"arn:aws:iam::123456789012:role/github-actions-role\"}" \
"https://your-api-gateway-url.execute-api.region.amazonaws.com/prod/verify")
# Parse and set AWS credentials
echo "::add-mask::$(echo $RESPONSE | jq -r '.data.AccessKeyId')"
echo "::add-mask::$(echo $RESPONSE | jq -r '.data.SecretAccessKey')"
echo "::add-mask::$(echo $RESPONSE | jq -r '.data.SessionToken')"
echo "AWS_ACCESS_KEY_ID=$(echo $RESPONSE | jq -r '.data.AccessKeyId')" >> $GITHUB_ENV
echo "AWS_SECRET_ACCESS_KEY=$(echo $RESPONSE | jq -r '.data.SecretAccessKey')" >> $GITHUB_ENV
echo "AWS_SESSION_TOKEN=$(echo $RESPONSE | jq -r '.data.SessionToken')" >> $GITHUB_ENV
env:
ACTIONS_ID_TOKEN_REQUEST_URL: ${{ vars.ACTIONS_ID_TOKEN_REQUEST_URL }}
ACTIONS_ID_TOKEN: ${{ vars.ACTIONS_ID_TOKEN }}
- name: Deploy with AWS credentials
run: |
# Now you can use AWS CLI with the obtained credentials
aws s3 ls
The project provides multiple deployment options depending on your AWS infrastructure:
1. API Gateway + Lambda (Recommended for Production)
API Gateway provides a public API endpoint that can be secured with API keys, restricting access to authorized repositories only. This approach minimizes endpoint exposure, enhancing security for safer usage.
# Build the API Gateway handler
GOOS=linux GOARCH=arm64 go build -o bootstrap cmd/apigateway/main.go
zip lambda-apigateway.zip bootstrap
# Create Lambda function
aws lambda create-function \
--function-name aws-oidc-warden-apigateway \
--runtime provided.al2023 \
--role arn:aws:iam::ACCOUNT:role/lambda-execution-role \
--handler bootstrap \
--zip-file fileb://lambda-apigateway.zip
2. Lambda URLs (Recommended for Simple Setups)
# Build the Lambda URL handler
GOOS=linux GOARCH=arm64 go build -o bootstrap cmd/lambdaurl/main.go
zip lambda-url.zip bootstrap
# Create Lambda function with Function URL
aws lambda create-function \
--function-name aws-oidc-warden-url \
--runtime provided.al2023 \
--role arn:aws:iam::ACCOUNT:role/lambda-execution-role \
--handler bootstrap \
--zip-file fileb://lambda-url.zip
# Create Function URL
aws lambda create-function-url-config \
--function-name aws-oidc-warden-url \
--auth-type NONE \
--cors '{"AllowOrigins":["*"],"AllowMethods":["POST"]}'
3. Application Load Balancer (High Traffic)
# Build the ALB handler
GOOS=linux GOARCH=arm64 go build -o bootstrap cmd/alb/main.go
zip lambda-alb.zip bootstrap
# Create Lambda function for ALB target group
aws lambda create-function \
--function-name aws-oidc-warden-alb \
--runtime provided.al2023 \
--role arn:aws:iam::ACCOUNT:role/lambda-execution-role \
--handler bootstrap \
--zip-file fileb://lambda-alb.zip
4. Container-Based Deployment (Recommended Lambda Deployment)
# Using pre-built container images
aws lambda create-function \
--function-name aws-oidc-warden \
--package-type Image \
--code ImageUri=ghcr.io/boogy/aws-oidc-warden:latest \
--role arn:aws:iam::ACCOUNT:role/lambda-execution-role
Recommended for Production: Instead of building your own images, you can use the pre-built container images from GitHub Container Registry (GHCR). These images are automatically built and published for each release.
Container Registries:
docker pull ghcr.io/boogy/aws-oidc-warden:latestdocker pull boogy/aws-oidc-warden:latestAvailable tags:
latest - Latest stable releasev1.x.x - Specific version tagslinux/amd64 and linux/arm64# Create Lambda function with pre-built container image
aws lambda create-function \
--function-name aws-oidc-warden \
--package-type Image \
--code ImageUri=ghcr.io/boogy/aws-oidc-warden:latest \
--role arn:aws:iam::123456789012:role/lambda-execution-role
# Update existing Lambda function
aws lambda update-function-code \
--function-name aws-oidc-warden \
--image-uri ghcr.io/boogy/aws-oidc-warden:v1.0.0
To avoid GitHub Container Registry rate limits and improve performance for production deployments, set up an AWS ECR pull-through cache:
Create ECR pull-through cache rule:
aws ecr create-pull-through-cache-rule \
--ecr-repository-prefix ghcr.io \
--upstream-registry-url ghcr.io \
--region us-east-1
Use the pull-through cache URI in Lambda:
# Create Lambda function using ECR pull-through cache
aws lambda create-function \
--function-name aws-oidc-warden \
--package-type Image \
--code ImageUri=123456789012.dkr.ecr.us-east-1.amazonaws.com/ghcr.io/boogy/aws-oidc-warden:latest \
--role arn:aws:iam::123456789012:role/lambda-execution-role
# Update existing Lambda function
aws lambda update-function-code \
--function-name aws-oidc-warden \
--image-uri 123456789012.dkr.ecr.us-east-1.amazonaws.com/ghcr.io/boogy/aws-oidc-warden:v1.0.0
Configure permissions for pull-through cache:
Your Lambda execution role needs these additional permissions:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ecr:BatchGetImage",
"ecr:GetDownloadUrlForLayer",
"ecr:GetAuthorizationToken"
],
"Resource": "*"
}
]
}
Benefits of ECR Pull-Through Cache:
Container Image URI Format:
ghcr.io/boogy/aws-oidc-warden:TAGACCOUNT_ID.dkr.ecr.REGION.amazonaws.com/ghcr.io/boogy/aws-oidc-warden:TAGWhen a token is successfully validated and the AWS role is assumed, the service returns temporary AWS credentials:
{
"success": true,
"statusCode": 200,
"requestId": "12258876-a981-452b-a7ae-415f8fa737b6",
"processingMs": 254,
"message": "Token validation successful and role assumed",
"data": {
"AccessKeyId": "ASIA1234567890EXAMPLE",
"SecretAccessKey": "wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY",
"SessionToken": "FwoGZXIvYXdzEPH//////////wEaDKLZ3MQOJZBKxR1JDiLBARJhUlx1g09xLW+oIYHDt15IZY4...",
"Expiration": "2023-09-29T20:31:14Z"
}
}
When validation fails or the role cannot be assumed, the service returns an error response:
{
"success": false,
"statusCode": 403,
"requestId": "12258876-a981-452b-a7ae-415f8fa737b6",
"processingMs": 383,
"message": "Permission denied for the requested operation",
"errorCode": "permission_denied",
"errorDetails": "role not allowed for repository or doesn't meet constraints"
}
The application includes a simple caching system for JWKS (JSON Web Key Sets) to improve performance and reduce load on GitHub's OIDC servers and mostly avoid hitting GitHub API rate limits:
cache:
type: "memory"
ttl: "1h"
max_local_size: 20
cache:
type: "dynamodb"
ttl: "4h"
dynamodb_table: "aws-oidc-warden-cache"
cache:
type: "s3"
ttl: "24h"
s3_bucket: "aws-oidc-warden-cache"
s3_prefix: "jwks-cache"
repo field rather than overly broad patterns like .*The service automatically applies tags to the AWS sessions it creates. These tags include:
repo: The repository name (without owner)actor: The GitHub user or system that triggered the workflowref: The Git reference that triggered the workflowevent-name: The event that triggered the workflowrepo-owner: The owner of the repositoryref-type: The type of Git reference (branch, tag)These tags can be used for auditing, cost allocation, and for creating condition-based IAM policies.
The service follows these steps to validate tokens and assume roles:
This project is licensed under the Apache License 2.0.
Content type
Image
Digest
sha256:34610ea2f…
Size
5.8 MB
Last updated
12 days ago
docker pull boogy/aws-oidc-warden:alb-latest