diff --git a/apigw-lambda-wildcard-resourcebasedpolicy-cdk/.gitignore b/apigw-lambda-wildcard-resourcebasedpolicy-cdk/.gitignore new file mode 100644 index 000000000..37833f8be --- /dev/null +++ b/apigw-lambda-wildcard-resourcebasedpolicy-cdk/.gitignore @@ -0,0 +1,10 @@ +*.swp +package-lock.json +__pycache__ +.pytest_cache +.venv +*.egg-info + +# CDK asset staging directory +.cdk.staging +cdk.out diff --git a/apigw-lambda-wildcard-resourcebasedpolicy-cdk/README.md b/apigw-lambda-wildcard-resourcebasedpolicy-cdk/README.md new file mode 100644 index 000000000..eff2b57e6 --- /dev/null +++ b/apigw-lambda-wildcard-resourcebasedpolicy-cdk/README.md @@ -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 \ No newline at end of file diff --git a/apigw-lambda-wildcard-resourcebasedpolicy-cdk/apigw-lambda-wildcard-resourcebasedpolicy-cdk.json b/apigw-lambda-wildcard-resourcebasedpolicy-cdk/apigw-lambda-wildcard-resourcebasedpolicy-cdk.json new file mode 100644 index 000000000..353e102ba --- /dev/null +++ b/apigw-lambda-wildcard-resourcebasedpolicy-cdk/apigw-lambda-wildcard-resourcebasedpolicy-cdk.json @@ -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" + } + } +} diff --git a/apigw-lambda-wildcard-resourcebasedpolicy-cdk/app.py b/apigw-lambda-wildcard-resourcebasedpolicy-cdk/app.py new file mode 100644 index 000000000..943e8d9a0 --- /dev/null +++ b/apigw-lambda-wildcard-resourcebasedpolicy-cdk/app.py @@ -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() \ No newline at end of file diff --git a/apigw-lambda-wildcard-resourcebasedpolicy-cdk/cdk.json b/apigw-lambda-wildcard-resourcebasedpolicy-cdk/cdk.json new file mode 100644 index 000000000..b7d6c491d --- /dev/null +++ b/apigw-lambda-wildcard-resourcebasedpolicy-cdk/cdk.json @@ -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 + } +} diff --git a/apigw-lambda-wildcard-resourcebasedpolicy-cdk/cdk_apigw_lambda_wildcardpolicy_stack/__init__.py b/apigw-lambda-wildcard-resourcebasedpolicy-cdk/cdk_apigw_lambda_wildcardpolicy_stack/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/apigw-lambda-wildcard-resourcebasedpolicy-cdk/cdk_apigw_lambda_wildcardpolicy_stack/cdk_apigw_lambda_wildcardpolicy_stack.py b/apigw-lambda-wildcard-resourcebasedpolicy-cdk/cdk_apigw_lambda_wildcardpolicy_stack/cdk_apigw_lambda_wildcardpolicy_stack.py new file mode 100644 index 000000000..1993ab3e0 --- /dev/null +++ b/apigw-lambda-wildcard-resourcebasedpolicy-cdk/cdk_apigw_lambda_wildcardpolicy_stack/cdk_apigw_lambda_wildcardpolicy_stack.py @@ -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 diff --git a/apigw-lambda-wildcard-resourcebasedpolicy-cdk/example-pattern.json b/apigw-lambda-wildcard-resourcebasedpolicy-cdk/example-pattern.json new file mode 100644 index 000000000..40f9e2285 --- /dev/null +++ b/apigw-lambda-wildcard-resourcebasedpolicy-cdk/example-pattern.json @@ -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" + } + ] +} diff --git a/apigw-lambda-wildcard-resourcebasedpolicy-cdk/function/appfunc.py b/apigw-lambda-wildcard-resourcebasedpolicy-cdk/function/appfunc.py new file mode 100644 index 000000000..845659bc5 --- /dev/null +++ b/apigw-lambda-wildcard-resourcebasedpolicy-cdk/function/appfunc.py @@ -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 + } \ No newline at end of file diff --git a/apigw-lambda-wildcard-resourcebasedpolicy-cdk/requirements-dev.txt b/apigw-lambda-wildcard-resourcebasedpolicy-cdk/requirements-dev.txt new file mode 100644 index 000000000..927094516 --- /dev/null +++ b/apigw-lambda-wildcard-resourcebasedpolicy-cdk/requirements-dev.txt @@ -0,0 +1 @@ +pytest==6.2.5 diff --git a/apigw-lambda-wildcard-resourcebasedpolicy-cdk/requirements.txt b/apigw-lambda-wildcard-resourcebasedpolicy-cdk/requirements.txt new file mode 100644 index 000000000..d3bf5ab1f --- /dev/null +++ b/apigw-lambda-wildcard-resourcebasedpolicy-cdk/requirements.txt @@ -0,0 +1,2 @@ +aws-cdk-lib==2.95.1 +constructs>=10.0.0,<11.0.0 \ No newline at end of file diff --git a/apigw-lambda-wildcard-resourcebasedpolicy-cdk/source.bat b/apigw-lambda-wildcard-resourcebasedpolicy-cdk/source.bat new file mode 100644 index 000000000..9e1a83442 --- /dev/null +++ b/apigw-lambda-wildcard-resourcebasedpolicy-cdk/source.bat @@ -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 diff --git a/apigw-lambda-wildcard-resourcebasedpolicy-cdk/tests/__init__.py b/apigw-lambda-wildcard-resourcebasedpolicy-cdk/tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/apigw-lambda-wildcard-resourcebasedpolicy-cdk/tests/unit/__init__.py b/apigw-lambda-wildcard-resourcebasedpolicy-cdk/tests/unit/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/apigw-lambda-wildcard-resourcebasedpolicy-cdk/tests/unit/test_cdk_apigw_stack.py b/apigw-lambda-wildcard-resourcebasedpolicy-cdk/tests/unit/test_cdk_apigw_stack.py new file mode 100644 index 000000000..7eb31256c --- /dev/null +++ b/apigw-lambda-wildcard-resourcebasedpolicy-cdk/tests/unit/test_cdk_apigw_stack.py @@ -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 +# })