To update your staging or prod deployments, your CI/CD pipelines need access to your cloud environment. Unsurprisingly, they need credentials to present to authenticate to those environments. Traditionally, we store credentials to the CI/CD platform’s secret storage.
But there’s a problem. Sometimes more than one. Either the credentials are hard to manage or, worse, not all that secure. In this post, we’ll take a closer look at using OpenID Connect to to replace credentials instead of storing them in the CI/CD platform.
The problem with storing credentials to secret storage
The CI/CD pipeline configurations are usually stored in a repository along with the source code. However, since we do not want to store the credentials there, we usually use secret storage provided by the CI/CD providers. For example, GitHub has encrypted secrets that are accessible from GitHub Actions workflows.
Traditionally this is what people do when credentials are needed for their pipelines. But, there are problems:
- The credentials are not managed by Infrastructure as Code (IaC). We want to manage our infrastructure as a form of code, but because we do not want to store credentials in the repository, these values cannot be managed by code. They need to be manually configured.
- The credentials are non-expiring. While we make an effort not to reveal credentials in the CI/CD logs, it is possible that they are accidentally shown. These stored credentials are non-expiring, so once they are revealed, they can be used anywhere, at any time, until revoked.
- The credentials can be used in multiple locations. Downloadable credentials can be used for non-CI/CD usage. The credential itself doesn’t attest to where it’s used. When something weird happens, It’s hard to trace where the credentials have been used.
To solve these problems, the cloud providers and the CI/CD providers support OpenID Connect for the identity federation.
Identity Federation with OpenID Connect
OpenID Connect (OIDC) is a standard around authentication. Simply put, it defines two things:
- Format of the credentials
- Steps to verify those credentials
GitHub implements the first part, and it can issue credentials for GitHub Actions. GCP, AWS, and Azure implement the second part, and it can verify and accept the credentials minted by GitHub. The actual usage can look like this:
A user configures their GCP project to trust GitHub credentials issued for a repository “myorg/myrepo”, and gives necessary permissions to them. Then, they can start a GitHub actions workflow on that repository. GitHub mints a credential for that repository based on the OIDC spec and starts the workflow. The workflow can use that credential to call a GCP API. GCP can verify the given credential based on the OIDC spec. Since the user configures it to trust GitHub creds from “myorg/myrepo”, it permits the API call.
This authentication scheme solves the problems of the stored credentials.
- The OIDC trust configuration can be managed with Infrastructure as Code. The trust relationship itself doesn’t have secret values involved. We can manage this with IaC.
- The credentials are short-lived. The minted credentials expire very quickly. Even if it’s accidentally revealed, the blast radius is contained to that limit.
- The credentials are issued on demand. GitHub issues the credentials when a workflow is triggered. The same credential will never be reused outside of that workflow run.
The example used GitHub and GCP, but as long as both sides support OIDC, the authentication works in a similar fashion. The usage is not limited to CI/CD pipelines. Kubernetes clusters can issue OIDC-compatible credentials for Kubernetes service accounts, and it can be used for authenticating to cloud services.
Technical aspects of OpenID Connect
OIDC’s credentials are called ID tokens, and they are formatted as a JSON Web Token (https://jwt.io). JWT is a JSON document with a verifiable signature. The ID tokens are basically a verifiable JSON document, and OIDC defines what keys need to be in that JSON document and their meanings. You can add extra data as well.
The minter of ID tokens creates a public key pair and uses the private key to sign a JWT. The public key is exposed to the Internet so that anybody can verify the validity of the signed tokens. You can see GitHub Action’s public key at https://token.actions.githubusercontent.com/.well-known/jwks.
CI and cloud provider support on OIDC
Major CI and Cloud providers already support OIDC.
- CI providers: Minting OIDC ID tokens
- Cloud provider: Accepting OIDC ID tokens
Aviator: Automate your cumbersome processes
Aviator automates tedious developer workflows by managing git Pull Requests (PRs) and continuous integration test (CI) runs to help your team avoid broken builds, streamline cumbersome merge processes, manage cross-PR dependencies, and handle flaky tests while maintaining their security compliance.
There are 4 key components to Aviator:
- MergeQueue – an automated queue that manages the merging workflow for your GitHub repository to help protect important branches from broken builds. The Aviator bot uses GitHub Labels to identify Pull Requests (PRs) that are ready to be merged, validates CI checks, processes semantic conflicts, and merges the PRs automatically.
- ChangeSets – workflows to synchronize validating and merging multiple PRs within the same repository or multiple repositories. Useful when your team often sees groups of related PRs that need to be merged together, or otherwise treated as a single broader unit of change.
- TestDeck – a tool to automatically detect, take action on, and process results from flaky tests in your CI infrastructure.
- Stacked PRs CLI – a command line tool that helps developers manage cross-PR dependencies. This tool also automates syncing and merging of stacked PRs. Useful when your team wants to promote a culture of smaller, incremental PRs instead of large changes, or when your workflows involve keeping multiple, dependent PRs in sync.