Simplifying AWS Access in GitHub Actions with OIDC Provider
Managing AWS access keys for GitHub Actions can be a challenge, especially when ensuring security and ease of access. Traditionally, AWS IAM user access keys have been used to grant GitHub Actions the permissions needed to interact with AWS resources. However, there is a more secure and manageable way: using OpenID Connect (OIDC) identity providers to obtain temporary AWS credentials.
In this blog post, we'll explore how to set up AWS OIDC for Github to access AWS resources without needing long-term IAM user access keys.
Why Use OIDC with GitHub Actions?
Using OIDC with GitHub Actions offers several benefits:
- Enhanced Security: By eliminating the need for long-term AWS access keys, you reduce the risk of credential leakage.
- Ease of Management: Short-term credentials simplify credential rotation and management.
- Scoped Access: You can tightly scope the permissions granted to specific repositories, reducing the risk of over-privileged access.
Below are some common use cases where integrating GitHub Actions with AWS OIDC enhances security for various AWS operations:
- Access to AWS Self-Hosted Runners.
- Access to specific AWS resources (EC2, S3, RDS) securely
- For Infrastructure as Code (IaC) tasks with Terraform, GitHub Actions can assume a
role with the necessary permissions to provision and manage AWS resources.
- Secure access to S3 and DynamoDB for Terraform state file and state locking.
- Access to push docker container images to ECR securely.
- Access to interact with Kubernetes API for deploying applications to specific EKS
cluster.
Create an OIDC Provider in AWS
First, we need to configure AWS to trust GitHub's OIDC provider
- Navigate to the AWS IAM Console. Go to the "Identity providers" section and click "Add provider".
- Select "OpenID Connect".
- For the provider URL, enter https://token.actions.githubusercontent.com
- Add sts.amazonaws.com to the audience
Using Terraform to create OIDC Provider:
resource "aws_iam_openid_connect_provider" "github" {
url = "https://token.actions.githubusercontent.com"
client_id_list = ["sts.amazonaws.com"]
thumbprint_list = ["1c58a3a8518e8759bf075b76b750d4f2df264fcd"]
}
Create an IAM Role
Next, we will create an IAM role that GitHub Actions can assume. We will scope the Trust Relationship Policy by limiting it to a specific GitHub organization, repository, or branch that can assume the role associated with the IAM IdP.
Trust Relationships
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::123456789012:oidc-provider/token.actions.githubusercontent.com"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com",
"token.actions.githubusercontent.com:sub": "repo:sarubhai/test-aws-oidc:ref:refs/heads/main"
}
}
}
]
}
Github_OIDC_Basic_Permission
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:List*"
],
"Resource": "*"
}
]
}
Using Terraform to create IAM Role:
data "aws_iam_policy_document" "github_actions_trust_policy" {
statement {
effect = "Allow"
actions = ["sts:AssumeRoleWithWebIdentity"]
principals {
type = "Federated"
identifiers = [aws_iam_openid_connect_provider.github.arn]
}
condition {
test = "StringEquals"
variable = "token.actions.githubusercontent.com:aud"
values = ["sts.amazonaws.com"]
}
condition {
test = "StringLike"
variable = "token.actions.githubusercontent.com:sub"
values = ["repo:sarubhai/github-aws-oidc:*"]
}
}
}
resource "aws_iam_role" "github_actions_role" {
name = "github-actions-role"
assume_role_policy = data.aws_iam_policy_document.github_actions_trust_policy.json
managed_policy_arns = [aws_iam_policy.github_actions_policy.arn]
}
resource "aws_iam_policy" "github_actions_policy" {
name = "github-actions-policy"
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = ["s3:List*"]
Resource = "*"
},
]
})
}
Create a Github Action
Let us first add two Github Secrets namely AWS_REGION & AWS_ROLE_TO_ASSUME
.github/workflows/devops.yml
name: DevOps
on:
# workflow_dispatch:
push:
branches:
- main
permissions:
id-token: write
contents: read
jobs:
devops-automation:
runs-on: ubuntu-latest
steps:
- name: Checkout local repo
uses: actions/checkout@v3
- name: configure aws credentials
uses: aws-actions/configure-aws-credentials@v3
with:
role-to-assume: $\{\{ secrets.AWS_ROLE_TO_ASSUME }}
aws-region: $\{\{ secrets.AWS_REGION }}
disable-retry: "true"
- name: Get STS Caller Identity
run: aws sts get-caller-identity
- name: List S3 Buckets
run: aws s3 ls
One of the key benefits of using OIDC with GitHub Actions is the ability to manage access across multiple repositories efficiently. Each repository can assume a different IAM role tailored to its specific needs, enhancing security and reducing the risk of over-privileged access.
- For each GitHub repository, create a distinct IAM role.
- Customize the trust policy for each role to include the specific repository.
- Attach specific permissions to each role based on the requirements of the repository. For instance, one role might need access to S3 and DynamoDB, while another might require permissions for ECR and EKS.
- In each repository, add a secret named
AWS_ROLE_TO_ASSUME
containing the ARN of the IAM role assigned to that repository.
By following this approach, you ensure that each repository has the least privilege necessary to perform its tasks, enhancing the overall security of your AWS resources.
In this post, we've walked through creating an OIDC provider, setting up an IAM role with a scoped trust policy, and configuring a GitHub Actions workflow to assume the role and interact with AWS. With these steps, you can eliminate the need for long-term IAM user access keys, making your CI/CD pipelines more secure and manageable.