amazon-web-servicesaws-lambdaamazon-rdsamazon-vpcamazon-rds-proxy

Lambda RDS Proxy connection from different VPC


I have two AWS accounts with VPCs connected with peer connections. I have RDS Proxy on account 1 and Lambda in the private, isolated subnet on account 2.

I cannot figure out how to connect to RDS Proxy. I was trying all possible VPC endpoints and interfaces and whatnot.

The only way I managed to connect was through NAT Gateway, but it's expensive. And to be honest, weird, if I want to keep a private network.

Is it possible to have a PrivateLink or something? How should I connect to RDS Proxy from Lambda?

I have attached the CF template (I hope I cleaned it from all sensitive data 😑):

{
  "Resources": {
    "vpcA2121C38": {
      "Type": "AWS::EC2::VPC",
      "Properties": {
        "CidrBlock": "10.0.0.0/16",
        "EnableDnsHostnames": true,
        "EnableDnsSupport": true,
        "InstanceTenancy": "default"
      },
      "Metadata": {
        "aws:cdk:path": "Mws/vpc/Resource"
      }
    },
    "vpcPrivateSubnet1Subnet934893E8": {
      "Type": "AWS::EC2::Subnet",
      "Properties": {
        "CidrBlock": "10.0.0.0/26",
        "VpcId": {
          "Ref": "vpcA2121C38"
        },
        "AvailabilityZone": {
          "Fn::Select": [
            0,
            {
              "Fn::GetAZs": ""
            }
          ]
        },
        "MapPublicIpOnLaunch": false
      },
      "Metadata": {
        "aws:cdk:path": "Mws/vpc/PrivateSubnet1/Subnet"
      }
    },
    "vpcPrivateSubnet1RouteTableB41A48CC": {
      "Type": "AWS::EC2::RouteTable",
      "Properties": {
        "VpcId": {
          "Ref": "vpcA2121C38"
        }
      },
      "Metadata": {
        "aws:cdk:path": "Mws/vpc/PrivateSubnet1/RouteTable"
      }
    },
    "vpcPrivateSubnet1RouteTableAssociation67945127": {
      "Type": "AWS::EC2::SubnetRouteTableAssociation",
      "Properties": {
        "RouteTableId": {
          "Ref": "vpcPrivateSubnet1RouteTableB41A48CC"
        },
        "SubnetId": {
          "Ref": "vpcPrivateSubnet1Subnet934893E8"
        }
      },
      "Metadata": {
        "aws:cdk:path": "Mws/vpc/PrivateSubnet1/RouteTableAssociation"
      }
    },
    "vpcS3CB758969": {
      "Type": "AWS::EC2::VPCEndpoint",
      "Properties": {
        "ServiceName": {
          "Fn::Join": [
            "",
            [
              "com.amazonaws.",
              {
                "Ref": "AWS::Region"
              },
              ".s3"
            ]
          ]
        },
        "VpcId": {
          "Ref": "vpcA2121C38"
        },
        "RouteTableIds": [
          {
            "Ref": "vpcPrivateSubnet1RouteTableB41A48CC"
          }
        ],
        "VpcEndpointType": "Gateway"
      },
      "Metadata": {
        "aws:cdk:path": "Mws/vpc/S3/Resource"
      }
    },
    "vpcPeertomainaccount833D3E2C": {
      "Type": "AWS::EC2::VPCPeeringConnection",
      "Properties": {
        "PeerVpcId": "vpc-f04b939b",
        "VpcId": {
          "Ref": "vpcA2121C38"
        },
        "PeerOwnerId": "XXXX__ACCOINT_1__XXXXXX",
        "PeerRoleArn": "arn:aws:iam::XXXX__ACCOINT_1__XXXXXX:role/VPCPeerConnection"
        
      },
      "Metadata": {
        "aws:cdk:path": "Mws/vpc/Peer to main account"
      }
    },
    "producerServiceRoleEBCB54D0": {
      "Type": "AWS::IAM::Role",
      "Properties": {
        "AssumeRolePolicyDocument": {
          "Statement": [
            {
              "Action": "sts:AssumeRole",
              "Effect": "Allow",
              "Principal": {
                "Service": "lambda.amazonaws.com"
              }
            }
          ],
          "Version": "2012-10-17"
        },
        "ManagedPolicyArns": [
          {
            "Fn::Join": [
              "",
              [
                "arn:",
                {
                  "Ref": "AWS::Partition"
                },
                ":iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
              ]
            ]
          },
          {
            "Fn::Join": [
              "",
              [
                "arn:",
                {
                  "Ref": "AWS::Partition"
                },
                ":iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole"
              ]
            ]
          }
        ]
      },
      "Metadata": {
        "aws:cdk:path": "Mws/producer/producer/ServiceRole/Resource"
      }
    },
    "producerServiceRoleDefaultPolicyEA5B80A1": {
      "Type": "AWS::IAM::Policy",
      "Properties": {
        "PolicyDocument": {
          "Statement": [
            {
              "Action": [
                "xray:PutTraceSegments",
                "xray:PutTelemetryRecords"
              ],
              "Effect": "Allow",
              "Resource": "*"
            },
            {
              "Action": [
                "s3:GetObject*",
                "s3:GetBucket*",
                "s3:List*"
              ],
              "Effect": "Allow",
              "Resource": [
                {
                  "Fn::Join": [
                    "",
                    [
                      "arn:",
                      {
                        "Ref": "AWS::Partition"
                      },
                      ":s3:::",
                      {
                        "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}"
                      }
                    ]
                  ]
                },
                {
                  "Fn::Join": [
                    "",
                    [
                      "arn:",
                      {
                        "Ref": "AWS::Partition"
                      },
                      ":s3:::",
                      {
                        "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}"
                      },
                      "/*"
                    ]
                  ]
                }
              ]
            }
          ],
          "Version": "2012-10-17"
        },
        "PolicyName": "producerServiceRoleDefaultPolicyEA5B80A1",
        "Roles": [
          {
            "Ref": "producerServiceRoleEBCB54D0"
          }
        ]
      },
      "Metadata": {
        "aws:cdk:path": "Mws/producer/producer/ServiceRole/DefaultPolicy/Resource"
      }
    },
    "producerSecurityGroup9AA1BE28": {
      "Type": "AWS::EC2::SecurityGroup",
      "Properties": {
        "GroupDescription": "Automatic security group for Lambda Function Mwsproducer416E938A",
        "SecurityGroupEgress": [
          {
            "CidrIp": "0.0.0.0/0",
            "Description": "Allow all outbound traffic by default",
            "IpProtocol": "-1"
          }
        ],
        
        "VpcId": {
          "Ref": "vpcA2121C38"
        }
      },
      "Metadata": {
        "aws:cdk:path": "Mws/producer/producer/SecurityGroup/Resource"
      }
    },
    "producerAD962441": {
      "Type": "AWS::Lambda::Function",
      "Properties": {
        "Code": {
          "S3Bucket": {
            "Fn::Sub": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}"
          },
          "S3Key": "dc05df325f7034421a587e7ee47aed301c7472d001152e686053dd9d5c45c164.zip"
        },
        "Role": {
          "Fn::GetAtt": [
            "producerServiceRoleEBCB54D0",
            "Arn"
          ]
        },
        "Description": "Produces MWS customer orders messages",
        "Environment": {
          "Variables": {
            "DB_HOST": "mws-rds-proxy.proxy-XXXXXXXXX.eu-central-1.rds.amazonaws.com",
            "DB_NAME": "dev"
          }
        },
        "Handler": "lambda/mws_producer.php",
        "Layers": [
          "arn:aws:lambda:eu-central-1:209497400698:layer:php-80:18"
        ],
        "MemorySize": 1024,
        "Runtime": "provided.al2",
        
        "Timeout": 900,
        "TracingConfig": {
          "Mode": "Active"
        },
        "VpcConfig": {
          "SecurityGroupIds": [
            {
              "Fn::GetAtt": [
                "producerSecurityGroup9AA1BE28",
                "GroupId"
              ]
            }
          ],
          "SubnetIds": [
            {
              "Ref": "vpcPrivateSubnet1Subnet934893E8"
            }
          ]
        }
      },
      "DependsOn": [
        "producerServiceRoleDefaultPolicyEA5B80A1",
        "producerServiceRoleEBCB54D0"
      ]
    }
  }
}

Solution

  • Devil as we know hidden in detail. In this case, it's a Route Tables.

    enter image description here

    When we create a Peering connection between VPCs we also need to give to know our subnets how to use it. Basically, just add peering connection id (pcx-XXX) as a target for peering network.

    Took me several days to realize it. Happy happy happy!