From 4b0abbcdc6efe2d37e2a9eee382848d2de82de5c Mon Sep 17 00:00:00 2001 From: Adam Ruka Date: Mon, 15 Feb 2021 14:42:15 -0800 Subject: [PATCH] fix(rds): proxy cannot connect to cluster/instance (#12953) By default, when creating a Proxy, we were not creating a Security Group for it, and because of that, the Proxy could not connect to the Cluster/Instance. See docs at: https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/rds-proxy.html ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* --- packages/@aws-cdk/aws-rds/lib/proxy.ts | 16 ++++- .../aws-rds/test/integ.proxy.expected.json | 64 +++++++++++++++++-- packages/@aws-cdk/aws-rds/test/integ.proxy.ts | 4 +- packages/@aws-cdk/aws-rds/test/test.proxy.ts | 24 ++++++- 4 files changed, 98 insertions(+), 10 deletions(-) diff --git a/packages/@aws-cdk/aws-rds/lib/proxy.ts b/packages/@aws-cdk/aws-rds/lib/proxy.ts index e0105702523c8..8e95c343b39be 100644 --- a/packages/@aws-cdk/aws-rds/lib/proxy.ts +++ b/packages/@aws-cdk/aws-rds/lib/proxy.ts @@ -70,7 +70,7 @@ export class ProxyTarget { /** * Bind this target to the specified database proxy. */ - public bind(_: DatabaseProxy): ProxyTargetConfig { + public bind(proxy: DatabaseProxy): ProxyTargetConfig { const engine: IEngine | undefined = this.dbInstance?.engine ?? this.dbCluster?.engine; if (!engine) { @@ -84,6 +84,10 @@ export class ProxyTarget { throw new Error(`Engine '${engineDescription(engine)}' does not support proxies`); } + // allow connecting to the Cluster/Instance from the Proxy + this.dbCluster?.connections.allowDefaultPortFrom(proxy, 'Allow connections to the database Cluster from the Proxy'); + this.dbInstance?.connections.allowDefaultPortFrom(proxy, 'Allow connections to the database Instance from the Proxy'); + return { engineFamily, dbClusters: this.dbCluster ? [this.dbCluster] : undefined, @@ -402,7 +406,13 @@ export class DatabaseProxy extends DatabaseProxyBase secret.grantRead(role); } - this.connections = new ec2.Connections({ securityGroups: props.securityGroups }); + const securityGroups = props.securityGroups ?? [ + new ec2.SecurityGroup(this, 'ProxySecurityGroup', { + description: 'SecurityGroup for Database Proxy', + vpc: props.vpc, + }), + ]; + this.connections = new ec2.Connections({ securityGroups }); const bindResult = props.proxyTarget.bind(this); @@ -424,7 +434,7 @@ export class DatabaseProxy extends DatabaseProxyBase idleClientTimeout: props.idleClientTimeout?.toSeconds(), requireTls: props.requireTLS ?? true, roleArn: role.roleArn, - vpcSecurityGroupIds: props.securityGroups?.map(_ => _.securityGroupId), + vpcSecurityGroupIds: cdk.Lazy.list({ produce: () => this.connections.securityGroups.map(_ => _.securityGroupId) }), vpcSubnetIds: props.vpc.selectSubnets(props.vpcSubnets).subnetIds, }); diff --git a/packages/@aws-cdk/aws-rds/test/integ.proxy.expected.json b/packages/@aws-cdk/aws-rds/test/integ.proxy.expected.json index 3a77314058702..52aab8716ba8c 100644 --- a/packages/@aws-cdk/aws-rds/test/integ.proxy.expected.json +++ b/packages/@aws-cdk/aws-rds/test/integ.proxy.expected.json @@ -385,6 +385,37 @@ } } }, + "dbInstanceSecurityGroupfromawscdkrdsproxydbProxyProxySecurityGroupA345AFE5IndirectPortE3621D4F": { + "Type": "AWS::EC2::SecurityGroupIngress", + "Properties": { + "IpProtocol": "tcp", + "Description": "Allow connections to the database Instance from the Proxy", + "FromPort": { + "Fn::GetAtt": [ + "dbInstance4076B1EC", + "Endpoint.Port" + ] + }, + "GroupId": { + "Fn::GetAtt": [ + "dbInstanceSecurityGroupA58A00A3", + "GroupId" + ] + }, + "SourceSecurityGroupId": { + "Fn::GetAtt": [ + "dbProxyProxySecurityGroup16E727A7", + "GroupId" + ] + }, + "ToPort": { + "Fn::GetAtt": [ + "dbInstance4076B1EC", + "Endpoint.Port" + ] + } + } + }, "dbInstanceSecret032D3661": { "Type": "AWS::SecretsManager::Secret", "Properties": { @@ -429,6 +460,7 @@ "Ref": "dbInstanceSubnetGroupD062EC9E" }, "Engine": "postgres", + "EngineVersion": "11.5", "MasterUsername": { "Fn::Join": [ "", @@ -508,6 +540,22 @@ ] } }, + "dbProxyProxySecurityGroup16E727A7": { + "Type": "AWS::EC2::SecurityGroup", + "Properties": { + "GroupDescription": "SecurityGroup for Database Proxy", + "SecurityGroupEgress": [ + { + "CidrIp": "0.0.0.0/0", + "Description": "Allow all outbound traffic by default", + "IpProtocol": "-1" + } + ], + "VpcId": { + "Ref": "vpcA2121C38" + } + } + }, "dbProxy3B89EAF2": { "Type": "AWS::RDS::DBProxy", "Properties": { @@ -522,7 +570,6 @@ ], "DBProxyName": "dbProxy", "EngineFamily": "POSTGRESQL", - "RequireTLS": true, "RoleArn": { "Fn::GetAtt": [ "dbProxyIAMRole662F3AB8", @@ -536,6 +583,15 @@ { "Ref": "vpcPrivateSubnet2Subnet7031C2BA" } + ], + "RequireTLS": true, + "VpcSecurityGroupIds": [ + { + "Fn::GetAtt": [ + "dbProxyProxySecurityGroup16E727A7", + "GroupId" + ] + } ] } }, @@ -545,6 +601,7 @@ "DBProxyName": { "Ref": "dbProxy3B89EAF2" }, + "TargetGroupName": "default", "ConnectionPoolConfigurationInfo": { "ConnectionBorrowTimeout": 30, "MaxConnectionsPercent": 50 @@ -553,9 +610,8 @@ { "Ref": "dbInstance4076B1EC" } - ], - "TargetGroupName": "default" + ] } } } -} +} \ No newline at end of file diff --git a/packages/@aws-cdk/aws-rds/test/integ.proxy.ts b/packages/@aws-cdk/aws-rds/test/integ.proxy.ts index d3945cf4a5b99..e59ead63f3590 100644 --- a/packages/@aws-cdk/aws-rds/test/integ.proxy.ts +++ b/packages/@aws-cdk/aws-rds/test/integ.proxy.ts @@ -8,7 +8,9 @@ const stack = new cdk.Stack(app, 'aws-cdk-rds-proxy'); const vpc = new ec2.Vpc(stack, 'vpc', { maxAzs: 2 }); const dbInstance = new rds.DatabaseInstance(stack, 'dbInstance', { - engine: rds.DatabaseInstanceEngine.POSTGRES, + engine: rds.DatabaseInstanceEngine.postgres({ + version: rds.PostgresEngineVersion.VER_11_5, + }), instanceType: ec2.InstanceType.of(ec2.InstanceClass.BURSTABLE3, ec2.InstanceSize.MEDIUM), credentials: rds.Credentials.fromUsername('master', { excludeCharacters: '"@/\\', diff --git a/packages/@aws-cdk/aws-rds/test/test.proxy.ts b/packages/@aws-cdk/aws-rds/test/test.proxy.ts index 83d79146cebf7..eceec3011e584 100644 --- a/packages/@aws-cdk/aws-rds/test/test.proxy.ts +++ b/packages/@aws-cdk/aws-rds/test/test.proxy.ts @@ -123,8 +123,6 @@ export = { }, ], })); - - // THEN expect(stack).to(haveResourceLike('AWS::RDS::DBProxyTargetGroup', { DBProxyName: { Ref: 'ProxyCB0DFB71', @@ -138,6 +136,22 @@ export = { DBInstanceIdentifiers: ABSENT, TargetGroupName: 'default', })); + expect(stack).to(haveResourceLike('AWS::EC2::SecurityGroupIngress', { + IpProtocol: 'tcp', + Description: 'Allow connections to the database Cluster from the Proxy', + FromPort: { + 'Fn::GetAtt': ['DatabaseB269D8BB', 'Endpoint.Port'], + }, + GroupId: { + 'Fn::GetAtt': ['DatabaseSecurityGroup5C91FDCB', 'GroupId'], + }, + SourceSecurityGroupId: { + 'Fn::GetAtt': ['ProxyProxySecurityGroupC42FC3CE', 'GroupId'], + }, + ToPort: { + 'Fn::GetAtt': ['DatabaseB269D8BB', 'Endpoint.Port'], + }, + })); test.done(); }, @@ -205,6 +219,7 @@ export = { engine: rds.DatabaseClusterEngine.auroraPostgres({ version: rds.AuroraPostgresEngineVersion.VER_9_6_11, }), + port: 5432, }); new rds.DatabaseProxy(stack, 'Proxy', { @@ -221,6 +236,10 @@ export = { 'my-cluster', ], })); + expect(stack).to(haveResourceLike('AWS::EC2::SecurityGroup', { + GroupDescription: 'SecurityGroup for Database Proxy', + VpcId: { Ref: 'VPCB9E5F0B4' }, + })); test.done(); }, @@ -294,6 +313,7 @@ export = { 'cluster611F8AFF', 'clusterSecretAttachment69BFCEC4', 'clusterSecretE349B730', + 'clusterSecurityGroupfromproxyProxySecurityGroupA80F0525IndirectPortA13E5F3D', 'clusterSecurityGroupF441DCEA', 'clusterSubnets81E3593F', ],