EKS authenticates with IAM and authorises with Kubernetes RBAC. Unauthorized means the first step failed: your IAM identity is not mapped to any Kubernetes identity, so the API server does not know who you are.
That is different from Forbidden, which means it does know you and RBAC refused the action.
| Message | Stage |
|---|---|
You must be logged in to the server (Unauthorized) | Authentication. Identity not mapped |
Error from server (Forbidden): ... cannot list | Authorization. Mapped, but no RBAC grant |
Check who you are
aws sts get-caller-identity
kubectl config current-context
{ "Arn": "arn:aws:sts::123456789012:assumed-role/dev/session-name" }
EKS matches on the role ARN, arn:aws:iam::123456789012:role/dev, not the assumed-role session ARN. Mapping the session form does not work.
Regenerate the kubeconfig if the context looks wrong:
aws eks update-kubeconfig --region eu-west-1 --name prod
Add --role-arn if you need to assume a role first:
aws eks update-kubeconfig --region eu-west-1 --name prod \
--role-arn arn:aws:iam::123456789012:role/eks-admin
Which mechanism does the cluster use?
aws eks describe-cluster --name prod --query 'cluster.accessConfig'
{
"authenticationMode": "API_AND_CONFIG_MAP",
"bootstrapClusterCreatorAdminPermissions": true
}
| Mode | Mapping source |
|---|---|
CONFIG_MAP | The aws-auth ConfigMap only |
API_AND_CONFIG_MAP | Both, access entries take precedence |
API | Access entries only |
Access entries are the newer mechanism and the supported one. They are an AWS API rather than a ConfigMap, which means they are auditable in CloudTrail and cannot be broken by a bad YAML edit.
Access entries
aws eks create-access-entry \
--cluster-name prod \
--principal-arn arn:aws:iam::123456789012:role/dev \
--type STANDARD
The entry maps the principal. It grants nothing yet. Associate a policy:
aws eks associate-access-policy \
--cluster-name prod \
--principal-arn arn:aws:iam::123456789012:role/dev \
--policy-arn arn:aws:eks::aws:cluster-access-policy/AmazonEKSViewPolicy \
--access-scope type=cluster
The built-in policies:
| Policy | Grants |
|---|---|
AmazonEKSClusterAdminPolicy | cluster-admin |
AmazonEKSAdminPolicy | Admin within scoped namespaces |
AmazonEKSEditPolicy | Edit within scoped namespaces |
AmazonEKSViewPolicy | Read-only |
Scope to namespaces where appropriate:
--access-scope type=namespace,namespaces=team-a,team-b
You can also map to Kubernetes groups and use your own RBAC instead:
aws eks create-access-entry --cluster-name prod \
--principal-arn arn:aws:iam::123456789012:role/dev \
--kubernetes-groups dev-team
Then bind dev-team with an ordinary RoleBinding.
aws eks list-access-entries --cluster-name prod
The aws-auth ConfigMap
On older clusters:
kubectl -n kube-system get configmap aws-auth -o yaml
data:
mapRoles: |
- rolearn: arn:aws:iam::123456789012:role/eks-node-role
username: system:node:{{EC2PrivateDNSName}}
groups: [system:bootstrappers, system:nodes]
- rolearn: arn:aws:iam::123456789012:role/dev
username: dev
groups: [system:masters]
Editing it is genuinely risky. A malformed entry or a deleted node role mapping locks everyone out and stops nodes joining. Always back it up first:
kubectl -n kube-system get configmap aws-auth -o yaml > aws-auth-backup.yaml
Migrating to access entries is worth doing precisely because it removes this hazard:
aws eks update-cluster-config --name prod \
--access-config authenticationMode=API_AND_CONFIG_MAP
That change is one-way: you cannot go back to CONFIG_MAP.
Locked out entirely
The identity that created the cluster gets implicit cluster-admin, if bootstrapClusterCreatorAdminPermissions was true. Assume that identity:
aws cloudtrail lookup-events \
--lookup-attributes AttributeKey=EventName,AttributeValue=CreateCluster \
--query 'Events[0].Username'
If that identity is gone and the cluster is in CONFIG_MAP mode, switch it to allow access entries, which does not require cluster access:
aws eks update-cluster-config --name prod \
--access-config authenticationMode=API_AND_CONFIG_MAP
aws eks create-access-entry --cluster-name prod \
--principal-arn arn:aws:iam::123456789012:role/breakglass --type STANDARD
aws eks associate-access-policy --cluster-name prod \
--principal-arn arn:aws:iam::123456789012:role/breakglass \
--policy-arn arn:aws:eks::aws:cluster-access-policy/AmazonEKSClusterAdminPolicy \
--access-scope type=cluster
This is the main practical reason to enable access entries on every cluster: recovery no longer depends on already having access.
Forbidden rather than Unauthorized
Error from server (Forbidden): pods is forbidden: User "..." cannot list resource "pods"
Authentication succeeded. This is ordinary Kubernetes RBAC, and the fix is a Role and RoleBinding or a broader access policy.
A checklist
aws sts get-caller-identity. Map the role ARN, not the assumed-role session ARN.aws eks update-kubeconfigto regenerate the context.describe-cluster --query 'cluster.accessConfig'for the authentication mode.- Access entries →
create-access-entry, thenassociate-access-policy. aws-auth→ back it up before editing. A bad edit locks everyone out.- Migrate to
API_AND_CONFIG_MAP. It is one-way. - Locked out → find the cluster creator in CloudTrail, or enable access entries from the AWS API.
Forbiddeninstead ofUnauthorized→ you are authenticated; this is RBAC.
Frequently Asked Questions
What is the difference between Unauthorized and Forbidden on EKS?
Unauthorized is an authentication failure: EKS could not map your IAM identity to any Kubernetes user, so it does not know who you are. Forbidden is an authorization failure: it knows exactly who you are and Kubernetes RBAC refused the action, and the message names the identity, verb and resource. The first is fixed with an access entry or an aws-auth mapping, and the second with a Role and RoleBinding.
Should I use access entries or the aws-auth ConfigMap?
Access entries, for anything new. They are a proper AWS API, so changes are audited in CloudTrail, controlled by IAM permissions, and cannot be broken by a malformed YAML edit. The aws-auth ConfigMap has the significant hazard that a bad entry can lock every user out and stop nodes joining simultaneously. Switch with update-cluster-config --access-config authenticationMode=API_AND_CONFIG_MAP, noting that the change cannot be reversed.
How do I recover from being locked out of an EKS cluster?
If the cluster was created with bootstrapClusterCreatorAdminPermissions, the creating identity has implicit cluster-admin; CloudTrail's CreateCluster event tells you who that was. If that identity no longer exists, switch the cluster to API_AND_CONFIG_MAP and create an access entry for a role you control. Both of those are AWS API calls that need only IAM permissions, not cluster access, which is precisely why enabling access entries everywhere is worth doing in advance.
Why does my role mapping not match?
Almost always because the assumed-role session ARN was used rather than the role ARN. aws sts get-caller-identity returns arn:aws:sts::123456789012:assumed-role/dev/session-name, and EKS matches on arn:aws:iam::123456789012:role/dev. Paths matter too: a role created under a path is arn:aws:iam::123456789012:role/team/dev, and omitting the path means no match. Copy the ARN from aws iam get-role rather than from the STS output.
Does an access entry grant permissions by itself?
No. Creating an access entry maps an IAM principal into the cluster so it can authenticate, and that is all. You then either associate a built-in access policy such as AmazonEKSViewPolicy, optionally scoped to specific namespaces, or map the principal into Kubernetes groups with --kubernetes-groups and bind those groups with your own RBAC. Skipping the second step produces the Forbidden error instead, which at least confirms the mapping worked.