Skip to content

aws-eks: IPv6 nodegroup IAM grant hardcodes the 'aws' partition, so the CNI permission is dead in GovCloud #38552

Description

@Adityaj0

Describe the bug

When a nodegroup is created for an IPv6 cluster, Nodegroup grants the node role the IPv6 address-assignment permissions the VPC CNI needs. The resource ARN for that statement is a hardcoded string in the aws partition:

aws-eks/lib/managed-nodegroup.ts#L543-L552

if (props.cluster.ipFamily == IpFamily.IP_V6) {
  ngRole.addToPrincipalPolicy(new PolicyStatement({
    // eslint-disable-next-line @cdklabs/no-literal-partition
    resources: ['arn:aws:ec2:*:*:network-interface/*'],
    actions: [
      'ec2:AssignIpv6Addresses',
      'ec2:UnassignIpv6Addresses',
    ],
  }));
}

The same block exists verbatim in aws-eks-v2/lib/managed-nodegroup.ts#L519-L528.

An ARN naming the aws partition can never match a resource in aws-us-gov or aws-cn, so in those partitions the statement is a no-op: it grants nothing, and it does so silently — synthesis succeeds, cdk deploy succeeds, and the nodes come up without the permission the CNI needs to assign IPv6 addresses to pods.

Synthesizing the same app into three regions shows the ARN never changes, while CDK's own partition-aware ARNs in the same template do:

stack region partition Resource in the IPv6 statement a sibling managed-policy ARN in the same template
us-east-1 aws arn:aws:ec2:*:*:network-interface/* {"Fn::Join":["",["arn:",{"Ref":"AWS::Partition"},":iam::aws:policy/..."]]}
us-gov-west-1 aws-us-gov arn:aws:ec2:*:*:network-interface/* {"Fn::Join":["",["arn:",{"Ref":"AWS::Partition"},":iam::aws:policy/..."]]}
cn-north-1 aws-cn arn:aws:ec2:*:*:network-interface/* {"Fn::Join":["",["arn:",{"Ref":"AWS::Partition"},":iam::aws:policy/..."]]}

The eslint-disable for @cdklabs/no-literal-partition sits directly above the line, so the rule that exists to catch exactly this was suppressed rather than satisfied. AGENTS.md § ARN Construction states the rule this breaks:

Use Stack.of(scope).formatArn() — never hardcode ARN strings

Scope of the impact. AWS documents that ipv6 cannot be specified for clusters in China Regions, so aws-cn is not reachable in practice today. GovCloud (US) carries no such documented restriction, and that is where this bites: an IPv6 EKS cluster in us-gov-west-1 gets a node role whose IPv6 grant does nothing. The symptom is pods that never receive an address, with the CNI unable to call ec2:AssignIpv6Addresses.

Regression Issue

  • Select this option if this issue appears to be a regression.

Last Known Working CDK Library Version

No response

Expected Behavior

The grant should be scoped to the partition the stack is deployed into, the same way every other ARN CDK emits is:

{
  "Action": ["ec2:AssignIpv6Addresses", "ec2:UnassignIpv6Addresses"],
  "Effect": "Allow",
  "Resource": { "Fn::Join": ["", ["arn:", { "Ref": "AWS::Partition" }, ":ec2:*:*:network-interface/*"]] }
}

or, for an app with @aws-cdk/core:enablePartitionLiterals and a concrete region, the resolved literal for that partition — arn:aws-us-gov:ec2:*:*:network-interface/*.

Current Behavior

Resource is the literal arn:aws:ec2:*:*:network-interface/* in every partition. In aws-us-gov and aws-cn the statement matches no resource, so the two ec2:*Ipv6Addresses actions are effectively not granted. Nothing warns at synth or deploy time.

Reproduction Steps

import { App, Stack } from 'aws-cdk-lib';
import * as eks from 'aws-cdk-lib/aws-eks';
import * as lambda from 'aws-cdk-lib/aws-lambda';

const app = new App();
const stack = new Stack(app, 'S', { env: { account: '123456789012', region: 'us-gov-west-1' } });

const cluster = new eks.Cluster(stack, 'C', {
  version: eks.KubernetesVersion.V1_32,
  ipFamily: eks.IpFamily.IP_V6,
  defaultCapacity: 0,
  kubectlLayer: new lambda.LayerVersion(stack, 'KubectlLayer', { code: lambda.Code.fromAsset('layer') }),
});
cluster.addNodegroupCapacity('NG', {});

const tpl = app.synth().getStackByName('S').template;
for (const res of Object.values(tpl.Resources) as any[]) {
  if (res.Type !== 'AWS::IAM::Policy') continue;
  for (const s of res.Properties.PolicyDocument.Statement) {
    if (JSON.stringify(s).includes('network-interface')) console.log(JSON.stringify(s, null, 2));
  }
}

Output (identical for cn-north-1):

{
  "Action": ["ec2:AssignIpv6Addresses", "ec2:UnassignIpv6Addresses"],
  "Effect": "Allow",
  "Resource": "arn:aws:ec2:*:*:network-interface/*"
}

Possible Solution

Format the ARN instead of hardcoding it, and drop the eslint-disable:

if (props.cluster.ipFamily == IpFamily.IP_V6) {
  ngRole.addToPrincipalPolicy(new PolicyStatement({
    resources: [Stack.of(this).formatArn({
      service: 'ec2',
      region: '*',
      account: '*',
      resource: 'network-interface',
      resourceName: '*',
      arnFormat: ArnFormat.SLASH_RESOURCE_NAME,
    })],
    actions: [
      'ec2:AssignIpv6Addresses',
      'ec2:UnassignIpv6Addresses',
    ],
  }));
}

The same change applies to the aws-eks-v2 copy. In the aws partition the rendered ARN is unchanged in meaning ({"Ref":"AWS::Partition"} resolves to aws), so this only widens correctness — but note it does change the literal string in the template, so existing integ snapshots covering IPv6 nodegroups will need updating.

aws-eks/lib/alb-controller.ts already solves the identical problem for the ALB controller policy by rewriting arn:aws: to arn:${Aws.PARTITION}:, which is a useful precedent for how the maintainers have handled this before.

Additional Information/Context

Same class of defect as #33212 (hardcoded partition in the S3 auto-delete-objects handler) and tracked generally by #28474.

I have not verified the runtime symptom on a live GovCloud cluster — the report is from the synthesized template plus the IAM semantics of a cross-partition resource ARN. If EKS IPv6 turns out to be unavailable in every non-aws partition, the ARN is still wrong and still worth fixing, but the practical impact would be limited to the moment it becomes available.

AWS CDK Library version (aws-cdk-lib)

2.264.0

AWS CDK CLI version

N/A (synthesis-time, framework only)

Node.js Version

v24.1.0

OS

macOS 15 (Darwin 25.2.0)

Language

TypeScript

Language Version

No response

Other information

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    @aws-cdk/aws-eksRelated to Amazon Elastic Kubernetes ServicebugThis issue is a bug.effort/smallSmall work item – less than a day of effortp2

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions