如何让公有 APIG 访问 VPC 内的 lambda?



我有一个部署为 aws lambda 的小型 Web 应用程序,当通过 lambda 管理控制台进行测试时,该应用程序可以正常运行。但是,通过 apig 控制台运行测试会引发此错误:

Sun Jan 26 21:43:30 UTC 2020 : Execution failed due to configuration error: Invalid permissions on Lambda function

但是,apig 是使用执行权限预配的:

apiGatewayRole:
DependsOn:
- squashApp
Type: "AWS::IAM::Role"
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service:
- "apigateway.amazonaws.com"
Action: "sts:AssumeRole"
Path: "/"
ManagedPolicyArns:
- !Sub "arn:${AWS::Partition}:iam::aws:policy/service-role/AmazonAPIGatewayPushToCloudWatchLogs"
Policies:
- PolicyName: "API_Service_Role_Policy"
PolicyDocument:
Version: "2012-10-17"
Statement:
- Action: "lambda:InvokeFunction"
Resource: !GetAtt "squashApp.Arn"
Effect: "Allow"
lambdaApiGatewayInvoke:
Type: "AWS::Lambda::Permission"
Properties:
Action: "lambda:InvokeFunction"
FunctionName: !GetAtt "squashApp.Arn"
Principal: "apigateway.amazonaws.com"
SourceArn: !Sub "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${apiGateway}/*/*/"

以前,权限足以允许 lambda 调用(最近添加了 IAM 策略(。在 VPC 内移动 lambda 后,错误开始发生。

squashApp:
Type: AWS::Lambda::Function
Properties:
Environment:
Variables:
GO_ENV: production
DB_HOST: !GetAtt "squashRDS.Endpoint.Address"
DB_PORT: !GetAtt "squashRDS.Endpoint.Port"
DB_USER_NAME: !Sub "${rdsUser}"
DB_USER_PASS: !Sub "${rdsPassword}"
Code:
S3Bucket: '${lambdaHandoffBucket}'
S3Key: !Sub 'functions/${lambdaVersion}/${lambdaHash}' # TODO make parameter
Handler: 'app'
Role: !GetAtt "squashLambdaRole.Arn"
Runtime: 'go1.x'
Timeout: 2
FunctionName: !Ref "lambdaFunctionName"
VpcConfig:
SecurityGroupIds:
- !Ref "lambdaSecurtiyGroup"
SubnetIds:
- !Ref "privateSubnet1"
- !Ref "privateSubnet2"
vpc:
Type: AWS::EC2::VPC
Properties:
CidrBlock: 10.192.0.0/16
EnableDnsSupport: true
EnableDnsHostnames: true
privateSubnet1:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref vpc
CidrBlock: 10.192.21.0/24
MapPublicIpOnLaunch: false
AvailabilityZone: !Ref "PrimaryAZ"
Tags:
- Key: Name
Value: !Sub ${apiGatewayStageName} Private Subnet (AZ1)
privateSubnet2:
Type: "AWS::EC2::Subnet"
Properties:
VpcId: !Ref vpc
CidrBlock: 10.192.30.0/24
MapPublicIpOnLaunch: false
AvailabilityZone: !Ref "SecondaryAZ"
privateRouteTable1:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref vpc
privateSubnetRouteTableAssociation1:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref privateRouteTable1
SubnetId: !Ref privateSubnet1
privateRouteTable2:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref vpc
privateSubnetRouteTableAssociation2:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref privateRouteTable1
SubnetId: !Ref privateSubnet1
noIngressSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: "no-ingress-sg"
GroupDescription: "Security group with no ingress rule"
VpcId: !Ref vpc

完整的云形成资源部分:

Resources:
apiGateway:
Type: "AWS::ApiGateway::RestApi"
Properties:
Name: "SquashAPI"
Description: "Squash Box Ladder"
apiGatewayRootMethod:
Type: "AWS::ApiGateway::Method"
Properties:
AuthorizationType: "NONE"
# This can be ANY | GET| POST | etc and references the api http method as seen by a client
HttpMethod: "ANY"
Integration:
# This must be post or the integration between gateway and lambda fails
IntegrationHttpMethod: "POST"
Type: "AWS_PROXY"
Uri: !Sub
- "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${lambdaArn}/invocations"
- lambdaArn: !GetAtt "squashApp.Arn"
ResourceId: !GetAtt "apiGateway.RootResourceId"
RestApiId: !Ref "apiGateway"
apiGatewayCatchAllProxy:
Type: AWS::ApiGateway::Resource
Properties:
PathPart: "{proxy+}"
RestApiId: !Ref "apiGateway"
ParentId: !GetAtt "apiGateway.RootResourceId"
apiGatewayCatchAllMethod:
Type: "AWS::ApiGateway::Method"
Properties:
AuthorizationType: "NONE"
HttpMethod: "ANY"
Integration:
# This must be post or the integration between gateway and lambda fails
IntegrationHttpMethod: "POST"
Type: "AWS_PROXY"
Uri: !Sub
- "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${lambdaArn}/invocations"
- lambdaArn: !GetAtt "squashApp.Arn"
ResourceId: !Ref "apiGatewayCatchAllProxy"
RestApiId: !Ref "apiGateway"
apiGatewayDeployment:
Type: "AWS::ApiGateway::Deployment"
DependsOn:
- "apiGatewayRootMethod"
- "apiGatewayCatchAllMethod"
Properties:
RestApiId: !Ref "apiGateway"
StageName: !Ref "apiGatewayStageName"
squashApp:
Type: AWS::Lambda::Function
Properties:
Environment:
Variables:
GO_ENV: production
DB_HOST: !GetAtt "squashRDS.Endpoint.Address"
DB_PORT: !GetAtt "squashRDS.Endpoint.Port"
DB_USER_NAME: !Sub "${rdsUser}"
DB_USER_PASS: !Sub "${rdsPassword}"
Code:
S3Bucket: '${lambdaHandoffBucket}'
S3Key: !Sub 'functions/${lambdaVersion}/${lambdaHash}' # TODO make parameter
Handler: 'app'
Role: !GetAtt "squashLambdaRole.Arn"
Runtime: 'go1.x'
Timeout: 2
FunctionName: !Ref "lambdaFunctionName"
VpcConfig:
SecurityGroupIds:
- !Ref "lambdaSecurtiyGroup"
SubnetIds:
- !Ref "privateSubnet1"
- !Ref "privateSubnet2"
lambdaSecurtiyGroup:
Type: "AWS::EC2::SecurityGroup"
Properties:
GroupDescription: For Lambda Application
VpcId: !Ref vpc
lambdaEgres:
Type: AWS::EC2::SecurityGroupEgress
Properties:
GroupId: !Ref lambdaSecurtiyGroup
IpProtocol: tcp
FromPort: !GetAtt "squashRDS.Endpoint.Port"
ToPort: !GetAtt "squashRDS.Endpoint.Port"
CidrIp: !GetAtt 'vpc.CidrBlock'
lambdaApiGatewayInvoke:
Type: "AWS::Lambda::Permission"
Properties:
Action: "lambda:InvokeFunction"
FunctionName: !GetAtt "squashApp.Arn"
Principal: "apigateway.amazonaws.com"
SourceArn: !Sub "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${apiGateway}/*/*/"
squashLambdaRole:
Type: "AWS::IAM::Role"
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Action:
- "sts:AssumeRole"
Effect: "Allow"
Principal:
Service:
- "lambda.amazonaws.com"
ManagedPolicyArns:
- "arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole"
Policies:
- PolicyDocument:
Version: "2012-10-17"
Statement:
- Action:
- "logs:CreateLogGroup"
- "logs:CreateLogStream"
- "logs:PutLogEvents"
Effect: "Allow"
Resource:
- !Sub "arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/${lambdaFunctionName}:*"
PolicyName: "lambda"
lambdaLogGroup:
Type: "AWS::Logs::LogGroup"
Properties:
LogGroupName: !Sub "/aws/lambda/${lambdaFunctionName}"
RetentionInDays: 14
apiGatewayRole:
DependsOn:
- squashApp
Type: "AWS::IAM::Role"
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service:
- "apigateway.amazonaws.com"
Action: "sts:AssumeRole"
Path: "/"
ManagedPolicyArns:
- !Sub "arn:${AWS::Partition}:iam::aws:policy/service-role/AmazonAPIGatewayPushToCloudWatchLogs"
Policies:
- PolicyName: "API_Service_Role_Policy"
PolicyDocument:
Version: "2012-10-17"
Statement:
- Action: "lambda:InvokeFunction"
Resource: !GetAtt "squashApp.Arn"
Effect: "Allow"
apiGwAccountConfig:
Type: "AWS::ApiGateway::Account"
Properties:
CloudWatchRoleArn: !GetAtt "apiGatewayRole.Arn"
squashRDS:
Type: AWS::RDS::DBInstance
Properties:
DBInstanceClass: db.t2.micro
AllocatedStorage: 20
StorageType: "gp2"
Engine: "postgres"
MasterUsername: !Sub "${rdsUser}"
MasterUserPassword: !Sub "${rdsPassword}"
DBSubnetGroupName: !Ref "rdsSubnetGroup"
VPCSecurityGroups:
- !Ref rdsSecurityGroup
DeletionPolicy: Snapshot
rdsSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: For RDS Instance
VpcId: !Ref vpc
Tags:
- Key: Name
Value: RDS-SecurityGroup
rdsSubnetGroup:
Type: "AWS::RDS::DBSubnetGroup"
Properties:
DBSubnetGroupName: "RDS Subnet Group"
DBSubnetGroupDescription: Subnet including the RDS into the VPC
SubnetIds:
- !Ref "privateSubnet1"
- !Ref "privateSubnet2"
rdsIngres:
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: !Ref rdsSecurityGroup
IpProtocol: tcp
FromPort: !GetAtt "squashRDS.Endpoint.Port"
ToPort: !GetAtt "squashRDS.Endpoint.Port"
CidrIp: !GetAtt 'vpc.CidrBlock'
vpc:
Type: AWS::EC2::VPC
Properties:
CidrBlock: 10.192.0.0/16
EnableDnsSupport: true
EnableDnsHostnames: true
privateSubnet1:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref vpc
CidrBlock: 10.192.21.0/24
MapPublicIpOnLaunch: false
AvailabilityZone: !Ref "PrimaryAZ"
privateSubnet2:
Type: "AWS::EC2::Subnet"
Properties:
VpcId: !Ref vpc
CidrBlock: 10.192.30.0/24
MapPublicIpOnLaunch: false
AvailabilityZone: !Ref "SecondaryAZ"
privateRouteTable1:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref vpc
privateSubnetRouteTableAssociation1:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref privateRouteTable1
SubnetId: !Ref privateSubnet1
privateRouteTable2:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref vpc
privateSubnetRouteTableAssociation2:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref privateRouteTable1
SubnetId: !Ref privateSubnet1
noIngressSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: "no-ingress-sg"
GroupDescription: "Security group with no ingress rule"
VpcId: !Ref vpc
Outputs:
apiGatewayInvokeURL:
Value: !Sub "https://${apiGateway}.execute-api.${AWS::Region}.amazonaws.com/${apiGatewayStageName}"
lambdaArn:
Value: !GetAtt "squashApp.Arn"

在"squashLambdaRole"中,尝试在服务主体列表中添加"apigateway.amazonaws.com"+ 附加名为"AWSLambdaRole"的 AWS 托管策略。 它解决了我的问题,但我不明白为什么:/

我唯一能想到的是,API 网关源 Arn 应该以资源路径结尾。在您的情况下,它以/.

我会在末尾添加一个*,以允许任何资源路径调用 lambda。一旦它工作,您可以将其更改为特定路径。

lambdaApiGatewayInvoke:
Type: "AWS::Lambda::Permission"
Properties:
Action: "lambda:InvokeFunction"
FunctionName: !GetAtt "squashApp.Arn"
Principal: "apigateway.amazonaws.com"
SourceArn: !Sub "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${apiGateway}/*/*/*"

希望这有帮助。 祝你好运。

最新更新