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(lambda): warn if you use function.grantInvoke while also using currentVersion #19464

Merged
merged 14 commits into from
Mar 31, 2022

Conversation

kaizencc
Copy link
Contributor

@kaizencc kaizencc commented Mar 18, 2022

‼️ 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 #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

@gitpod-io
Copy link

gitpod-io bot commented Mar 18, 2022

@github-actions github-actions bot added the @aws-cdk/aws-lambda Related to AWS Lambda label Mar 18, 2022
@mergify mergify bot added the contribution/core This is a PR that came from AWS. label Mar 18, 2022
@kaizencc kaizencc added the pr-linter/exempt-readme The PR linter will not require README changes label Mar 18, 2022
@kaizencc kaizencc changed the title feat(lambda): support Lambda's new IAM authorization behavior for resource polices feat(lambda): warn relevant users of Lambda's new IAM authorization behavior for resource polices Mar 18, 2022
@kaizencc
Copy link
Contributor Author

kaizencc commented Mar 18, 2022

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
Copy link
Contributor Author

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.

Copy link
Contributor

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?

Copy link
Contributor Author

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.

@kaizencc kaizencc requested review from rix0rrr, madeline-k and a team March 18, 2022 17:25
mergify bot pushed a commit that referenced this pull request Mar 21, 2022
…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*
packages/@aws-cdk/aws-lambda/lib/function-base.ts Outdated Show resolved Hide resolved
@@ -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) {
Copy link
Contributor

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).

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 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);
Copy link
Contributor

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)

Copy link
Contributor Author

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.',
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
'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.

@kaizencc kaizencc added the pr-linter/exempt-integ-test The PR linter will not require integ test changes label Mar 22, 2022
mergify bot pushed a commit that referenced this pull request Mar 23, 2022
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*
@kaizencc kaizencc requested a review from madeline-k March 24, 2022 01:02
Copy link
Contributor

@rix0rrr rix0rrr left a 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:

});

// THEN
// cannot add permissions on latest version, so no warning necessary
Copy link
Contributor

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);
Copy link
Contributor

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; AND
  • currentVersion is called/has been called

If combine those, you're probably doing something wrong.

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 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?

Copy link
Contributor

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.

Copy link
Contributor

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).

Copy link
Contributor Author

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.

@kaizencc kaizencc requested a review from rix0rrr March 24, 2022 23:33
@github-actions github-actions bot added the p2 label Mar 24, 2022
@rix0rrr rix0rrr changed the title feat(lambda): warn relevant users of Lambda's new IAM authorization behavior for resource polices feat(lambda): warn if you call function.grantInvoke while also calling currentVersion Mar 25, 2022
@rix0rrr rix0rrr changed the title feat(lambda): warn if you call function.grantInvoke while also calling currentVersion feat(lambda): warn if you use function.grantInvoke while also using currentVersion Mar 25, 2022
mergify bot pushed a commit that referenced this pull request Mar 25, 2022
‼️ 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*
@mergify
Copy link
Contributor

mergify bot commented Mar 31, 2022

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 mergify bot merged commit fd1fff9 into master Mar 31, 2022
@mergify mergify bot deleted the conroy/update-lambda-resource-policies branch March 31, 2022 13:29
@mergify
Copy link
Contributor

mergify bot commented Mar 31, 2022

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: 0fc25bd
  • Result: SUCCEEDED
  • Build Logs (available for 30 days)

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

StevePotter pushed a commit to StevePotter/aws-cdk that referenced this pull request Apr 27, 2022
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*
StevePotter pushed a commit to StevePotter/aws-cdk that referenced this pull request Apr 27, 2022
‼️ 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*
StevePotter pushed a commit to StevePotter/aws-cdk that referenced this pull request Apr 27, 2022
… `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*
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
@aws-cdk/aws-lambda Related to AWS Lambda contribution/core This is a PR that came from AWS. p2 pr-linter/exempt-integ-test The PR linter will not require integ test changes pr-linter/exempt-readme The PR linter will not require README changes
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants