Access Model in Amazon EKS Clusters
The access rights model in Amazon EKS should be designed to:
- limit service rights based on the principle of least privilege;
- separate rights within Kubernetes and rights in AWS;
- manage access for applications, users, CI/CD, and worker nodes separately;
- avoid using application-specific IAM rights at the node level;
- eliminate static AWS keys in applications;
- ensure clear auditing: who has access to what and on what basis.
It is important to understand that EKS does not have a single, unified rights system.
There are several independent access domains that solve different problems.
Main Access Domains
| Domain | Who gets access | Mechanism | What access is granted to |
|---|---|---|---|
| Managing EKS via AWS API | administrators, Terraform, CI/CD | AWS IAM | creating and modifying clusters, node groups, add-ons, Access Entries |
| Access for people and automation to Kubernetes API | engineers, CI/CD, GitOps | EKS Access Entries, EKS Access Policies, Kubernetes RBAC | namespaces, Deployments, Pods, Secrets, and other Kubernetes objects |
| Application access to Kubernetes API | Pod on behalf of ServiceAccount | Kubernetes RBAC | Kubernetes API objects |
| Application access to AWS API | Pod on behalf of ServiceAccount | EKS Pod Identity or IRSA | S3, SQS, DynamoDB, Secrets Manager, KMS, and other AWS services |
| System rights of worker nodes | EC2 worker node or Fargate infrastructure | Node IAM Role or Fargate Pod Execution Role | node registration, image pulling, system component operation |
These domains do not replace each other.
For example:
- IAM rights to S3 do not give an application the right to read Kubernetes Secrets;
- A Kubernetes Role does not give an application access to SQS;
- IAM rights to manage an EKS cluster do not imply automatic access to all objects within Kubernetes;
- A worker node's role should not be used as a general role for applications.
General Principle
Every service in the cluster runs under a dedicated Kubernetes ServiceAccount.
This ServiceAccount is used as an application identifier and is linked to two independent rights models:
- Kubernetes RBAC defines the service's rights within the cluster;
- EKS Pod Identity or IRSA grants the service limited rights in AWS.
The linkage looks like this:
Application
|
v
Kubernetes ServiceAccount
|
+--> Kubernetes RBAC --> Kubernetes API
|
+--> EKS Pod Identity or IRSA --> IAM Role --> AWS API
The ServiceAccount itself does not contain an IAM policy and does not automatically grant rights.
It only identifies the workload. Rights appear after the ServiceAccount is linked to:
- A Role or ClusterRole via RoleBinding or ClusterRoleBinding;
- An IAM role via Pod Identity Association or an IRSA annotation.
For most applications, the recommended model is:
One service
-> one ServiceAccount
-> a separate set of Kubernetes RBAC rights
-> a separate IAM role or a separate set of IAM restrictions
Combining several services under one ServiceAccount or one IAM role should only be done if they truly have identical:
- access requirements;
- owners;
- trust level;
- lifecycle;
- risk profile.
Rights within Kubernetes: RBAC
The standard Kubernetes RBAC mechanism is used to manage service actions within the cluster.
A separate ServiceAccount is created for each service or group of homogeneous services.
Allowed operations with Kubernetes objects are defined via a Role or ClusterRole.
For example:
- reading a specific ConfigMap;
- reading a Secret in its own namespace;
- viewing Pods;
- creating a Job;
- updating a specific Custom Resource;
- accessing objects only in one namespace.
The role is bound to the corresponding ServiceAccount via a RoleBinding or ClusterRoleBinding.
The application manifest explicitly specifies which ServiceAccount the Pod should run under:
spec:
template:
spec:
serviceAccountName: orders-api
If serviceAccountName is not specified, the Pod runs under the default ServiceAccount of its namespace.
For production services, it's better not to use the common default ServiceAccount. A separate ServiceAccount makes rights explicit and simplifies auditing.
Role and ClusterRole
A Role defines rights within a single namespace.
A ClusterRole can be used:
- for cluster-wide resources;
- for rights that should apply across all namespaces;
- as a reusable rights template that is then bound via RoleBinding in a specific namespace.
By default, it is preferable to use Role and RoleBinding.
ClusterRoleBinding should only be applied when a service truly needs cluster-wide rights.
Example of minimal RBAC
The orders-api service is allowed to read only one ConfigMap in the orders namespace:
apiVersion: v1
kind: ServiceAccount
metadata:
name: orders-api
namespace: orders
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: orders-config-reader
namespace: orders
rules:
- apiGroups: [""]
resources: ["configmaps"]
resourceNames: ["orders-config"]
verbs: ["get"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: orders-api-config-reader
namespace: orders
subjects:
- kind: ServiceAccount
name: orders-api
namespace: orders
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: orders-config-reader
Such a service will not be able to:
- read other ConfigMaps;
- read Secrets;
- modify Deployments;
- execute commands in other Pods;
- work with objects in other namespaces.
Important feature of Kubernetes RBAC
Kubernetes RBAC operates on a permissions model.
There is no explicit deny in RBAC.
If a subject has been granted a right through at least one Role, ClusterRole, or binding, that right is considered allowed.
Therefore, rights from different roles are cumulative.
You cannot grant a broad role and then use another role to deny some operations.
Which rights are particularly dangerous
Permissions that require special control include:
- reading Secrets;
- creating and modifying RoleBinding and ClusterRoleBinding;
- creating Pods with arbitrary ServiceAccount;
pods/exec;pods/attach;pods/portforward;- modifying admission webhooks;
- modifying CustomResourceDefinition;
- using
impersonate; - access to all resources via
resources: ["*"]; - performing all operations via
verbs: ["*"].
Formally, such rights may not be called cluster-admin, but in practice, they often allow privilege escalation.
Should the ServiceAccount token be mounted?
If the application does not require access to the Kubernetes API, it is better not to mount the standard ServiceAccount token:
spec:
template:
spec:
serviceAccountName: orders-api
automountServiceAccountToken: false
This reduces the number of credentials inside the container.
EKS Pod Identity and IRSA use separate mechanisms for transmitting workload tokens, so the absence of a regular Kubernetes API token does not negate the need to correctly configure the application's IAM access.
Access for Users and CI/CD to Kubernetes API
Access for engineers, administrators, Terraform, CI/CD, and GitOps controllers should be considered separately from application access.
For new EKS clusters, the primary mechanism is EKS Access Entries.
An Access Entry links an IAM principal to a specific EKS cluster.
An IAM Role is typically used as the principal:
User or CI/CD
-> AssumeRole
-> IAM Role
-> EKS Access Entry
-> EKS Access Policy or Kubernetes RBAC
-> Kubernetes API
After creating an Access Entry, rights can be defined in two ways.
EKS Access Policies
A pre-defined EKS Access Policy can be attached to an Access Entry.
It can grant access:
- to the entire cluster;
- only to specific namespaces;
- at an administrator level;
- at an editor level;
- at a viewer level.
Important: An EKS Access Policy is not a regular IAM policy.
It defines rights within the Kubernetes API, not rights for AWS API calls.
Kubernetes RBAC via groups
An Access Entry can be linked to a Kubernetes group.
After this, the group's rights are defined by regular RoleBinding and ClusterRoleBinding.
This option is suitable when the predefined EKS Access Policies are too broad and more granular rights management is required.
For example:
IAM Role of the orders team
-> EKS Access Entry
-> Kubernetes group: orders-developers
-> RoleBinding in the orders namespace
What to do with aws-auth
The aws-auth ConfigMap has long been used to bind IAM roles and users to Kubernetes groups.
This mechanism is now considered deprecated.
For new clusters, EKS Access Entries should be used.
For existing clusters, a transitional API_AND_CONFIG_MAP mode is possible, where both Access Entries and aws-auth operate simultaneously.
Practical rules for human access
- use IAM Roles, not permanent IAM User credentials;
- use AWS IAM Identity Center or another federation mechanism;
- do not grant
cluster-adminfor daily work; - restrict teams to their own namespaces;
- create a separate break-glass role for emergency access;
- separately divide read, operational, and administrative roles;
- describe Access Entries and bindings via Infrastructure as Code.
Rights from EKS Access Policies and Kubernetes RBAC are cumulative.
Therefore, a narrower RoleBinding cannot be assumed to restrict a broad Access Policy.
Also, the kubectl auth can-i command shows Kubernetes RBAC but does not always reflect rights obtained through EKS Access Policies. During auditing, both mechanisms must be checked.
Managing EKS via AWS API
A separate domain is the rights to manage the Amazon EKS service itself.
These are IAM permissions for operations such as:
- creating and deleting clusters;
- modifying endpoint access;
- creating managed node groups;
- installing add-ons;
- creating Access Entries;
- creating Pod Identity Associations;
- updating Kubernetes versions;
- modifying control plane parameters.
For example, the right to call eks:UpdateClusterConfig belongs to the AWS API.
It is not equivalent to the right to modify a Deployment via the Kubernetes API.
Conversely, a user with cluster-admin inside Kubernetes does not necessarily have the right to change cluster settings via the AWS API.
These two types of administrative access must be separated.
Application Rights in AWS
Application access to S3, SQS, DynamoDB, Secrets Manager, KMS, and other AWS services should be done through temporary credentials.
Static AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY values should not be stored:
- in Kubernetes Secrets;
- in ConfigMaps;
- in Helm values;
- in CI/CD variables unnecessarily;
- inside the container image.
In Amazon EKS, two main mechanisms are used for workload identity:
- EKS Pod Identity;
- IAM Roles for Service Accounts, or IRSA.
EKS Pod Identity
For new applications on Linux EC2 nodes in EKS, EKS Pod Identity is the primary option.
The linkage looks like this:
Pod
-> Kubernetes ServiceAccount
-> EKS Pod Identity Association
-> IAM Role
-> temporary credentials
-> AWS API
For Pod Identity to work:
- The EKS Pod Identity Agent runs in the cluster.
- An IAM role is created with a trust policy for the
pods.eks.amazonaws.comservice. - An association is created in EKS between:
- the cluster;
- the namespace;
- the ServiceAccount;
- the IAM role.
- The AWS SDK within the application obtains temporary credentials through the standard credential provider chain.
Example trust policy:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "pods.eks.amazonaws.com"
},
"Action": [
"sts:AssumeRole",
"sts:TagSession"
]
}
]
}
Example of creating an association:
aws eks create-pod-identity-association \
--cluster-name production \
--namespace orders \
--service-account orders-api \
--role-arn arn:aws:iam::111122223333:role/orders-api
An annotation on the ServiceAccount for Pod Identity is not required.
The association is stored at the EKS API level.
Benefits of Pod Identity
- no separate IAM OIDC provider is required for each cluster;
- IAM role trust policies are easier to reuse across clusters;
- the association explicitly links the role to the cluster, namespace, and ServiceAccount;
- session tags are supported;
- session tags can be used for ABAC;
- easier to centrally manage a large number of clusters and roles.
Limitations of Pod Identity
Pod Identity is used for Pods on supported Linux EC2 nodes in Amazon EKS.
It does not replace IRSA for all scenarios.
Specifically, IRSA remains a necessary option for:
- AWS Fargate;
- Windows worker nodes;
- environments where the Pod Identity Agent is unavailable;
- existing integrations that already work correctly via IRSA.
IRSA: IAM Roles for Service Accounts
IRSA remains a relevant and supported mechanism.
The linkage looks like this:
Pod
-> Kubernetes ServiceAccount
-> OIDC token
-> AWS STS AssumeRoleWithWebIdentity
-> IAM Role
-> temporary credentials
-> AWS API
For IRSA to work:
- An IAM OIDC identity provider is configured for the EKS cluster.
- An IAM role is created for the application.
- The role's trust policy specifies the particular ServiceAccount and namespace.
- The minimally required IAM policy is attached to the IAM role.
- The ServiceAccount is annotated with the ARN of the IAM role.
- The AWS SDK within the application exchanges the signed token for temporary credentials via AWS STS.
Example ServiceAccount:
apiVersion: v1
kind: ServiceAccount
metadata:
name: orders-api
namespace: orders
annotations:
eks.amazonaws.com/role-arn: arn:aws:iam::111122223333:role/orders-api
Example of the main part of the trust policy:
{
"Condition": {
"StringEquals": {
"oidc.eks.eu-central-1.amazonaws.com/id/EXAMPLE:aud": "sts.amazonaws.com",
"oidc.eks.eu-central-1.amazonaws.com/id/EXAMPLE:sub": "system:serviceaccount:orders:orders-api"
}
}
}
The sub condition restricts the use of the role to a specific ServiceAccount in a specific namespace.
The aud condition confirms that the token is intended for AWS STS.
You should not unnecessarily allow AssumeRole for all ServiceAccounts in the cluster or for an entire namespace.
What exactly happens when a Pod with IRSA is launched
EKS does not pass permanent AWS keys to the application.
A signed ServiceAccount token is mounted in the Pod.
The AWS SDK finds this token through the standard credential provider chain, calls STS AssumeRoleWithWebIdentity, and obtains temporary credentials for the IAM role.
Therefore, the application does not need to store or refresh AWS keys itself.
Pod Identity or IRSA
The practical choice can be summarized in the following table.
| Scenario | Recommended Mechanism |
|---|---|
| New application on Linux EC2 nodes in EKS | EKS Pod Identity |
| Pod on AWS Fargate | IRSA |
| Pod on Windows worker node | IRSA |
| Existing working integration via IRSA | can keep IRSA |
| One IAM role used across multiple clusters | EKS Pod Identity |
| ABAC required by cluster, namespace, or ServiceAccount | EKS Pod Identity |
| Environment outside standard Amazon EKS | depends on the environment, often IRSA or another OIDC mechanism |
Both mechanisms solve the same problem: giving the application temporary AWS credentials without static keys.
The main difference is in the method of establishing trust and linking the ServiceAccount to the IAM role.
Application IAM Policies
Regardless of whether Pod Identity or IRSA is used, the IAM policy should be minimal.
Bad:
{
"Effect": "Allow",
"Action": "s3:*",
"Resource": "*"
}
Better:
{
"Effect": "Allow",
"Action": [
"s3:PutObject"
],
"Resource": "arn:aws:s3:::orders-archive/incoming/*"
}
You should restrict:
- the list of actions;
- the list of resources;
- specific S3 prefixes;
- specific SQS queues;
- specific DynamoDB tables;
- specific Secrets Manager secrets;
- specific KMS keys;
- region and account, where possible;
- additional conditions via IAM Conditions.
For large platforms, ABAC can be used, but only if the tagging scheme and rules for assigning them are centrally controlled.
Worker Node Role
The use of Pod Identity or IRSA does not negate the worker node's IAM role.
The Node IAM Role is still required for system operations.
For example:
- registering the node in the cluster;
- kubelet operation;
- pulling container images from Amazon ECR;
- interacting with EC2 and VPC at a system level;
- EKS Pod Identity Agent operation;
- network component operation in some configurations.
Correct principle:
Node IAM Role
-> only system rights of the node
Application IAM Role
-> only application-specific rights of the particular service
Incorrect principle:
Node IAM Role
-> access to all S3, SQS, DynamoDB, and Secrets Manager
-> all Pods on the node potentially inherit common risk
Why application rights should not be granted to the node
If application IAM rights are in the node's role, it is impossible to reliably determine:
- which service uses a specific permission;
- how to revoke access from only one service;
- which Pod accessed the AWS API;
- how to limit the blast radius if a container is compromised.
Additionally, a Pod might try to obtain instance profile credentials via the EC2 Instance Metadata Service.
Therefore, it is necessary to:
- restrict Pods' access to IMDS;
- use IMDSv2;
- avoid running regular applications with
hostNetwork: trueunnecessarily; - avoid using privileged containers unnecessarily;
- move system add-ons to separate workload IAM roles where supported.
For example, AWS VPC CNI should ideally use a separate IAM role, rather than having its rights stored in the general node role.
Fargate Pod Execution Role
For AWS Fargate, the Fargate Pod Execution Role is used.
It is needed by the Fargate infrastructure, for example, to pull container images.
This role should not be considered an application role for the container.
For the application itself to access S3, SQS, DynamoDB, and other AWS services on Fargate, IRSA is used.
Does a separate IAM role provide complete isolation?
A separate ServiceAccount and a separate IAM role significantly reduce the blast radius, but they do not, by themselves, create an absolute security boundary.
Compromise of one service should not grant access to resources of other services if the following conditions are met simultaneously:
- The IAM policy is restricted to specific actions and resources;
- The trust policy is restricted to a specific ServiceAccount;
- The Pod does not have access to the node's role credentials;
- The ServiceAccount does not have excessive Kubernetes RBAC rights;
- The service cannot create Pods on behalf of another ServiceAccount;
- The service cannot modify RoleBinding and ClusterRoleBinding;
- The service cannot execute commands in Pods of other applications;
- The service cannot read other services' Secrets;
- The Pod is not running in privileged mode unnecessarily;
- Dangerous host mounts are not used;
- Network access is also restricted.
Therefore, the correct conclusion is:
Dedicated workload identity does not guarantee absolute isolation, but it significantly reduces the consequences of compromise and makes application rights explicit, verifiable, and independently revocable.
Network Access as an Additional Layer of Protection
IAM and Kubernetes RBAC are responsible for authorization.
They do not replace network segmentation.
Additionally, you should use:
- private Kubernetes API endpoint;
- restricting the public endpoint by CIDR or disabling it;
- Kubernetes NetworkPolicy;
- Security Groups for Pods, if they fit the architecture;
- separate namespaces and node groups for different trust levels;
- restricting egress access;
- VPC endpoints for AWS services, where necessary.
For example, an IAM policy might allow access only to one SQS queue, while a NetworkPolicy might prohibit the service from arbitrary connections to other applications within the cluster.
These are different layers of protection that complement each other.
Audit and Operations
The rights model must be not only minimal but also auditable.
Several sources are used for auditing.
AWS CloudTrail
CloudTrail allows you to see:
- changes to IAM roles and policies;
- creation and modification of Access Entries;
- creation of Pod Identity Associations;
- AWS API calls made on behalf of the workload IAM role;
- changes to EKS settings.
EKS control plane logs:
For the Kubernetes API, control plane logs should be enabled, primarily:
audit;authenticator;api.
Audit logs allow you to see which Kubernetes subject performed operations on cluster objects.
Infrastructure as Code:
The following should be managed through code:
- IAM Roles;
- IAM Policies;
- trust policies;
- EKS Access Entries;
- EKS Access Policies;
- Pod Identity Associations;
- Kubernetes ServiceAccounts;
- Roles and ClusterRoles;
- RoleBindings and ClusterRoleBindings;
- endpoint access settings;
- control plane logging.
This allows for:
- conducting code reviews;
- viewing change history;
- detecting manual changes;
- comparing rights between environments;
- automatically checking policies.
What to check regularly:
You should periodically answer the following questions:
- which IAM roles have access to the Kubernetes API;
- who has
cluster-admin; - which ServiceAccounts have ClusterRoleBinding;
- who can read Secrets;
- who can use
pods/exec; - who can create Pods with arbitrary ServiceAccount;
- which IAM policies contain wildcard actions or resources;
- are there unused Access Entries;
- are there unused IAM roles;
- which Pods can obtain node credentials;
- which roles are used by several unrelated services simultaneously;
- which rights can be removed based on CloudTrail and audit logs.
Recommended Final Model
Users and CI/CD:
IAM Role
-> EKS Access Entry
-> EKS Access Policy or Kubernetes RBAC
-> Kubernetes API
Application within Kubernetes:
Kubernetes ServiceAccount
-> Role or ClusterRole
-> RoleBinding or ClusterRoleBinding
-> Kubernetes API
Application in AWS:
Kubernetes ServiceAccount
-> EKS Pod Identity or IRSA
-> IAM Role
-> specific AWS resources
Worker node:
Node IAM Role
-> only system permissions
-> no application access to service data
Key Rules
- Each service runs under a dedicated ServiceAccount.
- Rights within Kubernetes are granted only via RBAC.
- By default, namespace-scoped Role and RoleBinding are used.
- ClusterRoleBinding is applied only when truly necessary.
- Application access to AWS is granted via EKS Pod Identity or IRSA.
- For new workloads on Linux EC2 nodes, EKS Pod Identity is preferred.
- For Fargate and Windows, IRSA is used.
- IAM policy is restricted to specific actions and resources.
- Trust policy is restricted to a specific workload identity.
- Application-specific AWS rights are not added to the Node IAM Role.
- Static AWS keys are not stored in Kubernetes.
- Access for people and CI/CD is managed via EKS Access Entries.
aws-authis not used as the primary mechanism for new clusters.- Administrative AWS API and Kubernetes API rights are separated.
- All rights are described via Infrastructure as Code.
- CloudTrail and Kubernetes audit logs are enabled and regularly analyzed.
Conclusion
The access model in Amazon EKS consists not of a single ServiceAccount-to-IAM-role link, but of several independent layers.
For applications, the central identifier is the Kubernetes ServiceAccount.
Through Kubernetes RBAC, it defines the service's rights within the cluster.
Through EKS Pod Identity or IRSA, it is linked to an IAM role and gains limited access to the AWS API.
Access for people and CI/CD to the Kubernetes API is managed separately through EKS Access Entries, EKS Access Policies, and Kubernetes RBAC.
Worker nodes retain their own IAM role, but this role should contain only system permissions and not be used for application access to business resources.
As a result, each subject receives only the set of rights necessary for its operation:
- an application - to its Kubernetes objects and AWS resources;
- an engineer - to the necessary namespaces;
- CI/CD - to specific deployment operations;
- a node - only to system functions;
- an administrator - to platform management within a separate role.
This model reduces the blast radius, simplifies auditing, eliminates static keys, and allows for independent revocation of rights for each service, user, or platform component.
Comments