Benoît Bouré
Posted on December 27, 2021
Security is a very important topic for all cloud engineers. Making sure that your infrastructure and data are kept out of reach of malicious people is one of the most serious things to get right. In AWS, we are used to dealing with IAM roles and permissions that make our resources accessible to users or to other resources. However, sometimes you need to grant access from outside your organization.
One example is when you want to deploy your infrastructure from a CI/CD pipeline, like Github Actions. How do you allow your workflow to gain access to your AWS account?
One approach is to create a dedicated IAM user, store its credentials in the Github secrets store, and allow the workflow to use them. Easy, enough! Secrets are encrypted by Github, so it is secure, right?
Not really... The problem is that those credentials are meant to be long-lived. It means that if anyone is able to get hold of them for whatever reason (eg: a leak in a workflow logs, someone gaining access to a GitHub action runner, etc), they will be able to access all your resources (at least those that the credentials are allowed to control). Sure, you could rotate them from time to time, but you'd have to do that manually. This is probably not something you want to spend time doing, and let's face it, you probably won't!
Luckily, there is a better solution. If you are using Github Actions, you can allow Github to grab temporary, short-lived, credentials that it can use during the execution of the workflow. After that, the credentials will expire and no one will ever be able to use them again.
In this post, I will guide you through the steps to set this up. Don't worry, it's actually easier than you think!
Here is a schema representing what we are going to accomplish
Setting up your AWS account
💡 TL;DR; I created a CloudFormation quick-create link that you can use to automate the following steps. See at the bottom of this article. If you want to know how it works, and what CloudFormation is going to do, keep reading this section.
Create an OpenID Connect Identity provider
The first step is to create an OpenID Connect (OIDC) identity provider in your AWS Account. This will allow Github to identify itself.
- Got to the IAM console -> Identity providers
- Click Add new provider
- Select OpenID Connect
- Provider Url:
https://token.actions.githubusercontent.com
(Don't forget to clickGet Thumbprint
) - Audience:
sts.amazonaws.com
- Add tags if you want to and click Add Provider
💡 You will need to do this step only once per AWS account.
Create a role
You now need to create a role that Github will be able to assume in order to access the resources it needs to control.
- Go back to IAM and select Roles
- Create a new Role
- Chose Web Identity, select the Identity provider you created in the previous step, and its audience.
- Click Next:Permissions
You now need to give the role the appropriate permissions (Policies). These are the ones that Github needs in order to do whatever it has to do. This will vary based on your use case, so I will leave that up to you. Keep in mind that you should stick to the principle of least privileges.
When that is done, give your role a name and click Create Role.
There is now an additional step to do. You need to edit the trust policy of the role to reduce its scope to your repository only. Make sure you don't skip this part, it is very important. Without that, any repository on GitHub will be able to assume your role and access your resources. (Unfortunately, there does not seem to be a way to do that at creation time).
Go back to IAM Roles and select the created Role. Choose Trust Relationships and Edit Trust Relationship.
Under Condition
, add the following segment:
"StringLike": {
"token.actions.githubusercontent.com:sub": "repo:[your-org]/[your-repo]:*"
}
Replace the organization and repo names to match yours, and click Update Trust Policy
.
✍️ Note: You can take this even further and reduce the scope, by using git references, to a branch or tag only, for example.
eg:repo:[your-org]/[your-repo]:ref:refs/heads/master
The final result will look like this:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::1234567890:oidc-provider/token.actions.githubusercontent.com"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
},
"StringLike": {
"token.actions.githubusercontent.com:sub": "repo:[your-org]/[your-repo]:*"
}
}
}
]
}
This concludes the required configurations on your AWS account. Take note of the role ARN, you'll need it later.
💡 You can create different roles per account and use a different one for each use case. For example, one per application, per usage (configurations, deployment, integration tests), etc. You can play with that to reduce the scope of each session even more.
Configure Github action workflow
Your Github workflow requires additional permissions in order to be able to use OIDC. Add the following at the top of your workflow's YML file. You can also add it at the job level to reduce the scope if needed.
permissions:
id-token: write # required to use OIDC authentication
contents: read # required to checkout the code from the repo
You can now use the configure-aws-credentials Github action in the job that needs to assume the role. Add this step to generate credentials before doing any call to AWS:
- name: configure aws credentials
uses: aws-actions/configure-aws-credentials@v1
with:
role-to-assume: arn:aws:iam::1234567890:role/your-role-arn
role-duration-seconds: 900 # the ttl of the session, in seconds.
aws-region: us-east-1 # use your region here.
# You can now execute commands that use the credentials👇
- name: Serverless deploy
run: sls deploy --stage dev
The configure AWS credentials
step will use the OIDC integration to assume the given role, generate short-lived credentials, and make them available to the current job.
💡 If you want to take security even further, you can also keep your role's ARN used in
role-to-assume
in a Github secret.
Automate
The guys at configure-aws-credentials
shared a CloudFormation template that you can use to automate the AWS configuration steps.
I took it one step further; I hosted that template and created a deployment link for you.
Click here to deploy it into your account.
Fill in the parameters:
-
GitHubOrg
: your organization name, or your Github username -
RepositoryName
: the repository that needs access to your AWS account -
OIDCProviderArn
: your existing OIDC provider's ARN, if you have one already. If you don't, leave it empty and one will be created for you. (Remember that you only need one per account).
✍️ Note: The created role will not have any Policy attached to it. You will still need to attach the ones that your workflow needs to it after that.
Conclusion
As you can see, securing your account doesn't have to be hard. The part that might require a little more effort is to define the right Policies if you want to follow the principle of least privileges (which you should!).
For more content like this, follow me on Twitter @Benoit_Boure.
Posted on December 27, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.