How to create a temporary token for your GitHub Actions pipeline to deploy into AWS
Plus: Terraform IAC for this :)
TLDR;
Most often that I would like to admit, I learn to do a thing, and I do that thing in that way for the rest of my life. I don’t search for better ways to do it.
Until yesterday, my way to deploy to AWS was to create IAM users and save the access and secret key as environment secrets and go in that way.
The major issue for me with that process is that I don’t rotate the secrets as I should to protect my application and infrastructure. And this is clearly a security issue, even using IAM policies to get the minimum access configuration right.
A couple of days ago, I was trying to deploy an app to AWS App Runner and using ECR as the source for the service. So, I needed a GitHub Actions pipeline to build and push the image to my private ECR, where App Runner could use it.
And, while reading the docs (what was the last time that you have done this? properly hahaha) of the configure aws credentials action, I noticed that there are 5 ways to authenticate in AWS and the recommended one is using temporary tokens and OIDC:
And I’ve got curious about how to use the recommended path for authentication.
With OIDC and an Assume Role, I can generate temporary tokens that only live for one hour to make everything that I require in AWS.
The next step was to read GitHub and AWS docs and translate that into Terraform code. The step by step using the AWS console are in the mentioned docs, so from here I’ll show to you how I translated those 10 steps into a couple terraform files.
GitHub OIDC AWS auth with Terraform
I’m going to set up a `locals` with some variables to help us out with this process:
In the screenshot (I’ll put a gist with all this code in the end), I set up the github_oidc_domain
with the URL from GitHub token provider as shown in the GitHub docs.
The next step is to create and register GitHub as an Identity Provider on AWS with this code:
To configure this resource, we need to fill 3 variables: the URL from the OIDC that we get from the locals
, the client id that is the AWS Security Token Service and the thumbprint1.
The next step now is to create the IAM role and the Assume Role configuration, so this role can properly authenticate in AWS when used in our pipeline:
Now I’ll update the locals to have the URL from my GitHub repository to authenticate:
As always, I’ll use my good old friend the data resource from terraform the aws_iam_policy_document
:
Now in there are plenty of things to uncouple:
The Principal to assume the role is the ARN of the OpenID connect that we created in the previous step;
The action is to
AssumeRoleWithWebIdentity
, that allows the STS service to issue the temporary tokens that we need;We have two sets of conditions:
The first ensures that the audience is the STS service
The second ensures that the subject[sub] (or the user) is the GitHub repository that is trying to push the image to ECR.
This one is a little tricky to configure to specific GitHub tags, branches, etc. The configuration that I have made only allows the authentication from pipelines triggered by releases and tags. Check these docs to see how you can build your URL.
Presently, that it’s just a policy that needs to be created and attached somewhere, let’s create the IAM Role2 and attach that:
Now we have our OIDC configuration, and the IAM Role configured. Now we should only configure our Action in our GitHub pipeline, right? Wrong. What we did so far only allows the authentication, but the IAM Role does not have permission to use the ECR service. Let’s fix that:
Now we attach this policy to our IAM Role:
Now we can run our traditional terraform plan
and apply
and we are ready to go3.
Configuring our pipeline
For the GitHub Actions pipeline, we have the following configuration:
Note that when configuring the configure-aws-credentials action that I don’t pass the Access and Secret key, I just pass the ARN of the IAM Role (as a secret in my pipeline) that was created and attached to the OIDC in our previous section. In the next step, it authenticates to ECR and then builds and pushes the image to ECR with double tagging.
And now we are done. But a question remains:
How can I do that for multiple repositories? It isn’t possible to create an OIDC for each of them (because they are unique). And I can’t add many repositories in the IAM Role because that breaks the minimum responsibility for the Role. Apparently, the solution is to create an IAM Role for each repository and attach the OIDC as the principal. But does that create a security issue?
Please let me know in the comments what you think.
That’s all folks!
The thumbprint I have got directly from the AWS console after testing on how to create the IP. Click here to get form GitHub blog.
The nomenclature of all resources can be done in any convention that you may follow :)
You may want to output to terraform the ARN for the IAM Role that was created because we will need it soon.
Great post, Lays! Thank you for sharing 🙌
Regarding the security issue question, it seems that by scoping the IAM Role's audience to your GitHub account/repo/branch combination, you are guaranteeing that workflows in a different GitHub account/repo/branch won't be able to assume that role, even if they use the same OIDC identity provider.
- https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_create_for-idp_oidc.html#idp_oidc_Create_GitHub
- https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect#defining-trust-conditions-on-cloud-roles-using-oidc-claims