The node's IAM role isn't allowed into the cluster
Unauthorized NodeCreationFailure
What you see
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
ip-10-0-1-23.ec2.internal Ready <none> 12d v1.33.5-eks-113cf36
# the new instance is running in EC2, but never shows up here# on the instance: journalctl -u kubelet
kubelet[2041]: E1102 09:14:07 "Unable to register node with API server" err="Unauthorized" node="ip-10-0-2-41.ec2.internal"
kubelet[2041]: E1102 09:14:09 "Failed to watch" err="failed to list *v1.Service: Unauthorized"$ aws eks describe-nodegroup --cluster-name prod --nodegroup-name general --query 'nodegroup.health.issues'
[ { "code": "NodeCreationFailure", "message": "Instances failed to join the kubernetes cluster", "resourceIds": ["i-0b2c3d4e5f6a78901"] } ]Unauthorized means the request reached the API server and the node's identity was rejected. The network path is fine; the cluster just doesn't know this IAM role. A managed node group reports NodeCreationFailure when its instances haven't joined within 15 minutes, for this or a network cause (card 2).
Confirm
# Which IAM role does the instance actually use?
aws ec2 describe-instances --instance-ids i-0b2c3d4e5f6a78901 --query 'Reservations[].Instances[].IamInstanceProfile.Arn'
aws iam get-instance-profile --instance-profile-name <profile-name> --query 'InstanceProfile.Roles[].Arn'
# How does the cluster map IAM to Kubernetes? API, API_AND_CONFIG_MAP or CONFIG_MAP
aws eks describe-cluster --name prod --query 'cluster.accessConfig.authenticationMode'
# Is there an access entry for that role, of type EC2_LINUX?
aws eks describe-access-entry --cluster-name prod --principal-arn arn:aws:iam::111122223333:role/eks-node-role --query 'accessEntry.type'
# Or, with CONFIG_MAP: is the role mapped in aws-auth?
kubectl get configmap aws-auth -n kube-system -o yamlThe principal must be the role ARN (:role/eks-node-role), not the instance profile ARN. In aws-auth, a role with a path (:role/eks/nodes/eks-node-role) must be written without the path.
Fix
- Access entries (
APIorAPI_AND_CONFIG_MAP): create one for the node role:aws eks create-access-entry --cluster-name prod --principal-arn arn:aws:iam::111122223333:role/eks-node-role --type EC2_LINUX. AnEC2_LINUXentry puts the node insystem:nodesfor you; don't attach an access policy to it. Use typeEC2instead for Auto Mode custom NodeClasses and for self-managed AL2023 nodes that are named by instance ID. aws-authonly (CONFIG_MAP): map the role witheksctl create iamidentitymapping --cluster prod --arn arn:aws:iam::111122223333:role/eks-node-role --group system:bootstrappers,system:nodes --username 'system:node:{{EC2PrivateDNSName}}', or restore the entry someone removed.- Managed node groups create their own access entry (or
aws-authentry) when the group is created. If it was deleted, for example while migrating fromaws-authto access entries, recreate it as above; the role stays the same. - Mapping is right, still
Unauthorized: check the node's clock. Its token is a signed AWS STS request that is valid only for a short window, so a clock that is far off is rejected. EKS AMIs sync time with chrony and the Amazon Time Sync Service, so this is rare.
Prevent
- Move to access entries and manage them in the same IaC as the node groups, so a node role and its entry are created and deleted together.
- Don't hand-edit
aws-auth; a YAML mistake there can lock out every node at once.
Understand it
Let AWS check it for you: the AWSSupport-TroubleshootEKSWorkerNode Systems Manager runbook takes the cluster name and the instance ID, and checks the common reasons a node can't join: the node role's policies, security groups, network ACLs, routes and NAT gateways, VPC endpoints, DHCP options and the AMI. Its on-node checks run through Systems Manager, so the instance must be running with the SSM agent. It doesn't check access entries or aws-auth; do that with the commands above.