Skip to content

Commit

Permalink
feat: EC2IMDSv2Enabled rule (#1265)
Browse files Browse the repository at this point in the history
  • Loading branch information
dontirun authored Apr 20, 2023
1 parent 492bfca commit d8736ba
Show file tree
Hide file tree
Showing 8 changed files with 432 additions and 15 deletions.
8 changes: 4 additions & 4 deletions RULES.md

Large diffs are not rendered by default.

11 changes: 10 additions & 1 deletion src/packs/hipaa-security.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import {
import {
EC2EBSInBackupPlan,
EC2EBSOptimizedInstance,
EC2IMDSv2Enabled,
EC2InstanceDetailedMonitoringEnabled,
EC2InstanceNoPublicIp,
EC2InstanceProfileAttached,
Expand Down Expand Up @@ -395,7 +396,15 @@ export class HIPAASecurityChecks extends NagPack {
node: node,
});
this.applyRule({
info: 'The EC2 instance does not have detailed monitoring enabled - (Control IDs: 164.312(b)).',
info: 'The EC2 instance does not have IMDSV2 (Instance Metadata Service Version 2) enabled - (Control IDs: 164.308(a)(3)(i), 164.308(a)(4)(ii)(A), 164.308(a)(4)(ii)(C), 164.312(a)(1)).',
explanation:
'Instance Metadata Service Version 2 (IMDSv2) helps protect access and control of Amazon Elastic Compute Cloud (Amazon EC2) instance metadata. The IMDSv2 method uses session-based controls. With IMDSv2, controls can be implemented to restrict changes to instance metadata.',
level: NagMessageLevel.ERROR,
rule: EC2IMDSv2Enabled,
node: node,
});
this.applyRule({
info: 'The EC2 instance does not have detailed monitoring enabled - (Control ID: 164.312(b)).',
explanation:
'Detailed monitoring provides additional monitoring information (such as 1-minute period graphs) on the AWS console.',
level: NagMessageLevel.ERROR,
Expand Down
9 changes: 9 additions & 0 deletions src/packs/nist-800-53-r4.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import {
} from '../rules/dynamodb';
import {
EC2EBSInBackupPlan,
EC2IMDSv2Enabled,
EC2InstanceDetailedMonitoringEnabled,
EC2InstanceNoPublicIp,
EC2InstancesInVPC,
Expand Down Expand Up @@ -335,6 +336,14 @@ export class NIST80053R4Checks extends NagPack {
rule: EC2EBSInBackupPlan,
node: node,
});
this.applyRule({
info: 'The EC2 instance does not have IMDSV2 (Instance Metadata Service Version 2) enabled - (Control ID: AC-6).',
explanation:
'Instance Metadata Service Version 2 (IMDSv2) helps protect access and control of Amazon Elastic Compute Cloud (Amazon EC2) instance metadata. The IMDSv2 method uses session-based controls. With IMDSv2, controls can be implemented to restrict changes to instance metadata.',
level: NagMessageLevel.ERROR,
rule: EC2IMDSv2Enabled,
node: node,
});
this.applyRule({
info: 'The EC2 instance does not have detailed monitoring enabled - (Control IDs: CA-7(a)(b), SI-4(2), SI-4(a)(b)(c)).',
explanation:
Expand Down
9 changes: 9 additions & 0 deletions src/packs/nist-800-53-r5.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import {
import {
EC2EBSInBackupPlan,
EC2EBSOptimizedInstance,
EC2IMDSv2Enabled,
EC2InstanceNoPublicIp,
EC2InstanceProfileAttached,
EC2InstancesInVPC,
Expand Down Expand Up @@ -361,6 +362,14 @@ export class NIST80053R5Checks extends NagPack {
rule: EC2EBSOptimizedInstance,
node: node,
});
this.applyRule({
info: 'The EC2 instance does not have IMDSV2 (Instance Metadata Service Version 2) enabled - (Control ID: AC-2(6), AC-3, AC-3(3)(a), AC-3(3)(b)(1), AC-3(3)(b)(2), AC-3(3)(b)(3), AC-3(3)(b)(4), AC-3(3)(b)(5), AC-3(3)(c), AC-3(3), AC-3(4)(a), AC-3(4)(b), AC-3(4)(c), AC-3(4)(d), AC-3(4)(e), AC-3(4), AC-3(7), AC-3(8), AC-3(12)(a), AC-3(13), AC-3(15)(a), AC-3(15)(b), AC-4(28), AC-6, AC-24, CM-5(1)(a), MP-2, SC-23(3)).',
explanation:
'Instance Metadata Service Version 2 (IMDSv2) helps protect access and control of Amazon Elastic Compute Cloud (Amazon EC2) instance metadata. The IMDSv2 method uses session-based controls. With IMDSv2, controls can be implemented to restrict changes to instance metadata.',
level: NagMessageLevel.ERROR,
rule: EC2IMDSv2Enabled,
node: node,
});
this.applyRule({
info: 'The EC2 instance is associated with a public IP address - (Control IDs: AC-2(6), AC-3, AC-3(7), AC-4(21), AC-6, AC-17b, AC-17(1), AC-17(1), AC-17(4)(a), AC-17(9), AC-17(10), MP-2, SC-7a, SC-7b, SC-7c, SC-7(2), SC-7(3), SC-7(7), SC-7(9)(a), SC-7(11), SC-7(12), SC-7(16), SC-7(20), SC-7(21), SC-7(24)(b), SC-7(25), SC-7(26), SC-7(27), SC-7(28), SC-25).',
explanation:
Expand Down
120 changes: 120 additions & 0 deletions src/rules/ec2/EC2IMDSv2Enabled.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
/*
Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
import { parse } from 'path';
import { CfnResource, Stack } from 'aws-cdk-lib';
import {
CfnAutoScalingGroup,
CfnLaunchConfiguration,
} from 'aws-cdk-lib/aws-autoscaling';
import { CfnInstance, CfnLaunchTemplate } from 'aws-cdk-lib/aws-ec2';
import { NagRuleCompliance, NagRules } from '../../nag-rules';

/**
* The EC2 Instance requires IMDsv2
* @param node the CfnResource to check
*/
export default Object.defineProperty(
(node: CfnResource): NagRuleCompliance => {
if (node instanceof CfnInstance) {
if (node.launchTemplate === undefined) {
return NagRuleCompliance.NON_COMPLIANT;
}
const instanceLaunchTemplate = Stack.of(node).resolve(
node.launchTemplate
);
for (const child of Stack.of(node).node.findAll()) {
if (child instanceof CfnLaunchTemplate) {
if (
isMatchingLaunchTemplate(
child,
instanceLaunchTemplate.launchTemplateName,
instanceLaunchTemplate.launchTemplateId
)
) {
return hasHttpTokens(child)
? NagRuleCompliance.COMPLIANT
: NagRuleCompliance.NON_COMPLIANT;
}
}
}
return NagRuleCompliance.NON_COMPLIANT;
} else if (node instanceof CfnAutoScalingGroup) {
if (node.launchTemplate) {
const nodeLaunchTemplate = Stack.of(node).resolve(node.launchTemplate);
for (const child of Stack.of(node).node.findAll()) {
if (child instanceof CfnLaunchTemplate) {
if (
isMatchingLaunchTemplate(
child,
nodeLaunchTemplate.launchTemplateName,
nodeLaunchTemplate.launchTemplateId
)
) {
return hasHttpTokens(child)
? NagRuleCompliance.COMPLIANT
: NagRuleCompliance.NON_COMPLIANT;
}
}
}
} else if (node.launchConfigurationName) {
for (const child of Stack.of(node).node.findAll()) {
if (child instanceof CfnLaunchConfiguration) {
if (
isMatchingLaunchConfiguration(child, node.launchConfigurationName)
) {
return hasHttpTokens(child)
? NagRuleCompliance.COMPLIANT
: NagRuleCompliance.NON_COMPLIANT;
}
}
}
}
return NagRuleCompliance.NON_COMPLIANT;
} else {
return NagRuleCompliance.NOT_APPLICABLE;
}
},
'name',
{ value: parse(__filename).name }
);

function isMatchingLaunchTemplate(
node: CfnLaunchTemplate,
launchTemplateName?: string | undefined,
launchTemplateId?: string | undefined
): boolean {
return (
launchTemplateName === node.launchTemplateName ||
launchTemplateId === NagRules.resolveResourceFromInstrinsic(node, node.ref)
);
}

function isMatchingLaunchConfiguration(
node: CfnLaunchConfiguration,
launchConfigurationName?: string | undefined
): boolean {
return (
launchConfigurationName === node.launchConfigurationName ||
NagRules.resolveResourceFromInstrinsic(node, launchConfigurationName) ===
NagRules.resolveResourceFromInstrinsic(node, node.ref)
);
}

function hasHttpTokens(
node: CfnLaunchTemplate | CfnLaunchConfiguration
): boolean {
let meta;
if (node instanceof CfnLaunchTemplate) {
const launchTemplateData = Stack.of(node).resolve(node.launchTemplateData);
meta = Stack.of(node).resolve(
launchTemplateData.metadataOptions
) as CfnLaunchTemplate.MetadataOptionsProperty;
} else if (node instanceof CfnLaunchConfiguration) {
meta = Stack.of(node).resolve(
node.metadataOptions
) as CfnLaunchConfiguration.MetadataOptionsProperty;
}
return meta?.httpTokens === 'required';
}
3 changes: 2 additions & 1 deletion src/rules/ec2/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,12 @@ SPDX-License-Identifier: Apache-2.0
export { default as EC2EBSInBackupPlan } from './EC2EBSInBackupPlan';
export { default as EC2EBSOptimizedInstance } from './EC2EBSOptimizedInstance';
export { default as EC2EBSVolumeEncrypted } from './EC2EBSVolumeEncrypted';
export { default as EC2IMDSv2Enabled } from './EC2IMDSv2Enabled';
export { default as EC2InstanceDetailedMonitoringEnabled } from './EC2InstanceDetailedMonitoringEnabled';
export { default as EC2InstanceNoPublicIp } from './EC2InstanceNoPublicIp';
export { default as EC2InstanceProfileAttached } from './EC2InstanceProfileAttached';
export { default as EC2InstancesInVPC } from './EC2InstancesInVPC';
export { default as EC2InstanceTerminationProtection } from './EC2InstanceTerminationProtection';
export { default as EC2InstancesInVPC } from './EC2InstancesInVPC';
export { default as EC2RestrictedCommonPorts } from './EC2RestrictedCommonPorts';
export { default as EC2RestrictedInbound } from './EC2RestrictedInbound';
export { default as EC2RestrictedSSH } from './EC2RestrictedSSH';
Expand Down
5 changes: 4 additions & 1 deletion test/Packs.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ import {
AwsSolutionsChecks,
HIPAASecurityChecks,
IApplyRule,
NagMessageLevel,
NIST80053R4Checks,
NIST80053R5Checks,
NagMessageLevel,
PCIDSS321Checks,
} from '../src';

Expand Down Expand Up @@ -228,6 +228,7 @@ describe('Check NagPack Details', () => {
'HIPAA.Security-DynamoDBPITREnabled',
'HIPAA.Security-EC2EBSInBackupPlan',
'HIPAA.Security-EC2EBSOptimizedInstance',
'HIPAA.Security-EC2IMDSv2Enabled',
'HIPAA.Security-EC2InstanceDetailedMonitoringEnabled',
'HIPAA.Security-EC2InstanceNoPublicIp',
'HIPAA.Security-EC2InstanceProfileAttached',
Expand Down Expand Up @@ -346,6 +347,7 @@ describe('Check NagPack Details', () => {
'NIST.800.53.R4-DynamoDBInBackupPlan',
'NIST.800.53.R4-DynamoDBPITREnabled',
'NIST.800.53.R4-EC2EBSInBackupPlan',
'NIST.800.53.R4-EC2IMDSv2Enabled',
'NIST.800.53.R4-EC2InstanceDetailedMonitoringEnabled',
'NIST.800.53.R4-EC2InstanceNoPublicIp',
'NIST.800.53.R4-EC2InstancesInVPC',
Expand Down Expand Up @@ -448,6 +450,7 @@ describe('Check NagPack Details', () => {
'NIST.800.53.R5-DynamoDBPITREnabled',
'NIST.800.53.R5-EC2EBSInBackupPlan',
'NIST.800.53.R5-EC2EBSOptimizedInstance',
'NIST.800.53.R5-EC2IMDSv2Enabled',
'NIST.800.53.R5-EC2InstanceNoPublicIp',
'NIST.800.53.R5-EC2InstanceProfileAttached',
'NIST.800.53.R5-EC2InstancesInVPC',
Expand Down
Loading

0 comments on commit d8736ba

Please sign in to comment.