Skip to content

Commit

Permalink
fix(ec2): default gateway endpoint fails without private subnets
Browse files Browse the repository at this point in the history
Gateway endpoints can't be added to a VPC that doesn't have private
subnets.

Unless indicated otherwise, make the gateway endpoints routable from all
subnets. It'll only be routable from inside the VPC anyway (since
it will have a private IP address).

Fixes #7619.
  • Loading branch information
rix0rrr committed May 5, 2020
1 parent 1275952 commit c475783
Show file tree
Hide file tree
Showing 5 changed files with 54 additions and 20 deletions.
16 changes: 9 additions & 7 deletions packages/@aws-cdk/aws-ec2/lib/util.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as cdk from '@aws-cdk/core';
import { ISubnet, SelectedSubnets, Subnet, SubnetType } from './vpc';
import { ISubnet, Subnet, SubnetType } from './vpc';

/**
* Turn an arbitrary string into one that can be used as a CloudFormation identifier by stripping special characters
Expand Down Expand Up @@ -120,14 +120,16 @@ export function range(n: number): number[] {
/**
* Return the union of table IDs from all selected subnets
*/
export function allRouteTableIds(...ssns: SelectedSubnets[]): string[] {
export function allRouteTableIds(subnets: ISubnet[]): string[] {
const ret = new Set<string>();
for (const ssn of ssns) {
for (const subnet of ssn.subnets) {
if (subnet.routeTable && subnet.routeTable.routeTableId) {
ret.add(subnet.routeTable.routeTableId);
}
for (const subnet of subnets) {
if (subnet.routeTable && subnet.routeTable.routeTableId) {
ret.add(subnet.routeTable.routeTableId);
}
}
return Array.from(ret);
}

export function flatten<A>(xs: A[][]): A[] {
return Array.prototype.concat.apply([], xs);
}
26 changes: 21 additions & 5 deletions packages/@aws-cdk/aws-ec2/lib/vpc-endpoint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import { CfnVPCEndpoint } from './ec2.generated';
import { Peer } from './peer';
import { Port } from './port';
import { ISecurityGroup, SecurityGroup } from './security-group';
import { allRouteTableIds } from './util';
import { IVpc, SubnetSelection, SubnetType } from './vpc';
import { allRouteTableIds, flatten } from './util';
import { ISubnet, IVpc, SubnetSelection } from './vpc';

/**
* A VPC endpoint.
Expand Down Expand Up @@ -113,7 +113,21 @@ export interface GatewayVpcEndpointOptions {
/**
* Where to add endpoint routing.
*
* @default private subnets
* By default, this endpoint will be routable from all subnets in the VPC.
* Specify a list of subnet selection objects here to be more specific.
*
* @default - All subnets in the VPC
* @example
*
* vpc.addGatewayEndpoint('DynamoDbEndpoint', {
* service: ec2.GatewayVpcEndpointAwsService.DYNAMODB,
* // Add only to ISOLATED subnets
* subnets: [
* { subnetType: ec2.SubnetType.ISOLATED }
* ]
* });
*
*
*/
readonly subnets?: SubnetSelection[]
}
Expand Down Expand Up @@ -166,8 +180,10 @@ export class GatewayVpcEndpoint extends VpcEndpoint implements IGatewayVpcEndpoi
constructor(scope: Construct, id: string, props: GatewayVpcEndpointProps) {
super(scope, id);

const subnets = props.subnets || [{ subnetType: SubnetType.PRIVATE }];
const routeTableIds = allRouteTableIds(...subnets.map(s => props.vpc.selectSubnets(s)));
const subnets: ISubnet[] = props.subnets
? flatten(props.subnets.map(s => props.vpc.selectSubnets(s).subnets))
: [...props.vpc.privateSubnets, ...props.vpc.publicSubnets, ...props.vpc.isolatedSubnets];
const routeTableIds = allRouteTableIds(subnets);

if (routeTableIds.length === 0) {
throw new Error('Can\'t add a gateway endpoint to VPC; route table IDs are not available');
Expand Down
4 changes: 2 additions & 2 deletions packages/@aws-cdk/aws-ec2/lib/vpc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
import { NatProvider } from './nat';
import { INetworkAcl, NetworkAcl, SubnetNetworkAclAssociation } from './network-acl';
import { NetworkBuilder } from './network-util';
import { allRouteTableIds, defaultSubnetName, ImportSubnetGroup, subnetGroupNameFromConstructId, subnetId } from './util';
import { allRouteTableIds, defaultSubnetName, flatten, ImportSubnetGroup, subnetGroupNameFromConstructId, subnetId } from './util';
import { GatewayVpcEndpoint, GatewayVpcEndpointAwsService, GatewayVpcEndpointOptions, InterfaceVpcEndpoint, InterfaceVpcEndpointOptions } from './vpc-endpoint';
import { FlowLog, FlowLogOptions, FlowLogResourceType } from './vpc-flow-logs';
import { VpcLookupOptions } from './vpc-lookup';
Expand Down Expand Up @@ -372,7 +372,7 @@ abstract class VpcBase extends Resource implements IVpc {

// Propagate routes on route tables associated with the right subnets
const vpnRoutePropagation = options.vpnRoutePropagation ?? [{}];
const routeTableIds = allRouteTableIds(...vpnRoutePropagation.map(s => this.selectSubnets(s)));
const routeTableIds = allRouteTableIds(flatten(vpnRoutePropagation.map(s => this.selectSubnets(s).subnets)));

if (routeTableIds.length === 0) {
this.node.addError(`enableVpnGateway: no subnets matching selection: '${JSON.stringify(vpnRoutePropagation)}'. Select other subnets to add routes to.`);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -541,6 +541,15 @@
},
{
"Ref": "MyVpcPrivateSubnet3RouteTableB790927C"
},
{
"Ref": "MyVpcPublicSubnet1RouteTableC46AB2F4"
},
{
"Ref": "MyVpcPublicSubnet2RouteTable1DF17386"
},
{
"Ref": "MyVpcPublicSubnet3RouteTable15028F08"
}
],
"VpcEndpointType": "Gateway"
Expand Down Expand Up @@ -587,6 +596,15 @@
},
{
"Ref": "MyVpcPrivateSubnet3RouteTableB790927C"
},
{
"Ref": "MyVpcPublicSubnet1RouteTableC46AB2F4"
},
{
"Ref": "MyVpcPublicSubnet2RouteTable1DF17386"
},
{
"Ref": "MyVpcPublicSubnet3RouteTable15028F08"
}
],
"VpcEndpointType": "Gateway"
Expand Down
10 changes: 4 additions & 6 deletions packages/@aws-cdk/aws-ec2/test/test.vpc-endpoint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,10 @@ export = {
Ref: 'VpcNetworkB258E83A',
},
RouteTableIds: [
{
Ref: 'VpcNetworkPrivateSubnet1RouteTableCD085FF1',
},
{
Ref: 'VpcNetworkPrivateSubnet2RouteTableE97B328B',
},
{ Ref: 'VpcNetworkPrivateSubnet1RouteTableCD085FF1' },
{ Ref: 'VpcNetworkPrivateSubnet2RouteTableE97B328B' },
{ Ref: 'VpcNetworkPublicSubnet1RouteTable25CCC53F' },
{ Ref: 'VpcNetworkPublicSubnet2RouteTableE5F348DF' },
],
VpcEndpointType: 'Gateway',
}));
Expand Down

0 comments on commit c475783

Please sign in to comment.