Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(aws-autoscaling): add flag and aspect to require imdsv2 #16052

Merged
merged 6 commits into from
Oct 19, 2021

Conversation

jericht
Copy link
Contributor

@jericht jericht commented Aug 13, 2021

Partially fixes: #5137
Related PR: #16051

Note: I have some concerns about duplicated code between this and the above linked PR. Please see that PR for more details.

Changes

Adds an aspect that can enable/disable IMDSv1 on AutoScalingGroups

Testing

Added unit tests


By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license

@gitpod-io
Copy link

gitpod-io bot commented Aug 13, 2021

@jericht jericht force-pushed the jericht/autoscaling_imds_aspect branch from 1689715 to ff3f13d Compare August 13, 2021 23:25
@peterwoodworth peterwoodworth added effort/small Small work item – less than a day of effort @aws-cdk/aws-autoscaling Related to Amazon EC2 Auto Scaling p2 labels Aug 19, 2021
@jericht jericht force-pushed the jericht/autoscaling_imds_aspect branch from ff3f13d to 8d01baa Compare September 9, 2021 19:40
@jericht
Copy link
Contributor Author

jericht commented Sep 9, 2021

@comcalvi I just rebased this and it is ready for review. Will you be able to take a look at it?

@comcalvi
Copy link
Contributor

@jericht Thanks for the PR! I'll definitely take a look at this.

Copy link
Contributor

@comcalvi comcalvi left a comment

Choose a reason for hiding this comment

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

Overall, looks great! Thank you for your patience in this review. I have some comments below; in addition, we should add a property to autoscaling groups to enable / disable imdsv1.

Comment on lines 15 to 16
* Whether warning annotations from this Aspect should be suppressed or not.
* @default false
Copy link
Contributor

Choose a reason for hiding this comment

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

Should be a line with only an * on it between these two. Also, @default <value> clauses should be structured @default - <value>.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated

Comment on lines 61 to 54
/* istanbul ignore next */
if (node === undefined || !(node instanceof AutoScalingGroup)) {
return;
}
Copy link
Contributor

@comcalvi comcalvi Sep 17, 2021

Choose a reason for hiding this comment

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

The change in this comment will make the return line covered. In addition, the node === undefined check is unnecessary, as node is of type IConstruct and so can never be undefined.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sounds good, updated

Comment on lines 79 to 83
function implementsIResolvable(obj: any): boolean {
return 'resolve' in obj && typeof(obj.resolve) === 'function' &&
'creationStack' in obj && Array.isArray(obj.creationStack) &&
'toString' in obj && typeof(obj.toString) === 'function';
}
Copy link
Contributor

Choose a reason for hiding this comment

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

We have a function that does this in core which also checks for null and undefined 🙂.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh nice, thanks! Updated

}

const launchConfig = node.node.tryFindChild('LaunchConfig') as CfnLaunchConfiguration;
if (launchConfig.metadataOptions !== undefined && implementsIResolvable(launchConfig.metadataOptions)) {
Copy link
Contributor

@comcalvi comcalvi Sep 17, 2021

Choose a reason for hiding this comment

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

the launchConfig.metadataOptions !== undefined can be removed; see comment above.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed

Comment on lines 36 to 30
* Adds a warning annotation to a node, unless `suppressWarnings` is true.
* @param node The scope to add the warning to.
Copy link
Contributor

Choose a reason for hiding this comment

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

Should be a line with only an * on it between these two.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated

Comment on lines 63 to 45
// WHEN
aspect.visit(asg);

// THEN
expect(asg.node.metadataEntry).toContainEqual({
data: expect.stringContaining('CfnLaunchConfiguration.MetadataOptions field is a CDK token.'),
type: 'aws:cdk:warning',
trace: undefined,
});
expectCDK(stack).notTo(haveResourceLike('AWS::AutoScaling::LaunchConfiguration', {
MetadataOptions: {
HttpTokens: 'required',
},
}));
Copy link
Contributor

@comcalvi comcalvi Sep 17, 2021

Choose a reason for hiding this comment

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

aspect.visit() should not be called explicitly here. Instead, use the aspects API (and swap the expect() calls to ensure synthesize() is properly called, this will properly call the visit() function from within synthesize()):

Suggested change
// WHEN
aspect.visit(asg);
// THEN
expect(asg.node.metadataEntry).toContainEqual({
data: expect.stringContaining('CfnLaunchConfiguration.MetadataOptions field is a CDK token.'),
type: 'aws:cdk:warning',
trace: undefined,
});
expectCDK(stack).notTo(haveResourceLike('AWS::AutoScaling::LaunchConfiguration', {
MetadataOptions: {
HttpTokens: 'required',
},
}));
// WHEN
cdk.Aspects.of(stack).add(aspect);
// THEN
expectCDK(stack).notTo(haveResourceLike('AWS::AutoScaling::LaunchConfiguration', {
MetadataOptions: {
HttpTokens: 'required',
},
}));
expect(asg.node.metadataEntry).toContainEqual({
data: expect.stringContaining('CfnLaunchConfiguration.MetadataOptions field is a CDK token.'),
type: 'aws:cdk:warning',
trace: undefined,
});

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sounds good, updated

const aspect = new AutoScalingGroupImdsAspect({ enableImdsV1 });

// WHEN
aspect.visit(asg);
Copy link
Contributor

Choose a reason for hiding this comment

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

Similarly to above:

Suggested change
aspect.visit(asg);
cdk.Aspects.of(stack).add(aspect);

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated

@jericht jericht force-pushed the jericht/autoscaling_imds_aspect branch from 8d01baa to 6160686 Compare September 27, 2021 23:38
@mergify mergify bot dismissed comcalvi’s stale review September 27, 2021 23:38

Pull request has been modified.

@jericht
Copy link
Contributor Author

jericht commented Sep 28, 2021

@comcalvi I also wanted to get an opinion on my comments about the duplicated code between this PR and the ec2 PR. Do you think what we have right now is fine or should we reorganize these files?

Copy link
Contributor

@comcalvi comcalvi left a comment

Choose a reason for hiding this comment

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

We're almost there! It would be helpful if you could elaborate on the "suppress warnings" functionality; I don't fully understand what that's doing. We should definitely reduce duplication between this PR and the ec2 PR.

@@ -0,0 +1 @@
export * from './imds-aspect';
Copy link
Contributor

Choose a reason for hiding this comment

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

nitpick: can we add a newline to the end of this file?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure, added

@@ -73,12 +73,14 @@
"license": "Apache-2.0",
"devDependencies": {
"@types/jest": "^26.0.24",
"@types/sinon": "^9.0.11",
Copy link
Contributor

Choose a reason for hiding this comment

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

We prefer to use jest over sinon. Can you remove sinon from the dependencies?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure, removed

aspect.visit(construct);

// THEN
expect(stub.calledOnce).toBeTruthy();
Copy link
Contributor

Choose a reason for hiding this comment

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

in keeping with above comments, this should be expect(stub).toHaveBeenCalled()

suppressWarnings: true,
});
const errmsg = 'ERROR';
const stub = sinon.stub(aspect, 'visit').callsFake((node) => {
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you make this a jest stub and it a more descriptive name? Something like visitStub.

/**
* Adds a warning annotation to a node, unless `suppressWarnings` is true.
*
* @param node The scope to add the warning to.
* @param message The warning message.
*/
protected warn(node: cdk.IConstruct, message: string) {
if (this.suppressWarnings !== true) {
cdk.Annotations.of(node).addWarning(`${ImdsAspect.name} failed on node ${node.node.id}: ${message}`);
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Could you explain the need for the warning suppressing functionality here? I don't entirely understand why we need this.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We actually don't need this here now that I think about it, thanks for calling this out! Removed.

For context, this is a leftover from my original implementation, which included the changes in #16051. In that PR, I've added the option to suppress warnings in the event that the aspects for LaunchTemplates and Instances are used together. This is because the Instance aspect works by creating a LaunchTemplate for the instance, and it emits a warning if an instance is already associated with a LaunchTemplate (to avoid silently modifying a LaunchTemplate created by a user). If used together, warnings from the Instance aspect would be misleading since the LaunchTemplate aspect would handle those cases.

const construct = new cdk.Construct(stack, 'Construct');

// WHEN
aspect.visit(construct);
Copy link
Contributor

Choose a reason for hiding this comment

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

This should be cdk.Aspects.of(stack).add(aspect);.

vpc = new ec2.Vpc(stack, 'Vpc');
});

test('suppresses warnings', () => {
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you explain what this test verifies? Why do we need the capability to suppress warnings (and which warnings do we need to suppress)? I don't understand what this is checking.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've removed this test since we took out the ability to suppress warnings.

packages/@aws-cdk/aws-autoscaling/README.md Show resolved Hide resolved
@jericht jericht requested a review from comcalvi October 5, 2021 01:10
@mergify mergify bot dismissed comcalvi’s stale review October 5, 2021 01:10

Pull request has been modified.

@jericht jericht force-pushed the jericht/autoscaling_imds_aspect branch 2 times, most recently from 8cb15e3 to 8b3906a Compare October 5, 2021 01:13

```ts
const aspect = new autoscaling.AutoScalingGroupImdsAspect({
enableImdsV1: false,
Copy link
Contributor

Choose a reason for hiding this comment

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

It's a bit confusing to me that this field is called enableImdsV1 but the other one is called disableImdsv1.

Feels like they should be the same?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good catch, updated them both to be requireImdsv2 as requested in another comment

*/
interface ImdsAspectProps {
/**
* Whether IMDSv1 should be enabled or not.
Copy link
Contributor

Choose a reason for hiding this comment

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

Isn't it the default to have it enabled? So does it ever make sense to set this to anything other than false ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good point, removed this variable and renamed the class to reflect its behavior

/**
* Base class for IMDS configuration Aspect.
*/
abstract class ImdsAspect implements cdk.IAspect {
Copy link
Contributor

Choose a reason for hiding this comment

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

Why does this base class need to exist? There's only one subclass?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It doesn't, this was leftover from a previous implementation where there were multiple subclasses. I've merged this code into a single class.

*
* @default - false
*/
readonly disableImdsv1?: boolean;
Copy link
Contributor

Choose a reason for hiding this comment

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

I'd like to avoid negatives in booleans. Isn't something like requireImdsv2 much more descriptive?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I agree, updated

@mergify mergify bot dismissed rix0rrr’s stale review October 13, 2021 00:35

Pull request has been modified.

@jericht jericht requested a review from rix0rrr October 13, 2021 00:35
@jericht jericht force-pushed the jericht/autoscaling_imds_aspect branch from 8e54242 to 336da33 Compare October 13, 2021 23:16
@rix0rrr rix0rrr changed the title feat(aws-autoscaling): add aspect to enable/disable imdsv1 feat(aws-autoscaling): add flag and aspect to require imdsv2 Oct 19, 2021
@mergify
Copy link
Contributor

mergify bot commented Oct 19, 2021

Thank you for contributing! Your pull request will be updated from master and then merged automatically (do not update manually, and be sure to allow changes to be pushed to your fork).

@aws-cdk-automation
Copy link
Collaborator

AWS CodeBuild CI Report

  • CodeBuild project: AutoBuildProject89A8053A-LhjRyN9kxr8o
  • Commit ID: a26b1d6
  • Result: SUCCEEDED
  • Build Logs (available for 30 days)

Powered by github-codebuild-logs, available on the AWS Serverless Application Repository

@mergify mergify bot merged commit ef7e20d into aws:master Oct 19, 2021
@mergify
Copy link
Contributor

mergify bot commented Oct 19, 2021

Thank you for contributing! Your pull request will be updated from master and then merged automatically (do not update manually, and be sure to allow changes to be pushed to your fork).

mergify bot pushed a commit that referenced this pull request Oct 20, 2021
Partially fixes: #5137
Related PR: #16052

**Note:** This PR and the above related PR have common code that has been duplicated across these two PRs because I decided it made more sense for these Aspects to be in the same package with the constructs they work with. However, it means I had to duplicate some of the base class code across the two PRs. Looking for an opinion on what's better here:
- Should we keep it as is (2 PRs) so these Aspects are cleanly separated? or,
- Does it make sense to either combine them in some way (e.g. a new package `@aws-cdk/aspects`) or have one reference the other (maybe the AutoScalingGroup aspect can reference the code in this PR since it already depends on this package).

### Changes
Adds an aspect that can enable/disable IMDSv1 on Instances and Launch Templates.

### Testing
Added unit tests

----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
TikiTDO pushed a commit to TikiTDO/aws-cdk that referenced this pull request Feb 21, 2022
Partially fixes: aws#5137
Related PR: aws#16051

**Note:** I have some concerns about duplicated code between this and the above linked PR. Please see that PR for more details.

### Changes
Adds an aspect that can enable/disable IMDSv1 on AutoScalingGroups

### Testing
Added unit tests

----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
TikiTDO pushed a commit to TikiTDO/aws-cdk that referenced this pull request Feb 21, 2022
Partially fixes: aws#5137
Related PR: aws#16052

**Note:** This PR and the above related PR have common code that has been duplicated across these two PRs because I decided it made more sense for these Aspects to be in the same package with the constructs they work with. However, it means I had to duplicate some of the base class code across the two PRs. Looking for an opinion on what's better here:
- Should we keep it as is (2 PRs) so these Aspects are cleanly separated? or,
- Does it make sense to either combine them in some way (e.g. a new package `@aws-cdk/aspects`) or have one reference the other (maybe the AutoScalingGroup aspect can reference the code in this PR since it already depends on this package).

### Changes
Adds an aspect that can enable/disable IMDSv1 on Instances and Launch Templates.

### Testing
Added unit tests

----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
@aws-cdk/aws-autoscaling Related to Amazon EC2 Auto Scaling effort/small Small work item – less than a day of effort p2
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Support setting EC2 instance metadata to require token (IMDSv2)
5 participants