-
Notifications
You must be signed in to change notification settings - Fork 4k
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(lambda): warn if you use function.grantInvoke
while also using currentVersion
#19464
Conversation
PR failing for unrelated reason. Will fix malformed test in events in another PR. Please approve #19465 to fix this build :). |
}); | ||
|
||
// THEN | ||
// cannot add permissions on latest version, so no warning necessary |
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.
Do all of the Qualified functions actually use this base class? Looks like 'LatestVersion' does not.
LatestVersion
is a special case that cannot have permissions added to it, since it is a dynamic qualifier. The typical use case is to alias the latest version, and then apply permissions to the alias. All other qualified functions use the QualifiedFunctionBase
class.
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 typical use case is to alias the latest version, and then apply permissions to the alias.
Is this actually something people do? What's the value over just adding permissions to the 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.
Is this actually something people do? IDK, but I'm reading from the LatestVersion
docstring that says:
/**
* The $LATEST version of a function, useful when attempting to create aliases.
*/
At any rate you cannot add permissions to $LATEST.
…class (#19465) Also removes unnecessary assertion on number of messages, as this breaks when other messages are introduced. See #19464, which introduces a relevant message and breaks this test. Fixing in one other place as well. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
@@ -283,6 +290,14 @@ export abstract class FunctionBase extends Resource implements IFunction, ec2.IC | |||
const action = permission.action ?? 'lambda:InvokeFunction'; | |||
const scope = permission.scope ?? this; | |||
|
|||
if (['lambda:InvokeFunction', 'lambda:*'].includes(action) && !qualified) { |
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.
Is the scope of this narrow enough? I am concerned we might end up giving unnecessary warnings to customers. But, I can't think of a way to narrow this down further (without crazy amounts of effort).
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 think since lambda:*
gives permission to lambda:InvokeFunction
we have to give the warning since it's possible that these customers are broken by the new change. I think we should consider adding lambda:Invoke*
as well to the list (even though there's no other lambda:InvokeXxx
permission, which is why I didn't include it in the first place).
@@ -273,6 +273,13 @@ export abstract class FunctionBase extends Resource implements IFunction, ec2.IC | |||
* @param permission The permission to grant to this Lambda function. @see Permission for details. | |||
*/ | |||
public addPermission(id: string, permission: Permission) { | |||
this.addPermissionHelper(id, permission, 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.
Passing around boolean literals here makes it kindof hard to decipher what is going on. I think it would be more readable to include a protected function in the base class called something like addAnnotationsForPermissionsIfNeeded
(sorry, awfully long name). The default implementation would include the Annotation. Then the Qualified Function can override it to do nothing. (This is basically the template pattern)
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.
Great suggestion and thanks for the link to the pattern. I've updated the code to do this instead.
@@ -283,6 +290,14 @@ export abstract class FunctionBase extends Resource implements IFunction, ec2.IC | |||
const action = permission.action ?? 'lambda:InvokeFunction'; | |||
const scope = permission.scope ?? this; | |||
|
|||
if (['lambda:InvokeFunction', 'lambda:*'].includes(action) && !qualified) { | |||
Annotations.of(this).addWarning([ | |||
'AWS Lambda has changed their authorization strategy, which may cause client invocations of the lambda function to fail with Access Denied errors.', |
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.
'AWS Lambda has changed their authorization strategy, which may cause client invocations of the lambda function to fail with Access Denied errors.', | |
'AWS Lambda has changed their authorization strategy, which may cause client invocations using the `Qualifier` parameter of the lambda function to fail with Access Denied errors.', |
This is clunky writing, but I think we need to mention that only clients using Qualifer will get the errors. I'd welcome better suggestions here.
Updating a test that requires metadata to be the first item returned in `node.metadataEntry`. This is not resilient because we could add additional metadata in the future that changes the order of `node.metadatEntry`. Instead, these tests should use assertions. I've found that we do this quite a bit elsewhere in the CDK. Because this test is actively blocking #19464, I'm including this as a separate PR to get it in and will fast-follow with other changes later. ---- ### All Submissions: * [x] Have you followed the guidelines in our [Contributing guide?](https://github.com/aws/aws-cdk/blob/master/CONTRIBUTING.md) ### Adding new Unconventional Dependencies: * [ ] This PR adds new unconventional dependencies following the process described [here](https://github.com/aws/aws-cdk/blob/master/CONTRIBUTING.md/#adding-new-unconventional-dependencies) ### New Features * [ ] Have you added the new feature to an [integration test](https://github.com/aws/aws-cdk/blob/master/INTEGRATION_TESTS.md)? * [ ] Did you use `cdk-integ` to deploy the infrastructure and generate the snapshot (i.e. `cdk-integ` without `--dry-run`)? *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
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 could be this PR or a different PR, but can you also @deprecate
all qualifier
parameters in integration classes that take Lambdas (with the message something like "pass a Version or Alias object as IFunction instead")? Examples:
readonly qualifier?: string; |
readonly qualifier?: string; |
readonly qualifier?: string; |
}); | ||
|
||
// THEN | ||
// cannot add permissions on latest version, so no warning necessary |
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 typical use case is to alias the latest version, and then apply permissions to the alias.
Is this actually something people do? What's the value over just adding permissions to the function?
@@ -283,6 +303,8 @@ export abstract class FunctionBase extends Resource implements IFunction, ec2.IC | |||
const action = permission.action ?? 'lambda:InvokeFunction'; | |||
const scope = permission.scope ?? this; | |||
|
|||
this.addWarningOnInvokeFunctionPermissions(scope, action); |
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 looks like it will add the warning always, even if you're not using aliases or versions at all. Do I have that right?
If so, that is probably overly spammy. I'd prefer adding the warning if we detect the following:
grantInvoke
is called on the function; ANDcurrentVersion
is called/has been called
If combine those, you're probably doing something wrong.
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 am happy to do this, but what about the scenarios that this will miss? I'm thinking:
const fn = new lambda.Function();
const version = new lambda.Version(this, 'v', {
lambda: fn,
});
fn.grantInvoke();
Shoudn't we warn users who do this that they should grantInvoke on the version?
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.
To minimize the amount of support requests, I'd prefer to err on the side of caution: tell people when they're definitely doing something wrong, rather than when they might be doing something wrong.
So: I don't want false positives. We can talk about how to reduce false negatives.
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.
There might be ways, we might care to invest in them (we might also 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.
Okay! I think I've done exactly as you've described. Moved this from reducing false negatives to reducing false positives.
There is an addVersion()
on Functions that is @deprecated
in favor of currentVersion
so I decided not to warn those users. I think @deprecated
is signal enough that you should not be using it. But open to feedback here.
function.grantInvoke
while also calling currentVersion
function.grantInvoke
while also calling currentVersion
function.grantInvoke
while also using currentVersion
‼️ Lambda is changing their authorization strategy. Under this new behavior customer lambda invocations will fail in this scenario: - the invocation is requested using an IAM Permission with an unqualified ARN as the FunctionName - the invocation is requested with an unqualified ARN and a Qualifier parameter The idea is to steer away from invoking lambdas with a Qualifier request parameter altogether, hence the deprecations. Instead, customers should be requesting permissions on qualified ARNs (versions and aliases) if they want to invoke versions/aliases. See #19464. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
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). |
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 |
Updating a test that requires metadata to be the first item returned in `node.metadataEntry`. This is not resilient because we could add additional metadata in the future that changes the order of `node.metadatEntry`. Instead, these tests should use assertions. I've found that we do this quite a bit elsewhere in the CDK. Because this test is actively blocking aws#19464, I'm including this as a separate PR to get it in and will fast-follow with other changes later. ---- ### All Submissions: * [x] Have you followed the guidelines in our [Contributing guide?](https://github.com/aws/aws-cdk/blob/master/CONTRIBUTING.md) ### Adding new Unconventional Dependencies: * [ ] This PR adds new unconventional dependencies following the process described [here](https://github.com/aws/aws-cdk/blob/master/CONTRIBUTING.md/#adding-new-unconventional-dependencies) ### New Features * [ ] Have you added the new feature to an [integration test](https://github.com/aws/aws-cdk/blob/master/INTEGRATION_TESTS.md)? * [ ] Did you use `cdk-integ` to deploy the infrastructure and generate the snapshot (i.e. `cdk-integ` without `--dry-run`)? *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
‼️ Lambda is changing their authorization strategy. Under this new behavior customer lambda invocations will fail in this scenario: - the invocation is requested using an IAM Permission with an unqualified ARN as the FunctionName - the invocation is requested with an unqualified ARN and a Qualifier parameter The idea is to steer away from invoking lambdas with a Qualifier request parameter altogether, hence the deprecations. Instead, customers should be requesting permissions on qualified ARNs (versions and aliases) if they want to invoke versions/aliases. See aws#19464. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
… `currentVersion` (aws#19464)‼️ Lambda is changing their authorization strategy, which means that some behavior that was previously valid now results in `access-denied` errors. Under the new behavior, customer lambda invocations will fail if the CDK generates a policy with an unqualified ARN as the resource, and the customer invokes lambda with the unqualified ARN and the `Qualifier` request parameter. Example of an affected setup: ``` Statement: { Effect: "Allow", Action: "lambda:InvokeFunction", Resource: "arn:aws:lambda:...:function:MyFunction", } API Call: lambda.Invoke({ FunctionName: "MyFunction", Qualifier: "1234", }) ``` This `Invoke` call *used* to succeed, but under the new authorization strategy it will fail. The required statement to make the call succeed would be (note the qualified ARN): ``` { Effect: "Allow", Action: "lambda:InvokeFunction", Resource: "arn:aws:lambda:...:function:MyFunction:1234", } ``` This PR aims to warn users who could be using an affected setup. Users will receive the a warning message under the following circumstances: - they grant `lambda:InvokeFunction` to an unqualified function arn - they call `lambda.currentVersion` somewhere in their code This is part of aws#19273. Related is aws#19318. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
access-denied
errors.Under the new behavior, customer lambda invocations will fail if the CDK generates a policy with an unqualified ARN as the resource, and the customer invokes lambda with the unqualified ARN and the
Qualifier
request parameter.Example of an affected setup:
This
Invoke
call used to succeed, but under the new authorization strategy it will fail. The required statement to make the call succeed would be (note the qualified ARN):This PR aims to warn users who could be using an affected setup. Users will receive the a warning message under the following circumstances:
lambda:InvokeFunction
to an unqualified function arnlambda.currentVersion
somewhere in their codeThis is part of #19273. Related is #19318.
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license