Using IAM Roles for Kubernetes service accounts in AWS EKS using Terraform
Part of the design principles of the security pillar in the AWS Well architected framework is “Implement a strong identity foundation”, that is:
“Implement the principle of least privilege and enforce separation of duties with appropriate authorization for each interaction with your AWS resources. Centralize identity management, and aim to eliminate reliance on long-term static credentials.”
Well Architected Framework - Security Pillar - Design principles
When we start using Kubernetes in AWS EKS, we might take some shortcuts during the learning phase and add all the policies to a role that we directly assign to our nodes. The issue is that we give much more privileges than we require just for practicality. And it can also become a habit. The way we learn something sometimes becomes our default pattern. So let’s break that habit and explore how to set IAM Roles to Kubernetes service accounts in AWS EKS.
Note: If you are interested in learning more about how to set up the directory structure for your Terraform project, you might find my guide, Meditations on Directory Structure for Terraform Projects, useful.
Bash Beyond Basics Increase your efficiency and understanding of the shell
If you are interested in this topic you might enjoy my course Bash Byond Basics. This course helps you level up your bash skills. This is not a course on shell-scripting, is a course on improving your efficiency by showing you the features of bash that are seldom discussed and often ignored.
Every day you spend many hours working in the shell, every little improvement in your worklflows will pay dividends many fold!
Learn moreWhat are IAM Roles for Kubernetes service accounts?
The IAM Roles for Kubernetes service accounts allow us to associate an IAM role with a Kubernetes service account. This feature is available through the Amazon EKS Pod Identity Webhook. The IAM role for a service account is then used to provide AWS credentials to the pod or resource using the service account. These credentials are automatically rotated before they expire.
If you have used WebIdentity federation with AWS Cognito, you will be familiar with the concept of IAM roles for service accounts.
Ok, so what do we need?
- OIDC Issuer - Already set up if you are running in EKS
- OIDC provider
- The IAM role that we want to associate with the service account
- The Kubernetes service account that we want to associate with the IAM role
Set up OIDC provider
The first step is to set up the OIDC provider. OIDC, if you are unfamiliar, is an authentication protocol similar to SAML, but it is based on JSON Web Tokens (JWT). AWS also uses it to authenticate users in AWS Cognito.
There are two parts to using OIDC an Issuer and a Provider. The issuer is the entity that issues the token, and the provider is the entity that validates the token. In our case, the issuer is handled by AWS, and Kubernetes handle the provider. When we create the EKS cluster, we already have an Issuer endpoint. If you want to use your OIDC issuer, you can provide that to EKS. But in this example, we’ll stick to the issuer already in the EKS cluster.
To get the URL of the issuer, run the following command:
| |
You should see something like the following:
https://oidc.eks.us-east-1.amazonaws.com/id/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Let’s check first if an OIDC provider is already created for our cluster. Run the following command:
| |
If you get an empty list, then you need to create the OIDC provider. Let’s define it in terraform:
| |
Now, if we run the command to list the OIDC providers, we should see the one we just created:
| |
With the OIDC provider created, we can now create the IAM role and the Kubernetes service account.
Creating the IAM role
We will create a new IAM role, and we will call it eks-test-role. We want the pod to read a parameter from the SSM parameter store, so we will also create a policy for that and attach it to the same role.
| |
We will create an additional policy allowing the pod to read any parameter in the parameter store. We are going to call it cluster-ssm-parameter-policy:
| |
We will create a go program that will serve as a basic HTTP server. The server’s sole function is to echo the parameter value. For Kubernetes to be able to pull the image from ECR, we need to create an ECR repository. Let’s do that. We are going to call it test-ecr:
| |
The following is a quick and dirty go program that will read the parameter from the parameter store and echo it:
| |
We will also need a Dockerfile to build the image:
| |
We can now build the image and push it to ECR. Let’s create a file called deploy.sh and add the following content:
| |
We can now run the script:
| |
And we should have our Image ready to deploy in Kubernetes.
Create the SSM parameter
We are going to create an SSM parameter that the pod will read. We are going to call it /my/parameter:
| |
Create the Kubernetes configuration
We will create the Kubernetes namespace.yml, deployment.yml, service.yml, and service-account.yml.
The namespace.yml file will create the namespace that we are going to use:
| |
The service-account.yml file will create the service account that we are going to use:
| |
Replace <AWS_ACCOUNT_ID> and <ROLE_NAME> with the values you created in the previous steps.
The service.yml file will create the service that we are going to use:
| |
The deployment.yml file will create the deployment that we are going to use:
| |
Replace <ERC_URL> with the value of the ECR we created in the previous steps.
Ok, now let’s deploy everything:
| |
And we should be ready to test.
Test that everything is working
We can now test if our pod uses the service account we assigned.
We can port forward to our local host and hit our endpoint to see if we get the parameter’s value. Assuming we only have one pod running in my namespace, we can do the following:
| |
In a different terminal, we can do a curl to the endpoint:
| |
And we should see:
Greeting: Hola Mundo
Final thoughts
Setting up the OIDC might be the most confusing part if we haven’t worked with it before. But remember you need an Issuer, which is already provided if you use AWS EKS. You can link to an external issuer in the same way. We also need a provider accessible to our cluster, as we did.
After the OIDC part is completed, we only need to create our IAM roles as we usually do and build the association by creating a service account and assigning the role to it.
I hope this helps you to understand how to use IAM roles for service accounts in Kubernetes.
References
AWS Documentation - Kubernetes service accounts AWS Documentation - Well architected framework Illustrated guide to OAuth and OpenID Connect