Home > Unlocking Cloud Build Security with OIDC

2022 Jul

This talk was given at fwd:cloudsec 2022.

Title Slide

In the past year, cloud build systems have added OIDC as a way to securely connect from your build to other cloud providers or even on-prem infrastructure. This is quite different from an end-user OAuth2 flow, so we'll cover what it looks like, common security pitfalls, and how to avoid them. We'll also show how the open source sigstore project can use OIDC to attest to the build process and sign your builds without managing a durable private key.

Why Cloud?

Engineering is all about tradeoffs. When starting out, it's often easier to build on your personal device for quick iteration. But when distributing software, you want to ensure the build is secure, which is easier in an ephemeral, isolated, and reproducible environment—often best achieved in the cloud.

The cloud is great, but what if your build needs to contact other services, like a cloud-hosted secrets manager, cloud infrastructure, or an internal notification system? How can your cloud build securely communicate with these services?

Humans vs Robots

Let's look at how people authenticate for inspiration, and see if our automated cloud build process can do something similar. The most common way people authenticate is with passwords, and automated systems often use API keys. Both have a similar weakness: if an attacker gets access to your secret, they can impersonate you, and you might not even know you've been compromised.

Some API keys support IP restrictions or audit logs, but these are imperfect. Best practice is to rotate API keys periodically, but this is often skipped.

Instead, let's see if we can model authentication for our automated system off the SSO flow that humans use. This would eliminate the need to maintain durable API keys.

Human OAuth2

Reviewing the familiar OAuth2 process helps establish terminology. On the left, a laptop wants to access a service (TokTagram for dogs). Instead of implementing their own authentication, the service relies on a third-party Identity Provider (like MegaCorp).

Human OAuth2 Flow

The flow is complex, but common. Notably, in step 4, the Identity Provider returns a signed JWT attesting to the user's identity. The user passes this to the Service Provider, who validates it and establishes the user's identity.

Build OIDC

For automated CI/CD systems, the flow is similar but reversed. The build system acts as the Identity Provider and wants to use its identity to access a cloud service (the Service Provider).

Some setup is required: create a service role in your Service Provider for your CI/CD system, and set up a trust relationship that allows access to session credentials if certain criteria are met.

Build OIDC Flow

The flow starts with the build system (scheduled or user-triggered). The CI/CD signs a JWT with build information and sends it to the Service Provider. The Service Provider validates it and, if successful, returns session credentials for the CI/CD system to complete the build.

JWT Details

The JWT contains a lot of information. The header describes the signing algorithm and public key. The body includes fields such as:

- aud: audience string provided during the build

- sha: commit hash

- repository: organization and repository path

- repository_id: numeric identifier for the repository

- workflow: specific workflow file

Security Considerations

If you're using a CI/CD system with other users, don't just trust any token—ensure it's from the specific resources you intend. You can trust an organization, specific repositories, or vetted workflows.

Organizations or repositories might change names. The numeric identifier helps ensure you're referencing the intended resource, even if names change.

When setting up trust with your cloud provider, check the audience for a unique string. This prevents JWTs from one provider being used to access others if compromised.

If using the JWT with a cloud provider, you won't need to implement JWT validation yourself. If you do, be aware of common bugs like algorithm confusion (especially the "none" algorithm).

OIDC Gateway Example

For advanced use cases, your build might need to contact a service on your private network. You can use the OIDC token (JWT) to authenticate requests from your cloud build system into your private network. There's an open-source reference implementation for this, acting as an API gateway or HTTP CONNECT proxy. It's stateless and scales horizontally, but requires maintenance and public internet access.

![Sigstore Keyless Signing](imgs/cloud_build_security/sigstore_keyless_signing.png)]

JWTs can replace durable API keys, but what if they could also replace durable signing keys? The open source sigstore project is working on this. The build system generates an ephemeral signing keypair, sends the JWT, public key, and a challenge to Fulcio (a certificate authority). Fulcio returns a certificate with the public key, timestamp, and JWT attributes. Sigstore also includes Rekor, a transparency log for publishing signatures and certificates for public validation. This is especially promising for open source projects seeking secure, auditable builds.