If you need to access an AWS resource from your environment (whether from your local machine or a CI/CD pipeline), you’ll need your permanent AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY. Your deployed application must also have and use similar credentials (though a temporal one) to access the resources required to start and run successfully. However, we can’t (and ideally shouldn’t) hard-code these values into the application and commit them to our version control platform.
One of the 12-Factor App principles, an established representation of a modern software application, is Config, which emphasizes a strict separation of configuration from code. The most prominent part of our configuration is usually the credentials required for the application to start and run successfully. These credentials typically include but are not limited to, those for external services like databases, identity providers, and supporting internal applications. The idea of separating configuration from code enhances the security of the application’s delivery, as well as its handling and deployment across multiple environments.
Scenario
Suppose we have a simple containerized Spring Boot application deployed in an EKS cluster. This application connects to several resources: an RDS database for textual data storage and S3 for object storage. These resources are all provisioned within the same AWS environment where the application will be deployed.
As mentioned earlier, the credentials or parameters for these resources are typically stored in a secrets or parameter management service, such as AWS Secrets Manager, AWS Systems Manager Parameter Store, or HashiCorp Vault.
The question arises: How will the application access these secrets to retrieve the necessary credentials to start and run successfully?
There are multiple ways to achieve this but they generally follow the same underlying approach. The focus of this article, however, will be on the IRSA approach.
What is IRSA?
IRSA stands for IAM Roles for Service Accounts.
IAM (Identity and Access Management) – AWS’s system for managing authentication and authorization for all users and requests. It defines who can access AWS resources and what actions they can perform.
For each IAM Role, there are two associated policies:
- Trust Policy – This policy specifies who (the trusted principal) is allowed to assume the role. This handles authentication.
- Permission Policy – This policy defines what actions can be performed with the role. It handles authorization.
A Service Account is one of the user categories in Kubernetes, with regular users being the other category (Kubernetes Authentication Documentation). Service accounts are essential for managing authentication and authorization for Kubernetes requests.
In Kubernetes, Pods authenticate requests using an auto-mounted token that is generated during the pod’s creation. This token was previously not OIDC (OpenID Connect) compliant—it had no expiration and was difficult to rotate. However, in recent versions of Kubernetes (>= 1.12), support for ProjectedServiceAccountToken and BoundServiceAccountToken was introduced, making the token OIDC-compliant. This token can now be bound to a specific AUDIENCE, TIME, and KEY, which improves security by enabling better control of the token’s scope and lifespan.
IRSA therefore is simply mapping these two (IAM and Service Account) together for proper authentication and authorization of requests between the two (AWS and K8s) component by creating an IAM role on AWS which a service account from Kubernetes can assumed every time it wants to access AWS resources.
Core Advantages of IAM Roles for Service Accounts (IRSA):
- Least Privilege: Only pods that are using a specific service account have access to the permissions granted by the roles mapped to that service account.
- Credential Isolation: Each pod retrieves and uses only the credentials associated with its own service account.
- Auditability: Events and logs related to the usage of the IAM role can be tracked and audited.
Question: How is this Mapping Achieved?
To securely map Kubernetes Service Accounts to IAM Roles for AWS resource access, several components are involved:
- OIDC Provider: With the support of Federated Identity by AWS, it’s now possible to use OpenID Connect (OIDC) to authenticate Kubernetes service accounts.
- Trust Provider: The trust provider establishes the relationship between the identity provider (OIDC) and AWS. In this case, the Trust provider is STS (Security Token Service).
- OIDC Discovery Endpoint: EKS provides a public OIDC discovery endpoint for each cluster, which contains the signing keys for the ProjectedServiceAccountToken (the token from Kubernetes). These keys are used by AWS to validate and accept the token.
Steps to Achieve the Mapping:
AWS Configuration:
- Create an OIDC Provider for your Cluster
- Follow the steps in the EKS guide to enable IAM roles for service accounts.
- Create the IAM Role (Trust Policy and Permission Policy)
- Follow the IAM role creation documentation to create an IAM role and attach the appropriate trust and permission policies.
- Associate the Role with the Service Account
- Link the IAM role with the Kubernetes service account via the EKS documentation for associating service accounts with roles.
A sample Trust relationship definition for the IamRole.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::accountId:oidc-provider/oidc.eks.us-west-1.amazonaws.com/id/xxxxxxxxxx"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"oidc.eks.us-west-1.amazonaws.com/id/xxxxxxx:aud": "sts.amazonaws.com"
}
}
}
]
}
Kubernetes Configuration:
- Annotate the Service Account with the IAM Role ARN
This step creates the mapping between the Kubernetes service account and the IAM role. The annotation also causes Kubernetes to download a web identity token file into the pod. Example YAML for service account annotation:
apiVersion: v1
kind: ServiceAccount
metadata:
name: service-account-name
annotations:
eks.amazonaws.com/role-arn: arn-of-the-iam-role
AWS STS (Security Token Service)
- STS provides temporary security credentials with limited privileges, complementing IAM roles by delegating access to those roles.
- The AWS SDK automatically interacts with STS to exchange a Kubernetes web identity token for temporary credentials, using regional or global STS endpoints as needed.
Workflow: Accessing AWS Resources
Here’s a typical flow of what happens when your application tries to access an AWS resource:
- Pod Creation: When the pod is created, a web identity token is downloaded into the pod.
- Application Start: When your application starts, the AWS SDK (with the STS client) uses the web identity token to call STS for temporary credentials.
- Temporary Credentials: The temporary credentials are injected into the application.
- Resource Access: The AWS SDK uses temporary credentials (access key, secret key, and session token) to access the AWS resource—just like it would use permanent credentials.
<dependency>
<groupId>software.amazon.awssdk</groupId>
<artifactId>sts</artifactId>
</dependency> - Credential Lookup: The AWS SDK automatically searches through the 7 possible locations for credentials and uses the first valid one it finds. These locations are detailed in the AWS SDK Credentials Guide.
Role of the Audience in Token Mapping
Web Identity Token Content: The content of the token includes the identity of the service account (its namespace and name), as well as the audience (aud), which is set to sts.amazonaws.com to ensure that is bound to only AWS STS.
Example content of the web identity token:
{
"iss": "https://sts.amazonaws.com/",
"sub": "system:serviceaccount:my-namespace:service-account-name",
"aud": "sts.amazonaws.com",
"exp": 1631835587,
"iat": 1631831987,
"kubernetes.io/serviceaccount/name": "service-account-name",
"kubernetes.io/serviceaccount/namespace": "my-namespace",
"kubernetes.io/serviceaccount/uid": "some-unique-identifier"
}
STS Response (Temporary Credentials): Once the token is exchanged for temporary credentials, the response from STS includes the temporary credentials that can be used by the application to access AWS resources.
Example response:
{
"Credentials": {
"AccessKeyId": "ASIA…EXAMPLE", // Temporary Access Key ID
"SecretAccessKey": "wJalr…EXAMPLE", // Temporary Secret Access Key
"SessionToken": "Fwo…EXAMPLE", // Temporary Session Token
"Expiration": "2024-12-14T16:20:12Z" // Expiration time in UTC
},
"AssumedRoleUser": {
"AssumedRoleId": "AROEXAMPLE:role-name", // IAM Role ARN assumed by the pod
"Arn": "arn:aws:sts::123456789012:assumed-role/role-name/session-name"
}
}
Conclusion
By combining Kubernetes’ service accounts with IAM roles using IRSA, a containerized application can securely access external AWS resources.
References
- Temporary credentials – AWS documentation on IAM temporary credentials.
- AssumedRole https://docs.aws.amazon.com/STS/latest/APIReference/API_AssumeRole.html
- OIDC – https://openid.net/developers/how-connect-works/
- Service Accounts – https://kubernetes.io/docs/concepts/security/service-accounts/
- IRSA – https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html
Leave a comment