Skip to content

Commit

Permalink
fix(ec2): launch template names in imdsv2 not unique across stacks (u…
Browse files Browse the repository at this point in the history
…nder feature flag) (#17766)

Fixes #17656

### Notes
Changes the name for the `LaunchTemplate` created in the aspect that enforces IMDSv2 on EC2 instances to a unique name.

Introduces a new feature flag (`@aws-cdk/aws-ec2:uniqueImdsv2TemplateName`) to change the launch template name.

### Testing
Added a unit test

----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
  • Loading branch information
jericht authored Jan 18, 2022
1 parent 91f3539 commit 2a80e4b
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 3 deletions.
11 changes: 8 additions & 3 deletions packages/@aws-cdk/aws-ec2/lib/aspects/require-imdsv2-aspect.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import * as cdk from '@aws-cdk/core';
import { FeatureFlags } from '@aws-cdk/core';
import * as cxapi from '@aws-cdk/cx-api';
import { CfnLaunchTemplate } from '../ec2.generated';
import { Instance } from '../instance';
import { LaunchTemplate } from '../launch-template';
Expand Down Expand Up @@ -83,17 +85,20 @@ export class InstanceRequireImdsv2Aspect extends RequireImdsv2Aspect {
return;
}

const name = `${node.node.id}LaunchTemplate`;
const launchTemplate = new CfnLaunchTemplate(node, 'LaunchTemplate', {
launchTemplateData: {
metadataOptions: {
httpTokens: 'required',
},
},
launchTemplateName: name,
});
if (FeatureFlags.of(node).isEnabled(cxapi.EC2_UNIQUE_IMDSV2_LAUNCH_TEMPLATE_NAME)) {
launchTemplate.launchTemplateName = cdk.Names.uniqueId(launchTemplate);
} else {
launchTemplate.launchTemplateName = `${node.node.id}LaunchTemplate`;
}
node.instance.launchTemplate = {
launchTemplateName: name,
launchTemplateName: launchTemplate.launchTemplateName,
version: launchTemplate.getAtt('LatestVersionNumber').toString(),
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import {
haveResourceLike,
} from '@aws-cdk/assert-internal';
import '@aws-cdk/assert-internal/jest';
import { testFutureBehavior, testLegacyBehavior } from '@aws-cdk/cdk-build-tools';
import * as cdk from '@aws-cdk/core';
import * as cxapi from '@aws-cdk/cx-api';
import {
CfnLaunchTemplate,
Instance,
Expand Down Expand Up @@ -135,6 +137,57 @@ describe('RequireImdsv2Aspect', () => {
trace: undefined,
});
});

testFutureBehavior('launch template name is unique with feature flag', { [cxapi.EC2_UNIQUE_IMDSV2_LAUNCH_TEMPLATE_NAME]: true }, cdk.App, (app2) => {
// GIVEN
const otherStack = new cdk.Stack(app2, 'OtherStack');
const otherVpc = new Vpc(otherStack, 'OtherVpc');
const otherInstance = new Instance(otherStack, 'OtherInstance', {
vpc: otherVpc,
instanceType: new InstanceType('t2.micro'),
machineImage: MachineImage.latestAmazonLinux(),
});
const imdsv2Stack = new cdk.Stack(app2, 'RequireImdsv2Stack');
const imdsv2Vpc = new Vpc(imdsv2Stack, 'Vpc');
const instance = new Instance(imdsv2Stack, 'Instance', {
vpc: imdsv2Vpc,
instanceType: new InstanceType('t2.micro'),
machineImage: MachineImage.latestAmazonLinux(),
});
const aspect = new InstanceRequireImdsv2Aspect();

// WHEN
cdk.Aspects.of(imdsv2Stack).add(aspect);
cdk.Aspects.of(otherStack).add(aspect);
app2.synth();

// THEN
const launchTemplate = instance.node.tryFindChild('LaunchTemplate') as LaunchTemplate;
const otherLaunchTemplate = otherInstance.node.tryFindChild('LaunchTemplate') as LaunchTemplate;
expect(launchTemplate).toBeDefined();
expect(otherLaunchTemplate).toBeDefined();
expect(launchTemplate.launchTemplateName !== otherLaunchTemplate.launchTemplateName);
});

testLegacyBehavior('launch template name uses legacy id without feature flag', cdk.App, (app2) => {
// GIVEN
const imdsv2Stack = new cdk.Stack(app2, 'RequireImdsv2Stack');
const imdsv2Vpc = new Vpc(imdsv2Stack, 'Vpc');
const instance = new Instance(imdsv2Stack, 'Instance', {
vpc: imdsv2Vpc,
instanceType: new InstanceType('t2.micro'),
machineImage: MachineImage.latestAmazonLinux(),
});
const aspect = new InstanceRequireImdsv2Aspect();

// WHEN
cdk.Aspects.of(imdsv2Stack).add(aspect);
app2.synth();

// THEN
const launchTemplate = instance.node.tryFindChild('LaunchTemplate') as LaunchTemplate;
expect(launchTemplate.launchTemplateName).toEqual(`${instance.node.id}LaunchTemplate`);
});
});

describe('LaunchTemplateRequireImdsv2Aspect', () => {
Expand Down
13 changes: 13 additions & 0 deletions packages/@aws-cdk/cx-api/lib/features.ts
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,17 @@ export const TARGET_PARTITIONS = '@aws-cdk/core:target-partitions';
*/
export const ECS_SERVICE_EXTENSIONS_ENABLE_DEFAULT_LOG_DRIVER = '@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver';

/**
* Enable this feature flag to have Launch Templates generated by the `InstanceRequireImdsv2Aspect` use unique names.
*
* Previously, the generated Launch Template names were only unique within a stack because they were based only on the
* `Instance` construct ID. If another stack that has an `Instance` with the same construct ID is deployed in the same
* account and region, the deployments would always fail as the generated Launch Template names were the same.
*
* The new implementation addresses this issue by generating the Launch Template name with the `Names.uniqueId` method.
*/
export const EC2_UNIQUE_IMDSV2_LAUNCH_TEMPLATE_NAME = '@aws-cdk/aws-ec2:uniqueImdsv2TemplateName';

/**
* This map includes context keys and values for feature flags that enable
* capabilities "from the future", which we could not introduce as the default
Expand Down Expand Up @@ -206,6 +217,7 @@ export const FUTURE_FLAGS: { [key: string]: boolean } = {
[LAMBDA_RECOGNIZE_VERSION_PROPS]: true,
[CLOUDFRONT_DEFAULT_SECURITY_POLICY_TLS_V1_2_2021]: true,
[ECS_SERVICE_EXTENSIONS_ENABLE_DEFAULT_LOG_DRIVER]: true,
[EC2_UNIQUE_IMDSV2_LAUNCH_TEMPLATE_NAME]: true,

// We will advertise this flag when the feature is complete
// [NEW_STYLE_STACK_SYNTHESIS_CONTEXT]: 'true',
Expand Down Expand Up @@ -245,6 +257,7 @@ const FUTURE_FLAGS_DEFAULTS: { [key: string]: boolean } = {
[LAMBDA_RECOGNIZE_VERSION_PROPS]: false,
[CLOUDFRONT_DEFAULT_SECURITY_POLICY_TLS_V1_2_2021]: false,
[ECS_SERVICE_EXTENSIONS_ENABLE_DEFAULT_LOG_DRIVER]: false,
[EC2_UNIQUE_IMDSV2_LAUNCH_TEMPLATE_NAME]: false,
};

export function futureFlagDefault(flag: string): boolean | undefined {
Expand Down

0 comments on commit 2a80e4b

Please sign in to comment.