One of the main security principles in technology is called The Principle of Least Privilege, which states that each user or application should only have strict access to the resources that it really needs. For example, if a user is only intended to Read from a file store and only within a specific folder, that user should have write access to the same folder, or read access to all the folders, or even more administrative permissions that could be a focus for permission escalation attacks.
In AWS, there are two ways to apply the Principle of Least Privilege, which are through IAM to provide granular permissions to access the AWS resources at the API level (interact with AWS itself), and the other way is through Network VPC but leveraging strict security groups, NACLs, VPC connections, VPNs, etc.
In this blog, we want to focus on the IAM part specially for the workloads deployed in Amazon EKS. The idea is to restrict permissions on those applications that are running in Kubernetes that require access to AWS resources, like an S3 bucket, an SNS topic, an SQS queue, etc.
Although historically there has been multiple solutions to approach this securely, like Kube2IAM and Kiam, AWS themselves have developed native solutions using their own services, which provide high availability and increased security.
One of those methods is called IAM Roles for Service Accounts, or IRSA. This method aligns the OpenID capabilities of Kubernetes with the ones of IAM, creating an IAM Identity Provider and connecting it to the EKS cluster to provide better connection and allow specific pods with specific Kubernetes Service Accounts to assume IAM Roles so that each pod could have its own set of IAM permissions. Let's dig deeper into it.
Requirements
First, to achieve this, you need an EKS cluster that can be created in multiple ways. We always recommend EKSCTL as the official EKS CLI tool to not only create cluster but also manage them throughout the live of the cluster.
Also, you'll need an IAM Identity Provider configured to work with your EKS Cluster. You can read more about the Identity Providers in IAM here: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers.html.
But don't worry! we have you covered. Later in the blog we'll show you an easy way to create one with EKSCTL and automatically bind it with your cluster.
But first, let's focus on how these components interact with IAM Roles, policies, and Kubernetes through Service Accounts and the actual pod that ends up running there.
How does it work?
Let's explain how all of this works behind the scenes to understand what it can do:
1. Youʼll create the IAM Identity Provider with the EKS information to connect both OpenID worlds. Explained later in the blog).
2. After you have that, you can start create IAM Roles that trust the IAM Identity Provider and, once assumed, those are going to be authenticated with Kubernetes as well.
3. Create an IAM Policy for the IAM role giving it access to, for example S3.
4. Assign the IAM Role using its ARN to a Service Account in Kubernetes.
5. Create the Pod that requires access to AWS, for example to S3.
6. Your pod, using the AWS SDK, AWS CLI, or whatever other method to access AWS, will be able to smoothly assume the IAM role related to the Service Account and authenticate against S3 successfully.
Steps 2 and 4 can be easily setup and its called the IAM Service Account, because is the mix of an IAM Role with the Kubernetes Service Account. Weʼll review how to do this easily later in the blog.
How to enable OIDC with IAM and EKS?
The easiest way to create the OIDC connection between IAM and EKS is using the eksctl tool. There are two methods for enabling this.
If you don’t have the EKS cluster yet, you can create it with the iam.withOIDC flag set to “true” in your cluster.yaml file, like this:
If your cluster already exists, you can either update it with the previous cluster.yaml file, or you can simply use the following command:
Both of these methods will create the OIDC provider in AWS
How to create IAM Service Accounts?
As expected with EKSCTL there are two ways for doing this:
Using the cluster.yaml file and creating the IAM Service Accounts in one place:
Using the EKSCTL command directly:
Note: The IAM Policy should have already been created. So you can use AWS Managed Policies, like in the example, which are pre-configured policies provided by AWS and available in any AWS account, or you can create your own policies.
What do we have after the creation of the IAM Service Account?
After the creation of the IAM Service Account, youʼll have the IAM Role already configured that looks like this:
An important detail here will be under the Trust relationships tab, which has all the details of who can assume this role and basically itʼll be the Kubernetes Service Account “read-from-s3ˮ. This Trust relationship policy is really important because itʼs the actually connection between Kubernetes and IAM.
Also, youʼll have the Service Account with a special annotation called eksamazonaws.com/rol-arn. This one has the ARN of the IAM Role it will assume:
How to assign IAM Service Accounts to the pod?
At this level, all the remaining work is at the Kubernetes level. Just adding the serviceAccountName spec in the Pod/Deployment will make sure the service account is assigned.
After the pod is created, youʼll see that the IRSA integration kicks in when you get the details of the brand new pod:
To the pod itself were added new environment variables automatically with information about the IAM Role. For example, you can find the AWS_ROLE_ARN which is the actual ARN of the Role that we created in previous steps. Another important one is AWS_WEB_IDENTITY_TOKEN_FILE which has the location of the JWT token injected to the pod which will be used for the authentication. Some other environment variables like the AWS Region are added.
Conclusion
As mentioned before, this is not the only way to give IAM access to pods in Kubernetes, but definitely one of the most important and smooth ones. Itʼs always great to have multiple ways to create these resources as it gives you flexibility for integrating in CICD environments for example.
And now that you are all set! your pod has native IAM access without granting it to other pods that wonʼt needed it. Enjoy your least privilege posture!