Skip to content

Commit

Permalink
fix(lambda): support Lambda's new Invoke with Qualifier authoriza…
Browse files Browse the repository at this point in the history
…tion strategy (#19318)

‼️ 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 align the CDK with the new authorization strategy. The PR introduces changes to the `grantInvoke()` api on a lambda function. Now, when calling `grantInvoke()` on a lambda function, `[ARN, ARN:*]` is used in the identity policy, so that identities that are granted permission to invoke the Function may also invoke all of its Versions and Aliases. 

When calling `grantInvoke()` on a lambda function Version or Alias, the generated identity policy will remain the same, and only include `ARN:<version/alias>` in the policy.

This is part of #19273


----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
  • Loading branch information
madeline-k committed Mar 24, 2022
1 parent 3c9ea5f commit d06b27f
Show file tree
Hide file tree
Showing 39 changed files with 862 additions and 267 deletions.
Expand Up @@ -1051,12 +1051,28 @@
{
"Action": "lambda:InvokeFunction",
"Effect": "Allow",
"Resource": {
"Fn::GetAtt": [
"nameserviceTaskRecordManagerCleanupResourceProviderHandler08068F99",
"Arn"
]
}
"Resource": [
{
"Fn::GetAtt": [
"nameserviceTaskRecordManagerCleanupResourceProviderHandler08068F99",
"Arn"
]
},
{
"Fn::Join": [
"",
[
{
"Fn::GetAtt": [
"nameserviceTaskRecordManagerCleanupResourceProviderHandler08068F99",
"Arn"
]
},
":*"
]
]
}
]
}
],
"Version": "2012-10-17"
Expand Down
2 changes: 1 addition & 1 deletion packages/@aws-cdk/aws-apigateway/lib/authorizers/lambda.ts
Expand Up @@ -105,7 +105,7 @@ abstract class LambdaAuthorizer extends Authorizer implements IAuthorizer {
this.role.attachInlinePolicy(new iam.Policy(this, 'authorizerInvokePolicy', {
statements: [
new iam.PolicyStatement({
resources: [this.handler.functionArn],
resources: this.handler.resourceArnsForGrantInvoke,
actions: ['lambda:InvokeFunction'],
}),
],
Expand Down
Expand Up @@ -176,12 +176,28 @@
{
"Action": "lambda:InvokeFunction",
"Effect": "Allow",
"Resource": {
"Fn::GetAtt": [
"MyAuthorizerFunction70F1223E",
"Arn"
]
}
"Resource": [
{
"Fn::GetAtt": [
"MyAuthorizerFunction70F1223E",
"Arn"
]
},
{
"Fn::Join": [
"",
[
{
"Fn::GetAtt": [
"MyAuthorizerFunction70F1223E",
"Arn"
]
},
":*"
]
]
}
]
}
],
"Version": "2012-10-17"
Expand Down
Expand Up @@ -393,7 +393,7 @@ describe('lambda authorizer', () => {
PolicyDocument: {
Statement: [
{
Resource: stack.resolve(func.functionArn),
Resource: stack.resolve(func.resourceArnsForGrantInvoke),
Action: 'lambda:InvokeFunction',
Effect: 'Allow',
},
Expand Down Expand Up @@ -485,7 +485,7 @@ describe('lambda authorizer', () => {
PolicyDocument: {
Statement: [
{
Resource: stack.resolve(func.functionArn),
Resource: stack.resolve(func.resourceArnsForGrantInvoke),
Action: 'lambda:InvokeFunction',
Effect: 'Allow',
},
Expand Down
Expand Up @@ -58,12 +58,28 @@
{
"Action": "lambda:InvokeFunction",
"Effect": "Allow",
"Resource": {
"Fn::GetAtt": [
"funcC3A0C2E2",
"Arn"
]
}
"Resource": [
{
"Fn::GetAtt": [
"funcC3A0C2E2",
"Arn"
]
},
{
"Fn::Join": [
"",
[
{
"Fn::GetAtt": [
"funcC3A0C2E2",
"Arn"
]
},
":*"
]
]
}
]
}
],
"Version": "2012-10-17"
Expand Down
Expand Up @@ -47,6 +47,7 @@ export class EdgeFunction extends Resource implements lambda.IVersion {
public readonly role?: iam.IRole;
public readonly version: string;
public readonly architecture: lambda.Architecture;
public readonly resourceArnsForGrantInvoke: string[];

private readonly _edgeFunction: lambda.Function;

Expand All @@ -68,6 +69,7 @@ export class EdgeFunction extends Resource implements lambda.IVersion {
this.permissionsNode = this._edgeFunction.permissionsNode;
this.version = lambda.extractQualifierFromArn(this.functionArn);
this.architecture = this._edgeFunction.architecture;
this.resourceArnsForGrantInvoke = this._edgeFunction.resourceArnsForGrantInvoke;

this.node.defaultChild = this._edgeFunction;
}
Expand Down
Expand Up @@ -299,12 +299,10 @@ describe('CodeDeploy Lambda DeploymentGroup', () => {
PolicyDocument: {
Statement: [{
Action: 'lambda:InvokeFunction',
Resource: {
'Fn::GetAtt': [
'PreHook8B53F672',
'Arn',
],
},
Resource: [
{ 'Fn::GetAtt': ['PreHook8B53F672', 'Arn'] },
{ 'Fn::Join': ['', [{ 'Fn::GetAtt': ['PreHook8B53F672', 'Arn'] }, ':*']] },
],
Effect: 'Allow',
}],
Version: '2012-10-17',
Expand Down Expand Up @@ -347,12 +345,10 @@ describe('CodeDeploy Lambda DeploymentGroup', () => {
PolicyDocument: {
Statement: [{
Action: 'lambda:InvokeFunction',
Resource: {
'Fn::GetAtt': [
'PreHook8B53F672',
'Arn',
],
},
Resource: [
{ 'Fn::GetAtt': ['PreHook8B53F672', 'Arn'] },
{ 'Fn::Join': ['', [{ 'Fn::GetAtt': ['PreHook8B53F672', 'Arn'] }, ':*']] },
],
Effect: 'Allow',
}],
Version: '2012-10-17',
Expand Down Expand Up @@ -395,12 +391,10 @@ describe('CodeDeploy Lambda DeploymentGroup', () => {
PolicyDocument: {
Statement: [{
Action: 'lambda:InvokeFunction',
Resource: {
'Fn::GetAtt': [
'PostHookF2E49B30',
'Arn',
],
},
Resource: [
{ 'Fn::GetAtt': ['PostHookF2E49B30', 'Arn'] },
{ 'Fn::Join': ['', [{ 'Fn::GetAtt': ['PostHookF2E49B30', 'Arn'] }, ':*']] },
],
Effect: 'Allow',
}],
Version: '2012-10-17',
Expand Down Expand Up @@ -443,12 +437,10 @@ describe('CodeDeploy Lambda DeploymentGroup', () => {
PolicyDocument: {
Statement: [{
Action: 'lambda:InvokeFunction',
Resource: {
'Fn::GetAtt': [
'PostHookF2E49B30',
'Arn',
],
},
Resource: [
{ 'Fn::GetAtt': ['PostHookF2E49B30', 'Arn'] },
{ 'Fn::Join': ['', [{ 'Fn::GetAtt': ['PostHookF2E49B30', 'Arn'] }, ':*']] },
],
Effect: 'Allow',
}],
Version: '2012-10-17',
Expand Down
Expand Up @@ -495,6 +495,34 @@
"PreHook8B53F672",
"Arn"
]
},
{
"Fn::Join": [
"",
[
{
"Fn::GetAtt": [
"PostHookF2E49B30",
"Arn"
]
},
":*"
]
]
},
{
"Fn::Join": [
"",
[
{
"Fn::GetAtt": [
"PreHook8B53F672",
"Arn"
]
},
":*"
]
]
}
]
}
Expand Down
Expand Up @@ -115,10 +115,7 @@ export class LambdaInvokeAction extends Action {
}));

// allow pipeline to invoke this lambda functionn
options.role.addToPolicy(new iam.PolicyStatement({
actions: ['lambda:InvokeFunction'],
resources: [this.props.lambda.functionArn],
}));
this.props.lambda.grantInvoke(options.role);

// allow the Role access to the Bucket, if there are any inputs/outputs
if ((this.actionProperties.inputs || []).length > 0) {
Expand Down
Expand Up @@ -551,12 +551,28 @@
{
"Action": "lambda:InvokeFunction",
"Effect": "Allow",
"Resource": {
"Fn::GetAtt": [
"LambdaFun98622869",
"Arn"
]
}
"Resource": [
{
"Fn::GetAtt": [
"LambdaFun98622869",
"Arn"
]
},
{
"Fn::Join": [
"",
[
{
"Fn::GetAtt": [
"LambdaFun98622869",
"Arn"
]
},
":*"
]
]
}
]
}
],
"Version": "2012-10-17"
Expand Down

0 comments on commit d06b27f

Please sign in to comment.