Skip to content

Commit

Permalink
Merge pull request #2013 from sabornibhattacharya/main
Browse files Browse the repository at this point in the history
New pattern - APIGW Lambda with wildcard resource-based policy using CDK
  • Loading branch information
julianwood authored Feb 1, 2024
2 parents 178ec01 + 192085e commit d3f590e
Show file tree
Hide file tree
Showing 15 changed files with 375 additions and 0 deletions.
10 changes: 10 additions & 0 deletions apigw-lambda-wildcard-resourcebasedpolicy-cdk/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
*.swp
package-lock.json
__pycache__
.pytest_cache
.venv
*.egg-info

# CDK asset staging directory
.cdk.staging
cdk.out
103 changes: 103 additions & 0 deletions apigw-lambda-wildcard-resourcebasedpolicy-cdk/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
# Amazon API Gateway to AWS Lambda with wildcard resource-based policy

Create a REST API with proxy integration to a Lambda function to keep the size of resource-based policy within the allowed hard limit. Currently, in an API Gateway-Lambda setup whenever an user adds an integration, CDK adds a new policy to the Lambda function's resource-based policy. It might result in exceeding the policy size limit for Lambda function which is 20KB.",
This sample project demonstrates how to use CDK to create a customized integration that would keep the policy size within limit by using wildcards in the resource-based policy.


Learn more about this pattern at Serverless Land Patterns: [https://serverlessland.com/patterns/apigw-lambda-wildcard-resourcebasedpolicy-cdk](https://serverlessland.com/patterns/apigw-lambda-wildcard-resourcebasedpolicy-cdk)

Important: this application uses various AWS services and there are costs associated with these services after the Free Tier usage - please see the [AWS Pricing page](https://aws.amazon.com/pricing/) for details. You are responsible for any AWS costs incurred. No warranty is implied in this example.

## Requirements

* [Create an AWS account](https://portal.aws.amazon.com/gp/aws/developer/registration/index.html) if you do not already have one and log in. The IAM user that you use must have sufficient permissions to make necessary AWS service calls and manage AWS resources.
* [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured
* [Git Installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git)
* [Node and NPM](https://nodejs.org/en/download/) installed
* [Python, pip, virtuenv](https://docs.aws.amazon.com/cdk/latest/guide/work-with-cdk-python.html) installed
* [AWS Cloud Development Kit](https://docs.aws.amazon.com/cdk/latest/guide/cli.html) (AWS CDK) installed

## Deployment Instructions

1. Clone the project to your local working directory

```sh
git clone https://github.com/aws-samples/serverless-patterns/
```

2. Change the working directory to this pattern's directory

```sh
cd serverless-patterns/apigw-lambda-wildcard-resourcebasedpolicy-cdk
```

3. Create and activate the project's virtual environment. This allows the project's dependencies to be installed locally in the project folder, instead of globally. Note that if you have multiple versions of Python installed, where the `python` command references Python 2.x, then you can reference Python 3.x by using the `python3` command. You can check which version of Python is being referenced by running the command `python --version` or `python3 --version`

```sh
python -m venv .venv
source .venv/bin/activate
```

4. Install the project dependencies

```sh
python -m pip install -r requirements.txt
```

5. Deploy the stack to your default AWS account and region.

```sh
cdk deploy
```

## How it works

The CDK app deploys the resources and the IAM permissions required to run the application.

## Testing

Log into the AWS Console, browse to Amazon API Gateway console to find REST API:

1. Verify the Lambda function integrated to the API resources.

2. Navigate to the function permissions option to validate its resource-based:
```JSON
{
"Version": "2012-10-17",
"Id": "default",
"Statement": [
{
"Sid": "CdkApigwLambdaWildCardPolicyStack-CDKFunction1BFFE2B32-wXxkAliuaQ0d",
"Effect": "Allow",
"Principal": {
"Service": "apigateway.amazonaws.com"
},
"Action": "lambda:InvokeFunction",
"Resource": "arn:aws:lambda:us-east-1:01234567890:function:CdkApigwLambdaWildCardPolicySt-CDKFunction43C45D9B-pnj1nEvuxNCR",
"Condition": {
"ArnLike": {
"AWS:SourceArn": "arn:aws:execute-api:us-east-1:01234567890:abcd1234/*/*/*"
}
}
}
]
}
```

3. Invoke your API endpoint to validate that your function is getting invoked successfully. Your API should return this response which is sent by your funtion
```
Invoked Successfully
```

## Cleanup

Run the given command to delete the resources that were created. It might take some time for the CloudFormation stack to get deleted.

```sh
cdk destroy
```

----
Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.

SPDX-License-Identifier: MIT-0
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
{
"title": "Amazon API Gateway to AWS Lambda with wildcard resource-based policy",
"description": "Create a REST API with proxy integration to a Lambda function to keep the size of resource-based policy within the allowed hard limit.",
"language": "Python",
"level": "200",
"framework": "CDK",
"introBox": {
"headline": "How it works",
"text": [
"Currently, in an API Gateway-Lambda setup whenever an user adds an integration, CDK adds a new policy to the Lambda function's resource-based policy.",
"It might result in exceeding the policy size limit for Lambda function which is 20KB.",
"This sample project demonstrates how to use CDK to create a customized integration that would keep the policy size within limit by using wildcards in the resource-based policy."
]
},
"gitHub": {
"template": {
"repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/apigw-lambda-wildcard-resourcebasedpolicy-cdk",
"templateURL": "serverless-patterns/apigw-lambda-wildcard-resourcebasedpolicy-cdk",
"projectFolder": "apigw-lambda-wildcard-resourcebasedpolicy-cdk",
"templateFile": "template.yaml"
}
},
"resources": {
"bullets": []
},
"deploy": {
"text": [
"sam deploy"
]
},
"testing": {
"text": [
"See the GitHub repo for detailed testing instructions."
]
},
"cleanup": {
"text": [
"Delete the stack: sam delete."
]
},
"authors": [
{
"name": "Saborni Bhattacharya",
"image": "https://drive.google.com/file/d/1AZFquOkafEQRUlrT4hKOtIbt4Cq66SHd/view?usp=sharing",
"bio": "I am Saborni working as a Cloud Engineer at AWS SE Serverless.",
"linkedin": "saborni-bhattacharya-5b523812a"
}
],
"patternArch": {
"icon1": {
"x": 20,
"y": 50,
"service": "apigw",
"label": "API Gateway REST API"
},
"icon2": {
"x": 80,
"y": 50,
"service": "lambda",
"label": "AWS Lambda"
},
"line1": {
"from": "icon1",
"to": "icon2"
}
}
}
11 changes: 11 additions & 0 deletions apigw-lambda-wildcard-resourcebasedpolicy-cdk/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import aws_cdk as cdk

from cdk_apigw_lambda_wildcardpolicy_stack.cdk_apigw_lambda_wildcardpolicy_stack import CdkApigwLambdaWildCardPolicyStack

app = cdk.App()

# Instantiate the CDK stack
CdkApigwLambdaWildCardPolicyStack(app, 'CdkApigwLambdaWildCardPolicyStack')

# Synthesize the CloudFormation template
app.synth()
58 changes: 58 additions & 0 deletions apigw-lambda-wildcard-resourcebasedpolicy-cdk/cdk.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
{
"app": "python3 app.py",
"watch": {
"include": [
"**"
],
"exclude": [
"README.md",
"cdk*.json",
"requirements*.txt",
"source.bat",
"**/__init__.py",
"python/__pycache__",
"tests"
]
},
"context": {
"@aws-cdk/aws-lambda:recognizeLayerVersion": true,
"@aws-cdk/core:checkSecretUsage": true,
"@aws-cdk/core:target-partitions": [
"aws",
"aws-cn"
],
"@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true,
"@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true,
"@aws-cdk/aws-ecs:arnFormatIncludesClusterName": true,
"@aws-cdk/aws-iam:minimizePolicies": true,
"@aws-cdk/core:validateSnapshotRemovalPolicy": true,
"@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true,
"@aws-cdk/aws-s3:createDefaultLoggingPolicy": true,
"@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": true,
"@aws-cdk/aws-apigateway:disableCloudWatchRole": true,
"@aws-cdk/core:enablePartitionLiterals": true,
"@aws-cdk/aws-events:eventsTargetQueueSameAccount": true,
"@aws-cdk/aws-iam:standardizedServicePrincipals": true,
"@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": true,
"@aws-cdk/aws-iam:importedRoleStackSafeDefaultPolicyName": true,
"@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy": true,
"@aws-cdk/aws-route53-patters:useCertificate": true,
"@aws-cdk/customresources:installLatestAwsSdkDefault": false,
"@aws-cdk/aws-rds:databaseProxyUniqueResourceName": true,
"@aws-cdk/aws-codedeploy:removeAlarmsFromDeploymentGroup": true,
"@aws-cdk/aws-apigateway:authorizerChangeDeploymentLogicalId": true,
"@aws-cdk/aws-ec2:launchTemplateDefaultUserData": true,
"@aws-cdk/aws-secretsmanager:useAttachedSecretResourcePolicyForSecretTargetAttachments": true,
"@aws-cdk/aws-redshift:columnId": true,
"@aws-cdk/aws-stepfunctions-tasks:enableEmrServicePolicyV2": true,
"@aws-cdk/aws-ec2:restrictDefaultSecurityGroup": true,
"@aws-cdk/aws-apigateway:requestValidatorUniqueId": true,
"@aws-cdk/aws-kms:aliasNameRef": true,
"@aws-cdk/aws-autoscaling:generateLaunchTemplateInsteadOfLaunchConfig": true,
"@aws-cdk/core:includePrefixInUniqueNameGeneration": true,
"@aws-cdk/aws-efs:denyAnonymousAccess": true,
"@aws-cdk/aws-opensearchservice:enableOpensearchMultiAzWithStandby": true,
"@aws-cdk/aws-lambda-nodejs:useLatestRuntimeVersion": true,
"@aws-cdk/aws-efs:mountTargetOrderInsensitiveLogicalId": true
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
from constructs import Construct
from aws_cdk import (
Stack,
aws_apigateway as apigateway,
aws_lambda as _lambda,
aws_iam as iam
)
from aws_cdk.aws_lambda import CfnPermission

class CdkApigwLambdaWildCardPolicyStack(Stack):
def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None:
super().__init__(scope, construct_id, **kwargs)

# Create a Lambda function
lambda_function = _lambda.Function(self, 'CDKFunction',runtime=_lambda.Runtime.PYTHON_3_11,handler='appfunc.handler',code=_lambda.Code.from_asset('function'))

# Create an API Gateway REST API
api = apigateway.RestApi(self, 'CDKAPI', rest_api_name='CDKAPI', default_integration=LambdaIntegrationNoPermission(lambda_function))
lambda_function.add_permission(id='1', principal=iam.ServicePrincipal('apigateway.amazonaws.com'), action='lambda:InvokeFunction', source_arn=f'arn:aws:execute-api:{self.region}:{self.account}:{api.rest_api_id}/*/*/*')

# Create resources and associate the Lambda integration without resource-based policy creation
resource = api.root.add_resource('resource1')
resource.add_method('GET', LambdaIntegrationNoPermission(lambda_function))
resource = api.root.add_resource('resource2')
resource.add_method('POST', LambdaIntegrationNoPermission(lambda_function))
resource = api.root.add_resource('resourceN')
resource.add_method('POST', LambdaIntegrationNoPermission(lambda_function))


class LambdaIntegrationNoPermission(apigateway.LambdaIntegration):
def __init__(self, handler, **kwargs):
super().__init__(handler, **kwargs)
def bind(self, method: apigateway.Method):
integration_config = super().bind(method)
permissions = filter(lambda x: isinstance(x, CfnPermission), method.node.children)
# Removing permissions policy for each integration
for p in permissions:
method.node.try_remove_child(p.node.id)
return integration_config
49 changes: 49 additions & 0 deletions apigw-lambda-wildcard-resourcebasedpolicy-cdk/example-pattern.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
{
"title": "Amazon API Gateway to AWS Lambda with wildcard resource-based policy",
"description": "Create a REST API with proxy integration to a Lambda function to keep the size of resource-based policy within the allowed hard limit.",
"language": "Python",
"level": "200",
"framework": "CDK",
"introBox": {
"headline": "How it works",
"text": [
"Currently, in an API Gateway-Lambda setup whenever an user adds an integration, CDK adds a new policy to the Lambda function's resource-based policy.",
"It might result in exceeding the policy size limit for Lambda function which is 20KB.",
"This sample project demonstrates how to use CDK to create a customized integration that would keep the policy size within limit by using wildcards in the resource-based policy."
]
},
"gitHub": {
"template": {
"repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/apigw-lambda-wildcard-resourcebasedpolicy-cdk",
"templateURL": "serverless-patterns/apigw-lambda-wildcard-resourcebasedpolicy-cdk",
"projectFolder": "apigw-lambda-wildcard-resourcebasedpolicy-cdk",
"templateFile": "template.yaml"
}
},
"resources": {
"bullets": []
},
"deploy": {
"text": [
"sam deploy"
]
},
"testing": {
"text": [
"See the GitHub repo for detailed testing instructions."
]
},
"cleanup": {
"text": [
"Delete the stack: sam delete."
]
},
"authors": [
{
"name": "Saborni Bhattacharya",
"image": "https://drive.google.com/file/d/1AZFquOkafEQRUlrT4hKOtIbt4Cq66SHd/view?usp=sharing",
"bio": "I am Saborni working as a Cloud Engineer at AWS SE Serverless.",
"linkedin": "saborni-bhattacharya-5b523812a"
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#! Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: MIT-0
def handler(event, context):
return {
"body":"Invoked Successfully",
"statusCode": 200
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pytest==6.2.5
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
aws-cdk-lib==2.95.1
constructs>=10.0.0,<11.0.0
13 changes: 13 additions & 0 deletions apigw-lambda-wildcard-resourcebasedpolicy-cdk/source.bat
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
@echo off

rem The sole purpose of this script is to make the command
rem
rem source .venv/bin/activate
rem
rem (which activates a Python virtualenv on Linux or Mac OS X) work on Windows.
rem On Windows, this command just runs this batch file (the argument is ignored).
rem
rem Now we don't need to document a Windows command for activating a virtualenv.

echo Executing .venv\Scripts\activate.bat for you
.venv\Scripts\activate.bat
Empty file.
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import aws_cdk as core
import aws_cdk.assertions as assertions

from cdk_apigw.cdk_apigw_stack import CdkApigwStack

# example tests. To run these tests, uncomment this file along with the example
# resource in cdk_apigw/cdk_apigw_stack.py
def test_sqs_queue_created():
app = core.App()
stack = CdkApigwStack(app, "cdk-apigw")
template = assertions.Template.from_stack(stack)

# template.has_resource_properties("AWS::SQS::Queue", {
# "VisibilityTimeout": 300
# })

0 comments on commit d3f590e

Please sign in to comment.