Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions packages/aws-cdk-lib/aws-eks-v2/lib/cluster.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1385,6 +1385,23 @@ export class Cluster extends ClusterBase {
throw new UnscopedValidationError('Private endpoint access requires the VPC to have DNS support and DNS hostnames enabled. Use `enableDnsHostnames: true` and `enableDnsSupport: true` when creating the VPC.');
}

// Validate that kubectl subnets are not isolated. Isolated subnets have no
// internet access by definition, so the kubectl Lambda will not be able to
// reach the EKS API, STS, or other AWS service endpoints required for
// kubectl operations (including the CoreDNS compute type patch).
// See https://github.com/aws/aws-cdk/issues/26613
const isolatedSubnetIds = new Set(this.vpc.isolatedSubnets.map(s => s.subnetId));
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as the aws-eks module — consider adding the this.vpc instanceof ec2.Vpc guard here for consistency with the DNS validation at line 1384 and to avoid blocking users with VPC endpoints on imported VPCs.

Per the AWS private clusters documentation, isolated subnets with VPC endpoints are a supported configuration.

const hasIsolatedSubnets = privateSubnets.some(s => isolatedSubnetIds.has(s.subnetId));
if (hasIsolatedSubnets) {
throw new ValidationError(
'Isolated subnets cannot be used for kubectl private subnets. Isolated subnets have no internet access, '
+ 'which is required for the kubectl Lambda to reach the EKS API, STS, and other AWS service endpoints. '
+ 'Use PRIVATE_WITH_EGRESS subnets with a NAT Gateway instead, or configure VPC endpoints for STS, EKS, and ECR. '
+ 'See https://docs.aws.amazon.com/eks/latest/userguide/private-clusters.html',
this,
);
}

kubectlSubnets = privateSubnets;

// the vpc must exist in order to properly delete the cluster (since we run `kubectl delete`).
Expand Down
51 changes: 51 additions & 0 deletions packages/aws-cdk-lib/aws-eks-v2/test/cluster.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1349,6 +1349,57 @@ describe('cluster', () => {
});
});

test('throws when kubectl subnets include isolated subnets', () => {
// GIVEN
const { stack } = testFixtureNoVpc();
const vpc = new ec2.Vpc(stack, 'Vpc', {
maxAzs: 2,
natGateways: 0,
subnetConfiguration: [
{ name: 'Isolated', subnetType: ec2.SubnetType.PRIVATE_ISOLATED, cidrMask: 24 },
],
});

// THEN
expect(() => {
new eks.Cluster(stack, 'Cluster', {
version: CLUSTER_VERSION,
vpc,
vpcSubnets: [{ subnetType: ec2.SubnetType.PRIVATE_ISOLATED }],
endpointAccess: eks.EndpointAccess.PRIVATE,
kubectlProviderOptions: {
kubectlLayer: new KubectlV33Layer(stack, 'kubectlLayer'),
},
prune: false,
});
}).toThrow(/Isolated subnets cannot be used for kubectl private subnets/);
});

test('does not throw when kubectl subnets are PRIVATE_WITH_EGRESS', () => {
// GIVEN
const { stack } = testFixtureNoVpc();
const vpc = new ec2.Vpc(stack, 'Vpc', {
maxAzs: 2,
natGateways: 1,
subnetConfiguration: [
{ name: 'Public', subnetType: ec2.SubnetType.PUBLIC, cidrMask: 24 },
{ name: 'Private', subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS, cidrMask: 24 },
],
});

// THEN - should not throw
new eks.Cluster(stack, 'Cluster', {
version: CLUSTER_VERSION,
vpc,
vpcSubnets: [{ subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS }],
endpointAccess: eks.EndpointAccess.PRIVATE,
kubectlProviderOptions: {
kubectlLayer: new KubectlV33Layer(stack, 'kubectlLayer'),
},
prune: false,
});
});

test('if openIDConnectProvider a new OpenIDConnectProvider resource is created and exposed', () => {
// GIVEN
const { stack } = testFixtureNoVpc();
Expand Down
17 changes: 17 additions & 0 deletions packages/aws-cdk-lib/aws-eks/lib/cluster.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1868,6 +1868,23 @@ export class Cluster extends ClusterBase {
throw new ValidationError('Private endpoint access requires the VPC to have DNS support and DNS hostnames enabled. Use `enableDnsHostnames: true` and `enableDnsSupport: true` when creating the VPC.', this);
}

// Validate that kubectl subnets are not isolated. Isolated subnets have no
// internet access by definition, so the kubectl Lambda will not be able to
// reach the EKS API, STS, or other AWS service endpoints required for
// kubectl operations (including the CoreDNS compute type patch).
// See https://github.com/aws/aws-cdk/issues/26613
const isolatedSubnetIds = new Set(this.vpc.isolatedSubnets.map(s => s.subnetId));
const hasIsolatedSubnets = privateSubnets.some(s => isolatedSubnetIds.has(s.subnetId));
if (hasIsolatedSubnets) {
throw new ValidationError(
'Isolated subnets cannot be used for kubectl private subnets. Isolated subnets have no internet access, '
+ 'which is required for the kubectl Lambda to reach the EKS API, STS, and other AWS service endpoints. '
+ 'Use PRIVATE_WITH_EGRESS subnets with a NAT Gateway instead, or configure VPC endpoints for STS, EKS, and ECR. '
+ 'See https://docs.aws.amazon.com/eks/latest/userguide/private-clusters.html',
this,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error message mentions "VPC endpoints for STS, EKS, and ECR" but per the AWS private clusters documentation, ECR also requires an S3 gateway endpoint (com.amazonaws.region-code.s3) for pulling container images. Consider updating to "STS, EKS, ECR, and S3" for completeness.

);
}
Copy link
Copy Markdown
Contributor

@vishaalmehrishi vishaalmehrishi Mar 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This validation throws unconditionally for isolated subnets, but the error message itself suggests VPC endpoints as a valid alternative. Users who have properly configured VPC endpoints for STS, EKS, ECR, and S3 in their isolated subnets will be blocked at synth time with no workaround.

The DNS validation directly above (line 1867) uses this.vpc instanceof ec2.Vpc to avoid false positives with imported VPCs. Consider applying the same guard here — when the VPC is CDK-created, we know isolated subnets have no egress; when it's imported, we can't be sure.

Per the AWS private clusters documentation, isolated subnets with VPC endpoints are a supported configuration.

if (this.vpc instanceof ec2.Vpc) {
  const isolatedSubnetIds = new Set(this.vpc.isolatedSubnets.map(s => s.subnetId));
  const hasIsolatedSubnets = privateSubnets.some(s => isolatedSubnetIds.has(s.subnetId));
  if (hasIsolatedSubnets) {
    throw new ValidationError(
      'Isolated subnets cannot be used for kubectl private subnets. ...',
      this,
    );
  }
}


this.kubectlPrivateSubnets = privateSubnets;

// the vpc must exist in order to properly delete the cluster (since we run `kubectl delete`).
Expand Down
47 changes: 47 additions & 0 deletions packages/aws-cdk-lib/aws-eks/test/cluster.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2328,6 +2328,53 @@ describe('cluster', () => {
});
});

test('throws when kubectl private subnets include isolated subnets', () => {
// GIVEN
const { stack } = testFixtureNoVpc();
const vpc = new ec2.Vpc(stack, 'Vpc', {
maxAzs: 2,
natGateways: 0,
subnetConfiguration: [
{ name: 'Isolated', subnetType: ec2.SubnetType.PRIVATE_ISOLATED, cidrMask: 24 },
],
});

// THEN
expect(() => {
new eks.Cluster(stack, 'Cluster', {
version: CLUSTER_VERSION,
vpc,
vpcSubnets: [{ subnetType: ec2.SubnetType.PRIVATE_ISOLATED }],
endpointAccess: eks.EndpointAccess.PRIVATE,
kubectlLayer: new KubectlV31Layer(stack, 'KubectlLayer'),
prune: false,
});
}).toThrow(/Isolated subnets cannot be used for kubectl private subnets/);
});

test('does not throw when kubectl private subnets are PRIVATE_WITH_EGRESS', () => {
// GIVEN
const { stack } = testFixtureNoVpc();
const vpc = new ec2.Vpc(stack, 'Vpc', {
maxAzs: 2,
natGateways: 1,
subnetConfiguration: [
{ name: 'Public', subnetType: ec2.SubnetType.PUBLIC, cidrMask: 24 },
{ name: 'Private', subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS, cidrMask: 24 },
],
});

// THEN - should not throw
new eks.Cluster(stack, 'Cluster', {
version: CLUSTER_VERSION,
vpc,
vpcSubnets: [{ subnetType: ec2.SubnetType.PRIVATE_WITH_EGRESS }],
endpointAccess: eks.EndpointAccess.PRIVATE,
kubectlLayer: new KubectlV31Layer(stack, 'KubectlLayer'),
prune: false,
});
});

test('if openIDConnectProvider a new OpenIDConnectProvider resource is created and exposed', () => {
// GIVEN
const { stack } = testFixtureNoVpc();
Expand Down
Loading