AWSTemplateFormatVersion: "2010-09-09" Description: Creates a Lambda function that handles the tenant data access. Parameters: VpcID: Type: AWS::EC2::VPC::Id PrivateSubnet1: Type: AWS::EC2::Subnet::Id PrivateSubnet2: Type: AWS::EC2::Subnet::Id LambdaRuntimeEnv: Type: String LayerVersionArn: Type: String Endpoint: Type: String RDSSecurityGroup: Type: AWS::EC2::SecurityGroup::Id ProxySecurityGroup: Type: String CreateRDSProxy: Type: String ClusterEndpointResourceId: Type: String SSLCertURL: Type: String Conditions: IsPython: !Equals [!Ref LambdaRuntimeEnv, Python] IsNodeJs: !Equals [!Ref LambdaRuntimeEnv, Nodejs] IsRDSProxy: !Equals [!Ref CreateRDSProxy, true] Transform: AWS::Serverless-2016-10-31 Resources: PythonFunction: Type: AWS::Serverless::Function DependsOn: - XraySeviceAccessPolicy - CloudWatchLogsSeviceAccessPolicy - VPCSeviceAccessPolicy - StsPermissionsLimitPolicy Properties: CodeUri: ../src/functions/python Description: Tenant data access function. Environment: Variables: AWS_STS_REGIONAL_ENDPOINTS: regional ENDPOINT: !Ref Endpoint CLUSTER_ENDPOINT_RESOURCE: !Sub arn:${AWS::Partition}:rds-db:${AWS::Region}:${AWS::AccountId}:dbuser:${ClusterEndpointResourceId}/user REGION: !Ref AWS::Region IAM_ARN: !GetAtt STSRole.Arn ACCOUNT_ID: !Ref AWS::AccountId SSL_CERTIFICATE_URL: !Ref SSLCertURL FunctionName: !Sub fn-python-${AWS::StackName} Handler: access.lambda_handler Layers: - !Ref LayerVersionArn Role: !GetAtt LambdaAccessRole.Arn Runtime: python3.8 Timeout: 60 Tracing: Active VpcConfig: SecurityGroupIds: - !Ref LambdaSecurityGroup SubnetIds: - !Ref PrivateSubnet1 - !Ref PrivateSubnet2 Condition: IsPython NodeJsFunction: Type: AWS::Serverless::Function DependsOn: - XraySeviceAccessPolicy - CloudWatchLogsSeviceAccessPolicy - VPCSeviceAccessPolicy - StsPermissionsLimitPolicy Properties: CodeUri: ../src/functions/nodejs Description: Tenant data access function. Environment: Variables: AWS_STS_REGIONAL_ENDPOINTS: regional ENDPOINT: !Ref Endpoint CLUSTER_ENDPOINT_RESOURCE: !Sub arn:${AWS::Partition}:rds-db:${AWS::Region}:${AWS::AccountId}:dbuser:${ClusterEndpointResourceId}/user REGION: !Ref AWS::Region IAM_ARN: !GetAtt STSRole.Arn ACCOUNT_ID: !Ref AWS::AccountId SSL_CERTIFICATE_URL: !Ref SSLCertURL FunctionName: !Sub fn-nodejs-${AWS::StackName} Handler: index.handler Layers: - !Ref LayerVersionArn Role: !GetAtt LambdaAccessRole.Arn Runtime: nodejs12.x Timeout: 60 Tracing: Active VpcConfig: SecurityGroupIds: - !Ref LambdaSecurityGroup SubnetIds: - !Ref PrivateSubnet1 - !Ref PrivateSubnet2 Condition: IsNodeJs LambdaSecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: Lambda Security Group SecurityGroupEgress: - CidrIp: 0.0.0.0/0 Description: Allow all https outbound traffic IpProtocol: tcp FromPort: 443 ToPort: 443 - DestinationSecurityGroupId: !If - IsRDSProxy - !Ref ProxySecurityGroup - !Ref RDSSecurityGroup Description: Allow db outbound traffic IpProtocol: tcp FromPort: 3306 ToPort: 3306 VpcId: !Ref VpcID LambdaToRDSIngress: Type: AWS::EC2::SecurityGroupIngress Properties: IpProtocol: tcp Description: Lambda to RDS ingress rule FromPort: 3306 GroupId: !Ref RDSSecurityGroup SourceSecurityGroupId: !Ref LambdaSecurityGroup ToPort: 3306 LambdaToProxyIngress: Type: AWS::EC2::SecurityGroupIngress Properties: IpProtocol: tcp Description: Lambda to Proxy ingress rule FromPort: 3306 GroupId: !Ref ProxySecurityGroup SourceSecurityGroupId: !Ref LambdaSecurityGroup ToPort: 3306 Condition: IsRDSProxy LambdaAccessRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Principal: Service: lambda.amazonaws.com Action: sts:AssumeRole STSRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Principal: AWS: - !Sub arn:aws:sts::${AWS::AccountId}:assumed-role/${LambdaAccessRole}/fn-python-${AWS::StackName} - !Sub arn:aws:sts::${AWS::AccountId}:assumed-role/${LambdaAccessRole}/fn-nodejs-${AWS::StackName} Action: sts:AssumeRole XraySeviceAccessPolicy: Type: AWS::IAM::ManagedPolicy Properties: Description: !Sub Allows the Access Function Lambda to access X-Ray. Stack ${AWS::StackName} PolicyDocument: Version: "2012-10-17" Statement: Effect: Allow Action: - xray:PutTraceSegments - xray:PutTelemetryRecords - xray:GetSamplingRules - xray:GetSamplingTargets - xray:GetSamplingStatisticSummaries Resource: '*' Roles: - !Ref LambdaAccessRole CloudWatchLogsSeviceAccessPolicy: Type: AWS::IAM::ManagedPolicy Properties: Description: !Sub Allows the Access Function Lambda to access CloudWatch logs. Stack ${AWS::StackName} PolicyDocument: Version: "2012-10-17" Statement: Effect: Allow Action: - logs:CreateLogGroup - logs:CreateLogStream - logs:PutLogEvents Resource: - !Sub arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:* - !Sub arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:*:log-stream:* Roles: - !Ref LambdaAccessRole VPCSeviceAccessPolicy: Type: AWS::IAM::ManagedPolicy Properties: Description: !Sub Allows the Access Function Lambda to create and delete network interfaces, and assign and unassign private IP addresses. Stack ${AWS::StackName} PolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Action: - ec2:DescribeNetworkInterfaces Resource: '*' - Effect: Allow Action: - ec2:CreateNetworkInterface - ec2:DeleteNetworkInterface - ec2:AssignPrivateIpAddresses - ec2:UnassignPrivateIpAddresses Resource: '*' Roles: - !Ref LambdaAccessRole StsPermissionsLimitPolicy: Type: AWS::IAM::ManagedPolicy Properties: Description: !Sub This policy provides a boundary on what can be assumed when the Access Function is trigged, using STS:AssumeRole. Stack ${AWS::StackName} PolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Action: rds-db:connect Resource: !Sub arn:aws:rds-db:${AWS::Region}:${AWS::AccountId}:dbuser:* Roles: - !Ref STSRole Outputs: PythonFunction: Value: !Ref PythonFunction Condition: IsPython NodeJsFunction: Value: !Ref NodeJsFunction Condition: IsNodeJs