· engineering · 7 min read
Authenticating with AWS IAM using a third-party Identity Provider (IDP)
Setting up an Identity Provider for AWS IAM to ensure secure authentication using GitLab as an example.
Table of Contents
If you need to access AWS from a third party platform or application, for example Gitlab, there are primarily two methods you can consider:
The first option is using IAM user credentials.
This method is quick and straightforward: you create an IAM user in the AWS account you wish to access, generate a programmatic access key, and share these credentials with your application.
While this approach is easy, it poses significant security risks because the credentials can be used by anyone, are not restricted to your application, and are long-lived, requiring frequent rotation.
The second and more secure method involves using an Identity Provider (IDP).
An IDP allows us to connect an application directly to AWS, allowing it to issue its own, trusted, short-lived tokens to assume AWS roles.
This method is much safer, as the tokens are short-lived, can be limited in scope, and don’t require rotation, all of which reduce the risk of credential leakage.
How it works
The IDP basically sends a signed request in the form of a JSON Web Token (JWT)). This includes data such as subject and audience, which allow for further access control over who can access your AWS resources, and crucially, a signature.
When we’re setting up an Identity Provider, we specify a “thumbprint” (unless it’s calculated for us).
The thumbprint is the key to the security of the whole setup. It’s a signature created from the SSL certificate issued to the IDP. The IDP uses this certificate to sign the tokens it passes with requests, and AWS uses the thumbprint to verify that the tokens are genuine, allowing the requester to assume the assigned roles.
The thumbprint is explained here in further detail, along with instructions on how to calculate it.
Note: In some cases you may need to update the thumbprint when the certificate is renewed.
Setting up the Identity Provider (IDP)
Setting up IDP is pretty straightforward - you can click your way through the console and figure it out, or use cdk or cloudformation as described below.
ClientId
From the AWS docs: “The client ID is a unique identifier for your app that is passed in the aud claim for authentication.”
In the examples below we will be using the domain of our gitlab as the clientId
- but this could be anything - just be sure it matches the audience
(or aud
) claim in your token (set in gitlab-ci.yml in our case).
Thumbnail Discussed above, this is a signature created from the Certificate that will be used by the IDP to sign the tokens.
Note: You can only deploy any given IDP once to an AWS account.
Using cloudformation:
An advantage of using plain Cloudformation, is that you could automatically provision this connection to all your accounts using stacksets, though it’s simplicity is also a drawback as you’ll need to calculate the thumbprint yourself.
Using CDK
AWS Cloud Development Kit (CDK) is an easy way to create AWS resources and generates cloudformation stacks.
Although it might feel like overkill, it will generate the thumbnail for you, which is nice.
Granting Access to an IAM Role
So, now we have our Identity provider all configured and we’re ready to give it access to some of our resources!
The concept is fairly simple, as you can see from the example below, we’re adding an AssumeRolePolicyDocument
to an otherwise basic role.
This AssumeRolePolicyDocument allows the Principal, the IDP we created earlier, to assume the role, under the given conditions.
You might want to output the ARN of the role created, as you will need it later when configuring gitlab
The token from the IDP contains an audience (aud) (which as we saw earlier must be identical to our clientID), and a subject (sub) which, allows us to restrict resources to certain groups, projects etc.
FOr gitlab, the value project_path:*/*:ref_type:branch:ref:*
will allow any branch of any project of any group to assume this, you can of course make this stricter.
use StringLike for this part of the condition if you are using wildcards - stringEqals won’t work!
Using Cloudformation
You will of course need to import MyIdentityProviderArn from the stack you deployed it in (unless it is the same stack), and set IdpClientID
in the condition correctly.
A note about the condition: I could not find out how to reference a cloudformation parameter as a property name (needed for the condition key) - so this is a limitation to using cloudformation. (If you know how, tell me.
Using CDK
Again, assuming you have imported the ARN for the identity provider, in this case as IdentityProviderArn
and the clientId (audience) as IdpClientID
Then you can assign any resources to the role as you normally would.
Configuring Gitlab CI
So, we have our Identity Provider, and we have a Role we want our Gitlab instance to use
The final piece of the puzzle is to add it to gitlab-ci.yml
.
There are three key parts:
- we specify the token and its audience in id_tokens.
- we write this token to a file, and reference it a named profile this in ~/.aws/config
- we use this named profile for our AWS cli commands
Note: this assumes you are using gitlab v16 or newer - when a complete reworking of the token issuing was introduced.
If you are using an earlier version, or want more info on how OIDC & tokens in works in gitlab, this article
Conclusion
Using IDP when authorizing access for applications to AWS resources is a much more secure, less risky approach than issuing Usernames and Passwords. The credentials are shorter lived, and it’s harder for them to be leaked. Using the “sub” claim of the token allows us to further restrict resource access. The main drawbacks are two fold - firstly, the need to calculate (and potentially update) the thumbprint of the SSL certificates used adds a small layer of complication, and secondly, the ability to provision each openId connect provider only once per account potentially means a change in approach when deploying multiple stacks that would require AWS access to a single account.
However the benefits of this approach outweigh these fairly minor drawbacks, and I would encourage anyone to chose IDP over IAM users wherever possible.
About James Babington
A cloud architect and engineer with a wealth of experience across AWS, web development, and security, James enjoys writing about the technical challenges and solutions he's encountered, but most of all he loves it when a plan comes together and it all just works.
No comments yet. Be the first to comment!