Skip to content

Commit f224f73

Browse files
committed
Add SourceAccount property to all S3-triggered Lambda and API Gateway Lambda permissions to prevent cross-account access
1 parent 72ca864 commit f224f73

File tree

2 files changed

+46
-11
lines changed

2 files changed

+46
-11
lines changed

packages/cdk/constructs/RestApiGateway/LambdaEndpoint.ts

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import {Construct} from "constructs"
22
import {IResource, LambdaIntegration} from "aws-cdk-lib/aws-apigateway"
33
import {HttpMethod} from "aws-cdk-lib/aws-lambda"
4-
import {IRole} from "aws-cdk-lib/aws-iam"
4+
import {IRole, ServicePrincipal} from "aws-cdk-lib/aws-iam"
5+
import {Aws} from "aws-cdk-lib"
56
import {LambdaFunction} from "../LambdaFunction"
67

78
export interface LambdaEndpointProps {
@@ -20,9 +21,20 @@ export class LambdaEndpoint extends Construct {
2021

2122
const resource = props.parentResource.addResource(props.resourceName)
2223

23-
resource.addMethod(props.method, new LambdaIntegration(props.lambdaFunction.function, {
24-
credentialsRole: props.restApiGatewayRole
25-
}))
24+
const integration = new LambdaIntegration(props.lambdaFunction.function, {
25+
credentialsRole: props.restApiGatewayRole,
26+
allowTestInvoke: false,
27+
proxy: false
28+
})
29+
30+
resource.addMethod(props.method, integration)
31+
32+
// Add source account to Lambda permission for NCSC compliance
33+
props.lambdaFunction.function.addPermission(`ApiGatewayInvoke-${this.node.id}`, {
34+
principal: new ServicePrincipal("apigateway.amazonaws.com"),
35+
action: "lambda:InvokeFunction",
36+
sourceAccount: Aws.ACCOUNT_ID
37+
})
2638

2739
props.restApiGatewayRole.addManagedPolicy(props.lambdaFunction.executionPolicy)
2840

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import {Construct} from "constructs"
2-
import {Bucket, EventType} from "aws-cdk-lib/aws-s3"
3-
import {LambdaDestination} from "aws-cdk-lib/aws-s3-notifications"
2+
import {Bucket, EventType, CfnBucket} from "aws-cdk-lib/aws-s3"
43
import {Function as LambdaFunction} from "aws-cdk-lib/aws-lambda"
4+
import {Aws} from "aws-cdk-lib"
5+
import {ServicePrincipal} from "aws-cdk-lib/aws-iam"
56

67
export interface S3LambdaNotificationProps {
78
bucket: Bucket
@@ -12,11 +13,33 @@ export class S3LambdaNotification extends Construct {
1213
constructor(scope: Construct, id: string, props: S3LambdaNotificationProps) {
1314
super(scope, id)
1415

15-
const lambdaDestination = new LambdaDestination(props.lambdaFunction)
16+
// Add source account to Lambda permission for NCSC compliance
17+
props.lambdaFunction.addPermission(`S3Invoke-${this.node.id}`, {
18+
principal: new ServicePrincipal("s3.amazonaws.com"),
19+
action: "lambda:InvokeFunction",
20+
sourceAccount: Aws.ACCOUNT_ID,
21+
sourceArn: props.bucket.bucketArn
22+
})
1623

17-
// Listen for all object events to keep knowledge base in sync
18-
props.bucket.addEventNotification(EventType.OBJECT_CREATED, lambdaDestination)
19-
props.bucket.addEventNotification(EventType.OBJECT_REMOVED, lambdaDestination)
20-
props.bucket.addEventNotification(EventType.OBJECT_RESTORE_COMPLETED, lambdaDestination)
24+
// Get the underlying CfnBucket to configure notifications directly
25+
const cfnBucket = props.bucket.node.defaultChild as CfnBucket
26+
27+
// Configure notifications directly on the CfnBucket to avoid auto-permission creation
28+
cfnBucket.notificationConfiguration = {
29+
lambdaConfigurations: [
30+
{
31+
event: EventType.OBJECT_CREATED,
32+
function: props.lambdaFunction.functionArn
33+
},
34+
{
35+
event: EventType.OBJECT_REMOVED,
36+
function: props.lambdaFunction.functionArn
37+
},
38+
{
39+
event: EventType.OBJECT_RESTORE_COMPLETED,
40+
function: props.lambdaFunction.functionArn
41+
}
42+
]
43+
}
2144
}
2245
}

0 commit comments

Comments
 (0)