API_CONFIGURATION_ERROR 为 AWS::Serverless::Api 使用 AWS_IAM 授权方时
API_CONFIGURATION_ERROR when using AWS_IAM authorizer for AWS::Serverless::Api
我正在尝试使用 sam 模板设置具有单个 lambda 函数的私有 API,但无论我做什么,API 网关都会记录 API_CONFIGURATION_ERROR。我不知道我做错了什么。 docs isn't very specific about it. According to this example 我认为我的模板看起来不错。
我的代码使用了 axios 和 aws4,我也尝试过使用 fetch 而不是 axios。我在使用 Postman 时也遇到了同样的错误,所以我真的不认为我的请求有问题。如果我只是禁用 IAM 授权,一切都很好。
这是我的 api 模板和我尝试调用的 lambda 函数。
AWSTemplateFormatVersion: "2010-09-09"
Transform: AWS::Serverless-2016-10-31
Description: >
iam-api-example
Parameters:
SamplePrivateApiStageName:
Type: String
Default: endpoint
SampleTableName:
Type: String
Default: SampleTable
Globals:
Function:
Runtime: nodejs14.x
Timeout: 10
MemorySize: 128
Handler: app.lambdaHandler
Resources:
SamplePrivateApi:
Type: AWS::Serverless::Api
Properties:
Name: SamplePrivateApi
StageName: !Ref SamplePrivateApiStageName
Auth:
DefaultAuthorizer: AWS_IAM
AccessLogSetting:
DestinationArn: !GetAtt SamplePrivateApiAccessLogs.Arn
Format: '{ "requestId":"$context.requestId", "ip": "$context.identity.sourceIp", "requestTime":"$context.requestTime", "httpMethod":"$context.httpMethod","routeKey":"$context.routeKey", "path":"$context.path", "status":"$context.status","protocol":"$context.protocol", "responseLength":"$context.responseLength", "errorMessage":"$context.error.message", "responseType":"$context.error.responseType" }'
SamplePrivateApiAccessLogs:
Type: AWS::Logs::LogGroup
GetQuestionFn:
Type: AWS::Serverless::Function
Properties:
Policies:
- AmazonDynamoDBFullAccess
CodeUri: build/lambdas/private/advertisement/get-question
Environment:
Variables:
TABLE_NAME: !Ref SampleTableName
Events:
GetQuestionFnEvent:
Type: Api
Properties:
Path: /questions
Method: GET
RestApiId: !Ref SamplePrivateApi
#Auth:
#Authorizer: "NONE"
如果我通过在 lambda 函数中取消注释最后两个授权行(即使用 Authorizer:“NONE”)来停止使用 defaultAuthorizer,一切正常。所以只有当我让 defaultAuthorizer 负责时它才会失败。
我在 API 日志中得到的错误是:
{
"requestId": "12311ec0-732d-4088-a641-71f05e4bd418",
"ip": "x.xx.xxx.xxx",
"requestTime": "16/Jul/2021:12:37:01 +0000",
"httpMethod": "GET",
"routeKey": "-",
"path": "/endpoint/questions",
"status": "500",
"protocol": "HTTP/1.1",
"responseLength": "36",
"errorMessage": "Internal server error",
"responseType": "API_CONFIGURATION_ERROR"
}
在 Postman 中,我使用 AWS 签名授权以及来自具有 AmazonAPIGatewayInvokeFullAccess 策略的 IAM 用户的访问密钥和密钥。区域也应该是正确的。
我在另一个 lambda 函数(也有 AmazonAPIGatewayInvokeFullAccess 策略)中使用的 axios 代码目前看起来像这样。
import AWS from 'aws-sdk'
const aws4 = require('aws4')
const fetchQuestion = async () => {
// Host is domain without https://
const urlParts = QUESTION_API_DOMAIN.split('://')
const host = urlParts.length > 1 ? urlParts[1] : ''
if (!host) {
console.error('No host could be extracted.')
return null
}
const request = {
host,
method: 'GET',
url: `${QUESTION_API_DOMAIN}/endpoint/questions`,
path: `/endpoint/questions`,
}
const secretAccessKey = AWS.config.credentials?.secretAccessKey
const accessKeyId = AWS.config.credentials?.accessKeyId
if (!secretAccessKey || !accessKeyId) {
console.error('No credentials could be found.')
return null
}
let signedRequest = aws4.sign(request, {
secretAccessKey: secretAccessKey,
accessKeyId: accessKeyId,
sessionToken: AWS.config.credentials?.sessionToken,
})
// delete signedRequest.headers['Host']
// delete signedRequest.headers['Content-Length']
try {
const response = await axios(signedRequest)
return response.data
} catch (err) {
console.error('Error while retrieving question:', err)
}
}
不知道哪里出了问题。
您的 API 网关需要分配一个角色,允许它调用 lambda 函数(授权方)
尝试在 API 验证部分 (https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-api-apiauth.html) 中使用 InvokeRole
属性。
---更新了----
调用角色 arn 必须指向角色 arn 而不是用户,请参见下面的示例
Type: AWS::Serverless::Api
Properties:
Name: SamplePrivateApi
StageName: !Ref SamplePrivateApiStageName
Auth:
DefaultAuthorizer: AWS_IAM
InvokeRole: !GetAtt APIGatewayRole.Arn
AccessLogSetting:
DestinationArn: !GetAtt SamplePrivateApiAccessLogs.Arn
Format: '{ "requestId":"$context.requestId", "ip": "$context.identity.sourceIp", "requestTime":"$context.requestTime", "httpMethod":"$context.httpMethod","routeKey":"$context.routeKey", "path":"$context.path", "status":"$context.status","protocol":"$context.protocol", "responseLength":"$context.responseLength", "errorMessage":"$context.error.message", "responseType":"$context.error.responseType" }'
SamplePrivateApiAccessLogs:
Type: AWS::Logs::LogGroup
APIGatewayRole:
Type: "AWS::IAM::Role"
Properties:
Path: "/"
ManagedPolicyArns:
- "arn:aws:iam::aws:policy/service-role/AmazonAPIGatewayPushToCloudWatchLogs"
Policies:
- PolicyName: "authorizerLambdaInvokeAccess"
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: "Allow"
Action:
- lambda:InvokeAsync
- lambda:InvokeFunction
Resource: !Sub ${AuthorizerLambdaFunction.Arn}
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Sid: "AllowApiGatewayServiceToAssumeRole"
Effect: "Allow"
Action:
- "sts:AssumeRole"
Principal:
Service:
- "apigateway.amazonaws.com" ```
我正在尝试使用 sam 模板设置具有单个 lambda 函数的私有 API,但无论我做什么,API 网关都会记录 API_CONFIGURATION_ERROR。我不知道我做错了什么。 docs isn't very specific about it. According to this example 我认为我的模板看起来不错。
我的代码使用了 axios 和 aws4,我也尝试过使用 fetch 而不是 axios。我在使用 Postman 时也遇到了同样的错误,所以我真的不认为我的请求有问题。如果我只是禁用 IAM 授权,一切都很好。
这是我的 api 模板和我尝试调用的 lambda 函数。
AWSTemplateFormatVersion: "2010-09-09"
Transform: AWS::Serverless-2016-10-31
Description: >
iam-api-example
Parameters:
SamplePrivateApiStageName:
Type: String
Default: endpoint
SampleTableName:
Type: String
Default: SampleTable
Globals:
Function:
Runtime: nodejs14.x
Timeout: 10
MemorySize: 128
Handler: app.lambdaHandler
Resources:
SamplePrivateApi:
Type: AWS::Serverless::Api
Properties:
Name: SamplePrivateApi
StageName: !Ref SamplePrivateApiStageName
Auth:
DefaultAuthorizer: AWS_IAM
AccessLogSetting:
DestinationArn: !GetAtt SamplePrivateApiAccessLogs.Arn
Format: '{ "requestId":"$context.requestId", "ip": "$context.identity.sourceIp", "requestTime":"$context.requestTime", "httpMethod":"$context.httpMethod","routeKey":"$context.routeKey", "path":"$context.path", "status":"$context.status","protocol":"$context.protocol", "responseLength":"$context.responseLength", "errorMessage":"$context.error.message", "responseType":"$context.error.responseType" }'
SamplePrivateApiAccessLogs:
Type: AWS::Logs::LogGroup
GetQuestionFn:
Type: AWS::Serverless::Function
Properties:
Policies:
- AmazonDynamoDBFullAccess
CodeUri: build/lambdas/private/advertisement/get-question
Environment:
Variables:
TABLE_NAME: !Ref SampleTableName
Events:
GetQuestionFnEvent:
Type: Api
Properties:
Path: /questions
Method: GET
RestApiId: !Ref SamplePrivateApi
#Auth:
#Authorizer: "NONE"
如果我通过在 lambda 函数中取消注释最后两个授权行(即使用 Authorizer:“NONE”)来停止使用 defaultAuthorizer,一切正常。所以只有当我让 defaultAuthorizer 负责时它才会失败。
我在 API 日志中得到的错误是:
{
"requestId": "12311ec0-732d-4088-a641-71f05e4bd418",
"ip": "x.xx.xxx.xxx",
"requestTime": "16/Jul/2021:12:37:01 +0000",
"httpMethod": "GET",
"routeKey": "-",
"path": "/endpoint/questions",
"status": "500",
"protocol": "HTTP/1.1",
"responseLength": "36",
"errorMessage": "Internal server error",
"responseType": "API_CONFIGURATION_ERROR"
}
在 Postman 中,我使用 AWS 签名授权以及来自具有 AmazonAPIGatewayInvokeFullAccess 策略的 IAM 用户的访问密钥和密钥。区域也应该是正确的。
我在另一个 lambda 函数(也有 AmazonAPIGatewayInvokeFullAccess 策略)中使用的 axios 代码目前看起来像这样。
import AWS from 'aws-sdk'
const aws4 = require('aws4')
const fetchQuestion = async () => {
// Host is domain without https://
const urlParts = QUESTION_API_DOMAIN.split('://')
const host = urlParts.length > 1 ? urlParts[1] : ''
if (!host) {
console.error('No host could be extracted.')
return null
}
const request = {
host,
method: 'GET',
url: `${QUESTION_API_DOMAIN}/endpoint/questions`,
path: `/endpoint/questions`,
}
const secretAccessKey = AWS.config.credentials?.secretAccessKey
const accessKeyId = AWS.config.credentials?.accessKeyId
if (!secretAccessKey || !accessKeyId) {
console.error('No credentials could be found.')
return null
}
let signedRequest = aws4.sign(request, {
secretAccessKey: secretAccessKey,
accessKeyId: accessKeyId,
sessionToken: AWS.config.credentials?.sessionToken,
})
// delete signedRequest.headers['Host']
// delete signedRequest.headers['Content-Length']
try {
const response = await axios(signedRequest)
return response.data
} catch (err) {
console.error('Error while retrieving question:', err)
}
}
不知道哪里出了问题。
您的 API 网关需要分配一个角色,允许它调用 lambda 函数(授权方)
尝试在 API 验证部分 (https://docs.aws.amazon.com/serverless-application-model/latest/developerguide/sam-property-api-apiauth.html) 中使用 InvokeRole
属性。
---更新了----
调用角色 arn 必须指向角色 arn 而不是用户,请参见下面的示例
Type: AWS::Serverless::Api
Properties:
Name: SamplePrivateApi
StageName: !Ref SamplePrivateApiStageName
Auth:
DefaultAuthorizer: AWS_IAM
InvokeRole: !GetAtt APIGatewayRole.Arn
AccessLogSetting:
DestinationArn: !GetAtt SamplePrivateApiAccessLogs.Arn
Format: '{ "requestId":"$context.requestId", "ip": "$context.identity.sourceIp", "requestTime":"$context.requestTime", "httpMethod":"$context.httpMethod","routeKey":"$context.routeKey", "path":"$context.path", "status":"$context.status","protocol":"$context.protocol", "responseLength":"$context.responseLength", "errorMessage":"$context.error.message", "responseType":"$context.error.responseType" }'
SamplePrivateApiAccessLogs:
Type: AWS::Logs::LogGroup
APIGatewayRole:
Type: "AWS::IAM::Role"
Properties:
Path: "/"
ManagedPolicyArns:
- "arn:aws:iam::aws:policy/service-role/AmazonAPIGatewayPushToCloudWatchLogs"
Policies:
- PolicyName: "authorizerLambdaInvokeAccess"
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: "Allow"
Action:
- lambda:InvokeAsync
- lambda:InvokeFunction
Resource: !Sub ${AuthorizerLambdaFunction.Arn}
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Sid: "AllowApiGatewayServiceToAssumeRole"
Effect: "Allow"
Action:
- "sts:AssumeRole"
Principal:
Service:
- "apigateway.amazonaws.com" ```