When integrating GitHub Actions Workflows with an AWS account, you'll need a way to authorize Actions to assume an IAM role. A straightforward way to authenticate is by creating an IAM user with an access key and then sharing the access key ID and secret as GitHub Actions secrets. These secrets can either be used with the Configure AWS Credentials GitHub action for role assumption or directly incorporated into an Action that accesses AWS through the CLI or SDK. When I started working in AWS, I often relied on access keys while using GitHub Actions for deployment because they were a convenient starting point. While this approach works, it could be more secure. This post will explore the problems with access keys and how you can improve your security posture using OpenID Connect (OIDC).
In 2023, AWS started warning users about the risks associated with long-term access keys when they attempt to create them in the AWS console. Along with the warnings, AWS provides a link to their IAM access key documentation page, which urges users to explore alternative methods whenever possible.
The risks of using long-lived credentials
Sharing AWS access keys as GitHub Actions secrets provides a basic level of security by allowing access to an AWS account without allowing public Internet exposure. GitHub secrets also allow collaboration in a repository while safeguarding sensitive keys.
However, access keys have their downsides:
Access keys are long-lived
AWS access keys remain active until manually deactivated or deleted. The longer keys remain active, the higher the risk of unintended exposure or misuse.
Secrecy is hard
Maintaining the secrecy of access keys is challenging. Unintentional secret disclosure in source control or other platforms is so prevalent multiple security tools exist to detect this problem, such as GitHub Advanced Security Code Scanning, GitLeaks, and GitGuardian. According to GitGuardian's 2022 Secrets Sprawl Report, git repositories leaked over six million secrets in 2022, a twofold increase from 2021. This difficulty in managing secrets is a major concern for security and compliance teams. Avoiding access keys and secrets entirely, whenever possible, is advisable.
Secret manager limitations
Secret management solutions like GitHub Secrets or Vault are far better than storing credentials in source control, but unfortunately, they're not perfect. Secrets can still be leaked as build output through CI/CD pipeline modifications.
Key rotation policies
Implementing and enforcing key rotation policies can make a secret less likely to become compromised. Key rotation policies are challenging to implement and enforce, especially in large organizations. Additionally, the process of rotating keys is both tedious and time-consuming.
Auditing long-lived AWS credentials also has limitations. First, tracking the usage of keys isn't trivial, especially if they've been shared with multiple accounts, services, or users. Additionally, because actors can reuse keys numerous times, it is much more challenging to differentiate between legitimate and compromised account access.
Improving security with OpenID Connect
You can significantly improve your security posture when integrating GitHub Actions with AWS accounts by leveraging OIDC federation. Federation establishes a secure trust relationship between GitHub and AWS, eliminating the need to share secrets between the two systems.
At a high level, here is how this works:
- Establish Trust: First, you must establish an OIDC trust between AWS IAM and GitHub's OIDC provider (more on how to set this up later).
- Workflow Token Generation: GitHub's OIDC provider generates a secure token each time a GitHub workflow is run. Each generated JWT token is unique to the workflow run. This token contains claims about the workflow's identity, such as its associated organization, repository, and branch.
- AWS IAM Token Validation: A GitHub Action sends a request to AWS IAM to assume a particular IAM role along with the token issued from GitHub's OIDC provider. IAM then validates the token issued by GitHub's OIDC provider. If the GitHub token is authentic, IAM will evaluate the token's claims and determine if the Action is permitted to assume the requested IAM role. AWS will give the Action a short-lived AWS token if authorized to assume the requested role. The Action can then use this AWS-issued token to assume the role and perform actions scoped to the permissions granted in the IAM role.
How to implement
The Configure AWS Credentials GitHub Action is a popular Action that easily configures AWS credentials as environmental variables for use by other GitHub Actions in a workflow. Since this is so easy to use, I'll assume most people integrating GitHub Actions with AWS are using this Action. If you're not using this Action and configuring credentials manually, most of the steps outlined here still apply. If you haven't looked at the documentation for this Action recently, you'll notice that its documentation strongly encourages using OIDC.
For each AWS account you wish to leverage OIDC federation, there's a one-time setup required that sets up a federation between GitHub's OIDC provider and an IAM identity provider. You could do this manually, but fortunately, AWS provides a CloudFormation Template to configure this for you here. You'll need to supply this template with the name of the GitHub organization or user (if not using GitHub Enterprise) you wish to establish trust with and the repository you want to trust. The other parameters are optional. However, if you use a non-standard AWS partition, such as AWS GovCloud (as I do at work), you must change the OIDC Audience (e.g., sts.us-gov-west-1.amazonaws.com for GovCloud). You only need to configure GitHub as a federated OIDC provider once per AWS account.
Once this Cloud Formation template completes, it will create a new role (e.g., github-oidc-federation-example-Role-CT1ElR4DjK6y), allowing a GitHub workflow to assume a role in the AWS account without sharing any secrets between AWS and GitHub. This Cloud Formation template will also setup federation between GitHub's OIDC provider and AWS IAM.
The screenshot above demonstrates that establishing a trust relationship is manageable without needing a CloudFormation template. The example github-oidc-federation-example-Role-CT1ElR4DjK6y role created by this template allows any GitHub workflow running on any branch within the GitHub repository named cebert/github-oidc-connect-example to assume this role with web identity.
You must carefully ensure that conditions are set to limit which GitHub workflows can assume a role. If you establish a trust relationship with GitHub without any conditions, it could be possible for any workflow in GitHub to assume the role, which is a huge security risk. If you're careful when establishing this trust relationship, OIDC is far more secure than sharing long-lived credentials.
GitHub outlines all claims included in its OIDC issued tokens in its well-known OIDC configuration. These claims can be used as conditions to restrict access, such as only allowing a particular workflow running in a specific organization and branch to have the ability to assume a role. The more restrictive you can be here, the better.
Assuming a role from GitHub
Once you have created an IAM role with a trust relationship with GitHub's OIDC, the role can be assumed by an Action running in a workflow. If we wanted the Configure AWS Credentials GitHub Action to assume the github-oidc-federation-example-Role-CT1ElR4DjK6y role that was created in the example above, we could do so in a few lines of code:
- name: Configure AWS Credentials for Example
Additionally, you'll need to grant your GitHub Actions workflow job
id-token permissions so that it can interact with GitHub's OIDC sts endpoint:
name: Assume AWS role example
# We need to access GH's OIDC provider endpoint
- name: Checkout
- name: Configure AWS Credentials for Example
## Add steps that need AWS credentials here
Because of the trust relationship we established for this role between an AWS account and GitHub's OIDC provider, only GitHub Action workflows running within the repository
cebert/github-oidc-connect-example will be allowed to assume this role. We could further restrict which workflows can assume this role by adding more conditions to our IAM role's trust relationship configuration.
Note: It's always recommended to provide both
role-duration-seconds parameter values when assuming an IAM role with Configure AWS Credentials, even though these parameters are not strictly required. Specifying a session name helps with auditing events in AWS CloudTrail. Setting the lowest possible value for
role-duration-seconds helps ensure the AWS token use to assume the IAM role is as short-lived as possible.
This post covered the inherent risks of using long-lived secrets to grant GitHub Actions workflows access to AWS accounts. By embracing OIDC federation, we improve our security posture while simplifying the integration between GitHub Actions and AWS. The steps I've outlined for setting up federation and role assumption in GitHub should provide a clear, actionable guide on adopting this authorization method.
If you're still sharing long-lived credentials with GitHub Actions, consider transitioning from access keys to OIDC next time you rotate your credentials. Configuring OIDC trust won't take much longer than rotating credentials, with the added benefit of improved security and not needing to rotate credentials again.