-
Notifications
You must be signed in to change notification settings - Fork 3.9k
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
Conversation
1689715
to
ff3f13d
Compare
ff3f13d
to
8d01baa
Compare
@comcalvi I just rebased this and it is ready for review. Will you be able to take a look at it? |
@jericht Thanks for the PR! I'll definitely take a look at this. |
There was a problem hiding this 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.
* Whether warning annotations from this Aspect should be suppressed or not. | ||
* @default false |
There was a problem hiding this comment.
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>
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated
/* istanbul ignore next */ | ||
if (node === undefined || !(node instanceof AutoScalingGroup)) { | ||
return; | ||
} |
There was a problem hiding this comment.
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
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good, updated
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'; | ||
} |
There was a problem hiding this comment.
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
🙂.
There was a problem hiding this comment.
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)) { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed
* Adds a warning annotation to a node, unless `suppressWarnings` is true. | ||
* @param node The scope to add the warning to. |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated
// 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', | ||
}, | ||
})); |
There was a problem hiding this comment.
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()
):
// 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, | |
}); |
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Similarly to above:
aspect.visit(asg); | |
cdk.Aspects.of(stack).add(aspect); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated
8d01baa
to
6160686
Compare
Pull request has been modified.
924c117
to
ebfd5f2
Compare
There was a problem hiding this 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'; |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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", |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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(); |
There was a problem hiding this comment.
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) => { |
There was a problem hiding this comment.
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}`); | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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', () => { |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
8cb15e3
to
8b3906a
Compare
|
||
```ts | ||
const aspect = new autoscaling.AutoScalingGroupImdsAspect({ | ||
enableImdsV1: false, |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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. |
There was a problem hiding this comment.
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
?
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree, updated
8e54242
to
336da33
Compare
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 CodeBuild CI Report
Powered by github-codebuild-logs, available on the AWS Serverless Application Repository |
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). |
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*
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*
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*
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