Skip to content

Commit

Permalink
fix(lambda): cannot create lambda in public subnets (#9468)
Browse files Browse the repository at this point in the history
----
Closes #8935

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
  • Loading branch information
flemjame-at-amazon authored Aug 11, 2020
1 parent aad951a commit b46fdc9
Show file tree
Hide file tree
Showing 2 changed files with 128 additions and 10 deletions.
19 changes: 13 additions & 6 deletions packages/@aws-cdk/aws-lambda/lib/function.ts
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,15 @@ export interface FunctionProps extends FunctionOptions {
* @default - will not mount any filesystem
*/
readonly filesystem?: FileSystem;

/**
* Lambda Functions in a public subnet can NOT access the internet.
* Use this property to acknowledge this limitation and still place the function in a public subnet.
* @see https://stackoverflow.com/questions/52992085/why-cant-an-aws-lambda-function-inside-a-public-subnet-in-a-vpc-connect-to-the/52994841#52994841
*
* @default false
*/
readonly allowPublicSubnet?: boolean;
}

/**
Expand Down Expand Up @@ -819,15 +828,13 @@ export class Function extends FunctionBase {
}
}

// Pick subnets, make sure they're not Public. Routing through an IGW
// won't work because the ENIs don't get a Public IP.
// Why are we not simply forcing vpcSubnets? Because you might still be choosing
// Isolated networks or selecting among 2 sets of Private subnets by name.
const allowPublicSubnet = props.allowPublicSubnet ?? false;
const { subnetIds } = props.vpc.selectSubnets(props.vpcSubnets);
const publicSubnetIds = new Set(props.vpc.publicSubnets.map(s => s.subnetId));
for (const subnetId of subnetIds) {
if (publicSubnetIds.has(subnetId)) {
throw new Error('Not possible to place Lambda Functions in a Public subnet');
if (publicSubnetIds.has(subnetId) && !allowPublicSubnet) {
throw new Error('Lambda Functions in a public subnet can NOT access the internet. ' +
'If you are aware of this limitation and would still like to place the function int a public subnet, set `allowPublicSubnet` to true');
}
}

Expand Down
119 changes: 115 additions & 4 deletions packages/@aws-cdk/aws-lambda/test/test.vpc-lambda.ts
Original file line number Diff line number Diff line change
Expand Up @@ -212,22 +212,133 @@ export = {
test.done();
},

'picking public subnets is not allowed'(test: Test) {
'can pick public subnet for Lambda'(test: Test) {
// GIVEN
const stack = new cdk.Stack();
const vpc = new ec2.Vpc(stack, 'VPC');

// WHEN
new lambda.Function(stack, 'PublicLambda', {
allowPublicSubnet: true,
code: new lambda.InlineCode('foo'),
handler: 'index.handler',
runtime: lambda.Runtime.NODEJS_10_X,
vpc,
vpcSubnets: { subnetType: ec2.SubnetType.PUBLIC },
});

// THEN
expect(stack).to(haveResource('AWS::Lambda::Function', {
VpcConfig: {
SecurityGroupIds: [
{'Fn::GetAtt': [ 'PublicLambdaSecurityGroup61D896FD', 'GroupId' ]},
],
SubnetIds: [
{Ref: 'VPCPublicSubnet1SubnetB4246D30'},
{Ref: 'VPCPublicSubnet2Subnet74179F39'},
],
},
}));
test.done();
},

'can pick private subnet for Lambda'(test: Test) {
// GIVEN
const stack = new cdk.Stack();
const vpc = new ec2.Vpc(stack, 'VPC');

// WHEN
new lambda.Function(stack, 'PrivateLambda', {
code: new lambda.InlineCode('foo'),
handler: 'index.handler',
runtime: lambda.Runtime.NODEJS_10_X,
vpc,
vpcSubnets: { subnetType: ec2.SubnetType.PRIVATE },
});

// THEN

expect(stack).to(haveResource('AWS::Lambda::Function', {
VpcConfig: {
SecurityGroupIds: [
{'Fn::GetAtt': [ 'PrivateLambdaSecurityGroupF53C8342', 'GroupId' ]},
],
SubnetIds: [
{Ref: 'VPCPrivateSubnet1Subnet8BCA10E0'},
{Ref: 'VPCPrivateSubnet2SubnetCFCDAA7A'},
],
},
}));
test.done();
},

'can pick isolated subnet for Lambda'(test: Test) {
// GIVEN
const stack = new cdk.Stack();
const vpc = new ec2.Vpc(stack, 'VPC', {
subnetConfiguration: [
{
name: 'Isolated',
subnetType: ec2.SubnetType.ISOLATED,
},
],
});

// WHEN
new lambda.Function(stack, 'IsolatedLambda', {
code: new lambda.InlineCode('foo'),
handler: 'index.handler',
runtime: lambda.Runtime.NODEJS_10_X,
vpc,
vpcSubnets: { subnetType: ec2.SubnetType.ISOLATED },
});

// THEN

expect(stack).to(haveResource('AWS::Lambda::Function', {
VpcConfig: {
SecurityGroupIds: [
{'Fn::GetAtt': [ 'IsolatedLambdaSecurityGroupCE25B6A9', 'GroupId' ]},
],
SubnetIds: [
{Ref: 'VPCIsolatedSubnet1SubnetEBD00FC6'},
{Ref: 'VPCIsolatedSubnet2Subnet4B1C8CAA'},
],
},
}));
test.done();
},

'picking public subnet type is not allowed if not overriding allowPublicSubnet'(test: Test) {
// GIVEN
const stack = new cdk.Stack();
const vpc = new ec2.Vpc(stack, 'VPC', {
subnetConfiguration: [
{
name: 'Public',
subnetType: ec2.SubnetType.PUBLIC,
},
{
name: 'Private',
subnetType: ec2.SubnetType.PRIVATE,
},
{
name: 'Isolated',
subnetType: ec2.SubnetType.ISOLATED,
},
],
});

// WHEN
test.throws(() => {
new lambda.Function(stack, 'Lambda', {
new lambda.Function(stack, 'PublicLambda', {
code: new lambda.InlineCode('foo'),
handler: 'index.handler',
runtime: lambda.Runtime.NODEJS_10_X,
vpc,
vpcSubnets: { subnetType: ec2.SubnetType.PUBLIC },
});
});

}, /Lambda Functions in a public subnet/);
test.done();
},
};
Expand Down

0 comments on commit b46fdc9

Please sign in to comment.