How Open ID Connect works in GitHub Actions (illustrated)

How Open ID Connect works in GitHub Actions (illustrated)

I first learned about Open ID Connect (OIDC) while working on digger, an IaC automation tool. The main component of Digger is a cli which integrates directly in your CI system of choice.

The first time we approached implementing oidc we were lazy and decided to leverage the github action setup-aws which does it for us under the hood. Then later we had some extra requirements which meant that OIDC needed to be implemented as a part of digger which meant that I needed to understand how OIDC works on a deeper level. Going through the official draft of OIDC to me was similar to reading an official spec for OAuth - too many abstract terms that make it difficult to understand. Through some trial and error as well as relying on github’s docs for implementing OIDC, and reverse engineering the setup-aws typescript code I got a working version for OIDC within digger using the aws sdk.

Over here I want to explain my learnings in a simplified way so that you too can understand how all this voodoo magic works under the hood. This article will focus on AWS services, but it works very similarly in other cloud providers as well.

It all started with keys:

Traditionally the easiest way to authenticate on AWS was via keys. you created a pair of keys and used those to authenticate in your application code and in your CI systems to whatever services you needed. This was the simplest way to authenticate and access the cloud services.

IMG_6861.HEIC

long lived keys are not secure

From a security perspective, long-lived passwords, tokens and keys are not secure to be stored. Instead you should rely on rotating keys periodically and ideally never storing them. This is where the role of AWS roles came handy. Now within EC2, lambda, codebuild and other services where your code ran you were able to assume a role and access other aws cloud services. Actually, under the hood the proccess of “assuming a role” generates a temporary set of AWS key, AWS secret and a session token for accessing these services. These keys are valid for an hour and can be set to expire sooner. Now as a developer you no longer needed to store any keys in your application code or worry about having them as secrets or rotating them, great!

IMG_6859.HEIC

How do I access my AWS from the outside world?

But, how do we gain keyless access from outside AWS, for example in my github actions CI system? Do I need to use AWS keys and hardcode them in my secrets store, then manually rotate them periodically?

This is where OIDC comes into play. At first it seems like blackmagic how it works. due to how much of the flow is abstracted away from the user. The steps to use OIDC with AWS+github actions are as follows:

  • Create a role on AWS, add trust policy specifying which github org+repo are allowed to access this AWS role
  • Create an identity provider for github actions
  • use the setup-aws action, specify the role and it will take care of the rest

Alot is hidden away from how it works, and you might guess there is some key exchange going on between AWS and github or something like that. Actually its much simpler if you look at how it really works. If you wanted to implement OIDC without relying on any other code this is how you would do it:

  • Github spins up an Action with your pipeline code
  • Every action job comes with a token as an envirionment variable for authenticated calls to github
  • You send a post request to github, asking for a “web identity token“
  • You send this token to AWS, exchanging it for (you guest it) a pair of keys and session token
  • You use this set of keys to authenticate with AWS services as normal
IMG_6860.HEIC

The key point here is that the “web identity token” is a JWT token signed by github and who’s contents can be verified by AWS using github’s public key. Therefore AWS can verify the key and use the data within the JWT token to verify access only using the token alone. Since the token also contains “scopes” such as organisation, repo, branch, AWS can either grant or deny access based on these scopes within the verified JWT token.

So there you have it, an illustrated guide to OIDC with AWS and Github actions. Other CIs and cloud providers also work in a similar way.