Skip to main content
6 min read·1,122 words

Protecting Amazon EKS Node IAM Credentials from Pod IMDS Access

Every Amazon EKS worker node runs the Instance Metadata Service (IMDS) locally at 169.254.169.254, and that endpoint hands out the temporary IAM credentials of the node's instance profile. By default, a pod that is not on the host network can reach IMDS and retrieve those credentials, inheriting every permission attached to the node's IAM role — regardless of what the pod itself was granted.

A common misconception is that requiring IMDSv2 closes this hole. It does not. IMDSv2 requires the caller to perform a PUT request to obtain a session token before reading metadata, but a compromised pod can perform that PUT itself. The control that actually blocks a non-host-network pod is the metadata response hop limit set to 1. This article shows how to enforce that hop limit on managed node groups and on Karpenter-provisioned nodes, and how to remove pods' reliance on the node role entirely with IRSA or EKS Pod Identity.


1. IMDSv2 Alone Does Not Stop Pods

Issue: With IMDSv2 required but the metadata hop limit left at 2, a pod can still complete the IMDSv2 token exchange and read the node instance profile's credentials. IMDSv2 requires a PUT request that includes a TTL for the session token before metadata can be read, but nothing prevents a pod from making that request. On EKS, both IMDSv1 and IMDSv2 are enabled and the hop limit is changed to 2 on nodes provisioned by eksctl or with the official CloudFormation templates — so pods can reach IMDS out of the box.
Fix: Require IMDSv2 and set the metadata response hop limit to 1. The hop limit is the number of network hops the metadata PUT response is allowed to make; at 1, the response cannot traverse the extra hop into a pod that is not on the host network, while processes on the node itself still work.

The exploit looks like this from inside a compromised pod (credential values redacted):

# Obtain an IMDSv2 session token, then read the node role's credentials
TOKEN=$(curl -s -X PUT "http://169.254.169.254/latest/api/token" \
-H "X-aws-ec2-metadata-token-ttl-seconds: 21600")

curl -s -H "X-aws-ec2-metadata-token: $TOKEN" \
http://169.254.169.254/latest/meta-data/iam/security-credentials/<node-instance-role>
{
"Code" : "Success",
"Type" : "AWS-HMAC",
"AccessKeyId" : "ASIA...REDACTED",
"SecretAccessKey" : "REDACTED",
"Token" : "REDACTED"
}

The returned credentials carry the full permission set of the node's IAM role. On clusters that use Karpenter, that is the Karpenter node role, which typically holds permissions such as EC2 describe/create actions — a significant escalation for an attacker who only compromised a single application pod.


2. Set the Metadata Hop Limit to 1

Issue: The default hop limit on an EC2 instance is 1, but EKS raises it to 2 on nodes launched by eksctl and the official CloudFormation templates, which re-opens pod access to IMDS.
Fix: Set http-tokens to required and http-put-response-hop-limit to 1. Do not disable IMDS entirely — components such as the node termination handler rely on it.

Apply it to an existing instance:

aws ec2 modify-instance-metadata-options \
--instance-id <instance-id> \
--http-tokens required \
--http-put-response-hop-limit 1

Bake it into the node group's launch template so every new node inherits it:

resource "aws_launch_template" "workers" {
name = "eks-workers"

metadata_options {
http_endpoint = "enabled"
http_tokens = "required"
http_put_response_hop_limit = 1
instance_metadata_tags = "enabled"
}
}

3. Karpenter: Enforce metadataOptions in the EC2NodeClass

Karpenter does not use node-group launch templates; it configures instance metadata through the EC2NodeClass. Karpenter's documented defaults already set a safe posture — httpPutResponseHopLimit: 1 and httpTokens: required — which the Karpenter documentation describes as changed "to disable IMDS access from containers not on the host network."

Issue: A custom metadataOptions block on the EC2NodeClass can override the safe default and raise the hop limit back to 2, restoring pod access to the node role. Nodes provisioned before that default was in effect can also carry a hop limit of 2.
Fix: Set metadataOptions explicitly on every EC2NodeClass so the posture does not depend on the default, and never raise the hop limit above 1 unless a specific workload requires it.

apiVersion: karpenter.k8s.aws/v1
kind: EC2NodeClass
metadata:
name: default
spec:
metadataOptions:
httpEndpoint: enabled
httpProtocolIPv6: disabled
httpPutResponseHopLimit: 1
httpTokens: required

Caveat: A pod configured with hostNetwork: true always has IMDS access, regardless of the hop limit — it shares the node's network namespace. Restrict hostNetwork to trusted workloads (for example, with Pod Security Admission or an admission policy), and give those workloads scoped AWS permissions through the mechanisms in the next section rather than relying on the node role.


4. Remove Pod Reliance on the Node Role

Blocking IMDS stops pods from stealing the node role, but pods that legitimately need AWS access still must get credentials from somewhere. Do not solve that by widening the node role — scope permissions to the workload instead. On EKS, when you use IRSA or EKS Pod Identity, the pod's credential chain uses those credentials first, but the pod can still inherit the node instance profile's rights if IMDS is not restricted. The two controls are complementary: restrict IMDS and grant per-workload permissions.

IAM Roles for Service Accounts (IRSA) associates an IAM role with a Kubernetes service account through an OIDC identity provider. It is covered in depth in Cloud Metadata Service Mitigation.

EKS Pod Identity is a newer, simpler alternative that does not use OIDC identity providers. Instead of distributing AWS credentials to containers or using the EC2 instance's role, you associate an IAM role with a Kubernetes service account and configure pods to use that service account. Its credential isolation is explicit: when IMDS access is restricted, a pod's containers can only retrieve credentials for the IAM role associated with their service account, and never the node IAM role or the roles of other pods on the node.

Setting it up involves three steps: install the Amazon EKS Pod Identity Agent add-on (once per cluster), associate an IAM role with a service account, and configure the pod to use that service account. The IAM role's trust policy must allow the EKS Auth service:

{
"Principal": {
"Service": "pods.eks.amazonaws.com"
}
}

Version skew: EKS Pod Identity requires a cluster running Kubernetes 1.28 at platform version eks.4 or later, and Linux Amazon EC2 worker nodes. It is not available on AWS Fargate, Windows nodes, AWS Outposts, EKS Anywhere, or self-managed Kubernetes on EC2. Managed clusters that trail upstream may not meet the platform-version floor — confirm before planning a migration.


5. Audit and Verify

Find every instance whose hop limit is above 1 or that still allows IMDSv1:

aws ec2 describe-instances \
--query 'Reservations[].Instances[].{Id:InstanceId,Hop:MetadataOptions.HttpPutResponseHopLimit,Tokens:MetadataOptions.HttpTokens}' \
--output table

Inspect the metadata settings Karpenter is applying:

kubectl get ec2nodeclass -o yaml | grep -A4 'metadataOptions'

After applying the fix, confirm a non-host-network pod can no longer reach IMDS. The token request should time out instead of returning a token:

kubectl run imds-test --rm -it --image=curlimages/curl --restart=Never -- \
curl -s -X PUT "http://169.254.169.254/latest/api/token" \
-H "X-aws-ec2-metadata-token-ttl-seconds: 21600" --max-time 5

An empty response or timeout confirms the hop limit is enforced. A pod that uses EKS Pod Identity or IRSA will still obtain its scoped credentials through the default AWS credential chain, because those do not depend on IMDS.


Conclusion

Requiring IMDSv2 is necessary but not sufficient: without a hop limit of 1, EKS pods can still read the worker node's IAM credentials and inherit the node role's permissions. Enforce httpTokens: required and httpPutResponseHopLimit: 1 on managed node groups and on every Karpenter EC2NodeClass, restrict hostNetwork, and give workloads their own scoped AWS permissions through IRSA or EKS Pod Identity so no pod ever needs the node role.



References

This article is based on information from the following official sources:

  1. Amazon EKS Best Practices Guide — Identity and Access Management - Amazon Web Services
  2. Karpenter — EC2NodeClass - Karpenter
  3. Amazon EKS User Guide — EKS Pod Identity - Amazon Web Services
  4. Amazon EC2 User Guide — Configure the Instance Metadata Service options - Amazon Web Services