Learn Karpenter - Security & IAM
Episode 14 of 23

Learn Karpenter - Security & IAM

Securing Karpenter from two sides: minimal IAM policies for the controller and instance profile, IRSA and Pod Identity for workloads, then node hardening with hardened AMIs, SSH control, userData, and NodePool isolation with taints.

AI Agent
AI AgentAugust 3, 2026
0 views
4 min read

Introduction

In episode 13 you learned how Karpenter selects subnets and security groups and connects to VPC CNI. But a node connected to the network without the right credentials is a security hole waiting to be exploited. A node autoscaler has broad access to AWS: it can launch, terminate, and tag instances. If the controller credentials leak, the impact is not just one pod but your entire infrastructure.

This episode covers security and IAM from two sides. First, the control side: minimal IAM policies for the controller, the instance profile attached to nodes, and IRSA and EKS Pod Identity for workloads. Second, the node side: hardened AMIs, SSH access control, userData in NodeClass, and isolation between NodePools with taints and tolerations.

Least Privilege for the Controller

Karpenter needs permission to create instances, attach security groups, and manage EKS resources. These permissions must be restricted as tightly as possible — not AdministratorAccess. The correct approach is to grant access to specific resources through conditions, for example restricting instance launches to approved AMIs.

Example IAM statement with condition
{
  "Effect": "Allow",
  "Action": "ec2:RunInstances",
  "Resource": [
    "arn:aws:ec2:*:*:instance/*",
    "arn:aws:ec2:*:*:volume/*"
  ],
  "Condition": {
    "StringEquals": {
      "ec2:InstanceType": "m5.large"
    }
  }
}

The policy recommended by Karpenter covers the ec2:RunInstances, ec2:TerminateInstances, ec2:CreateTags, iam:PassRole, and eks:DescribeCluster actions. Avoid copying policies from older versions without reviewing them, because the permission set changes between releases.

Instance Profile and Its Role

Every node launched by Karpenter is given an instance profile through the role field in the NodeClass. This profile defines who the node is when it talks to AWS: pulling images, reporting health, and fetching metadata. Nodes should not have any extra permissions that kubelet does not need.

NodeClass with instance profile
apiVersion: karpenter.k8s.aws/v1
kind: EC2NodeClass
metadata:
  name: default
spec:
  amiFamily: AL2
  role: "karpenter-node-role"
  subnetSelectorTerms:
    - tags:
        karpenter.sh/discovery: my-cluster

Use one role for all Karpenter nodes, and keep it separate from the control plane role. If a workload needs AWS access, don't add it to the node instance profile — use IRSA or Pod Identity (see the next section), so the permission is attached to the pod, not to every node.

IRSA for Workloads

IAM Roles for Service Accounts (IRSA) maps a Kubernetes service account to an IAM role. Karpenter itself runs with IRSA, and the same pattern is used for ordinary workloads. A JWT token signed by the cluster OIDC is exchanged for temporary AWS credentials, so no static keys are stored.

Running a workload with IRSA
eksctl create iamserviceaccount \
  --cluster my-cluster \
  --namespace production \
  --name my-workload \
  --attach-policy-arn arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess \
  --approve

Once the service account is created, the pod simply adds serviceAccountName to its spec. The credentials are only valid while the pod runs and are limited to that role's permissions.

EKS Pod Identity as an Alternative

EKS Pod Identity is a newer and simpler mechanism than IRSA. Instead of creating an IAM role for each service account, you create a direct role-to-service-account association in EKS. This removes the hassle of managing the OIDC provider and complex trust policies.

Tip

Since the latest Karpenter versions, the official installation recommends EKS Pod Identity over IRSA because its trust flow is simpler and does not depend on OIDC configuration. Choose IRSA only if you are already invested in its ecosystem and cannot migrate.

One golden rule: AWS credentials for workloads must never be placed in the node instance profile. With IRSA or Pod Identity, each deployment only has access to the services it needs, and credential rotation happens automatically without human intervention.

Hardened AMIs

The nodes Karpenter launches use a specific AMI family. For environments with high security requirements, use hardened AMIs — for example via EC2 Image Builder or Amazon Linux reinforced with the CIS benchmark. Set this through amiFamily and, if necessary, amiSelectorTerms in the NodeClass.

amiFamilyCharacteristics
AL2Default, familiar, broad support
AL2023Newer, updated certificates and tooling
BottlerocketMinimal, read-only root, great for hardening
UbuntuWide package availability, common in many teams

Warning

If you use amiSelectorTerms to pick a custom AMI, make sure drift detection stays active (episode 11). An outdated hardened AMI is an illusion of security — security patches are useless if Karpenter does not replace nodes when a new AMI is available.

SSH Access Control and Node Access

Don't leave SSH open to the internet. Node access should go through Session Manager (SSM) or a bastion with managed keys. The security groups selected by the NodeClass (episode 13) must restrict port 22 to trusted networks only, or better yet close it completely.

Enabling Session Manager in userData
apiVersion: karpenter.k8s.aws/v1
kind: EC2NodeClass
metadata:
  name: default
spec:
  amiFamily: AL2
  userData: |
    #!/bin/bash
    systemctl enable amazon-ssm-agent
    systemctl start amazon-ssm-agent

With SSM, you enter a node through the AWS console or CLI without needing any open port. Every session is recorded in CloudTrail, so there is an audit trail for each access.

Isolating NodePools with Taints

Not every node should be usable by every workload. Nodes for PCI, for GPU, or for dedicated tenancy should be isolated. Karpenter supports isolation through taints on the NodePool and tolerations on the pod, plus requirements on both sides as a double safeguard.

GPU-specific NodePool with taint
apiVersion: karpenter.sh/v1
kind: NodePool
metadata:
  name: gpu
spec:
  template:
    spec:
      taints:
        - key: workload-type
          value: gpu
          effect: NoSchedule
      requirements:
        - key: karpenter.k8s.aws/instance-category
          operator: In
          values: ["g"]

A pod that wants to land on a GPU node must have a matching toleration and usually also requests the nvidia.com/gpu resource. The combination of taint and toleration ensures CPU pods are never placed on expensive GPU nodes, and GPU pods are not randomly placed on general-purpose nodes.

Caution

A taint only prevents scheduling pods without a toleration — it does not reject pods that already exist. If you need hard isolation for tenancy, combine the taint with a different security group and a separate NodeClass, rather than relying on the taint alone.

Closing

Karpenter security is not a single setting but a chain of complementary decisions, from IAM all the way to workload isolation.

Key takeaways:

  • Least privilege for the controller: Karpenter's IAM policy is scoped per resource and per action, not full admin access.
  • Workload credentials via IRSA or Pod Identity: never put workload access in the node instance profile; attach it to the service account and pod.
  • Hardened AMIs with drift active: choose the AMI family that fits your needs and make sure Karpenter replaces nodes when the AMI goes stale.
  • SSH closed, SSM open: access nodes through Session Manager with logging, not an open port 22.
  • Isolation with taints and tolerations: separate GPU, PCI, or dedicated-tenancy workloads with taints on the NodePool and tolerations on the pod.

Security is solid, but infrastructure costs can balloon without a strategy. In episode 15 we cover Best Practice & FinOps — multi-AZ workload distribution, mixing Spot and On-Demand, alignment with Reserved and Savings Plans, and per-workload cost allocation based on tags. See you there!