AWS

error: You must be logged in to the server (Unauthorized) on EKS

Your IAM identity is not mapped to a Kubernetes user. How access entries replaced aws-auth, and how to recover a cluster nobody can get into.

hard fix5 min read

the aws error
error: You must be logged in to the server (Unauthorized)

error: You must be logged in to the server (the server has asked for the client to provide credentials)

Error from server (Forbidden): pods is forbidden: User "arn:aws:iam::123456789012:role/dev" cannot list resource "pods" in API group "" in the namespace "default"

Do this first3 steps

Run these in order. Each one tells you what its output means before you change anything.

  1. 1

    Confirm which IAM identity kubectl is presenting

    aws sts get-caller-identity && kubectl config current-context

    EKS maps the IAM identity to a Kubernetes user. If get-caller-identity shows a different role than the one you mapped, nothing else matters until that matches.

  2. 2

    Check which authentication mode the cluster uses

    aws eks describe-cluster --name prod --query 'cluster.accessConfig'

    API or API_AND_CONFIG_MAP means access entries are in play and are the supported way to grant access. CONFIG_MAP means the cluster still uses only the aws-auth ConfigMap.

  3. 3

    Grant yourself access with an access entry

    aws eks create-access-entry --cluster-name prod --principal-arn arn:aws:iam::123456789012:role/dev --type STANDARD

    The entry maps the IAM principal into the cluster. It grants nothing on its own until you associate an access policy or the principal is bound by RBAC, which is the second step.

All 8 sections

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.

MessageStage
You must be logged in to the server (Unauthorized)Authentication. Identity not mapped
Error from server (Forbidden): ... cannot listAuthorization. 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
}
ModeMapping source
CONFIG_MAPThe aws-auth ConfigMap only
API_AND_CONFIG_MAPBoth, access entries take precedence
APIAccess 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:

PolicyGrants
AmazonEKSClusterAdminPolicycluster-admin
AmazonEKSAdminPolicyAdmin within scoped namespaces
AmazonEKSEditPolicyEdit within scoped namespaces
AmazonEKSViewPolicyRead-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

  1. aws sts get-caller-identity. Map the role ARN, not the assumed-role session ARN.
  2. aws eks update-kubeconfig to regenerate the context.
  3. describe-cluster --query 'cluster.accessConfig' for the authentication mode.
  4. Access entries → create-access-entry, then associate-access-policy.
  5. aws-auth → back it up before editing. A bad edit locks everyone out.
  6. Migrate to API_AND_CONFIG_MAP. It is one-way.
  7. Locked out → find the cluster creator in CloudTrail, or enable access entries from the AWS API.
  8. Forbidden instead of Unauthorized → 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.

Reference and practice

Learn the underlying concept

Other AWS errors