我有一个 Cloudformation 模板(见底部),它允许我直接通过 API 网关获取 S3 对象。
获取存在的对象时效果很好,返回 200 和对象内容:
curl -i "https://xxx.execute-api.eu-west-1.amazonaws.com/prod/index.json"
HTTP/2 200
content-type: application/json
...
{"hello": "world"}
但是,当对象不存在时,我预计会出现 404 Not Found 错误,它仍然返回 200 OK:
curl -i "https:xxx.execute-api.eu-west-1.amazonaws.com/prod/abcd.json"
HTTP/2 200
content-type: application/json
content-length: 243
...
<?xml version="1.0" encoding="UTF-8"?>
<Error>
<Code>AccessDenied</Code>
<Message>Access Denied</Message>
<RequestId>CH2PRDQ2MVN0CXXX</RequestId>
<HostId>cCV8dG+6CXA5pVrzTwLqiRqMjpuW8F+iuISQRUrKDP5PugEA6f4wauU0Egmb3b1GyvLjQZgjXXX=</HostId>
</Error>%
如何修改我的 CloudFormation 模板,以便对未知 S3 密钥的请求导致 HTTP 404 响应?
Resources:
S3Bucket:
Type: AWS::S3::Bucket
ApiGatewayRestApi:
Type: AWS::ApiGateway::RestApi
Properties:
Name: S3ProxyAPI
ApiGatewayResource:
Type: AWS::ApiGateway::Resource
Properties:
RestApiId:
Ref: ApiGatewayRestApi
ParentId:
Fn::GetAtt:
- ApiGatewayRestApi
- RootResourceId
PathPart: "{proxy+}"
ApiGatewayMethod:
Type: AWS::ApiGateway::Method
Properties:
AuthorizationType: NONE
RestApiId:
Ref: ApiGatewayRestApi
ResourceId:
Ref: ApiGatewayResource
HttpMethod: GET
RequestParameters:
method.request.path.proxy: true
MethodResponses:
- StatusCode: 200
Integration:
IntegrationHttpMethod: ANY
Type: AWS
Uri:
Fn::Sub: arn:aws:apigateway:${AWS::Region}:s3:path/${S3Bucket}/{proxy}
Credentials:
Fn::GetAtt:
- ApiGatewayRole
- Arn
RequestParameters:
integration.request.path.proxy: method.request.path.proxy
PassthroughBehavior: WHEN_NO_MATCH
IntegrationResponses:
- StatusCode: 200
ApiGatewayDeployment:
Type: AWS::ApiGateway::Deployment
DependsOn: ApiGatewayMethod
Properties:
RestApiId:
Ref: ApiGatewayRestApi
StageName: prod
ApiGatewayRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: apigateway.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: ApiGatewayS3ProxyPolicy
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- s3:GetObject
Resource:
Fn::Sub: arn:aws:s3:::${S3Bucket}/*
Outputs:
ApiEndpoint:
Value:
Fn::Sub: https://${ApiGatewayRestApi}.execute-api.${AWS::Region}.amazonaws.com/prod/{proxy}
BucketName:
Value:
Ref: S3Bucket
MethodResponses
定义您的 API 方法可能使用哪些 HTTP 状态代码。 IntegrationResponses
然后处理“后端”响应到 API 网关响应的映射
他们俩一起工作。
您需要 404 状态代码的方法响应,以允许您返回 404 状态代码,然后是具有 404 选择模式的集成响应。这样,您可以从 API 网关返回针对来自 S3 的 404 错误的 404 响应.
你总是得到 200,因为没有集成响应来“捕获”404 错误(或者实际上,在本例中为 403,但更多内容见下文),即使你这样做了,你也需要一个方法响应才能将其映射到 xxx 状态代码。
像这样修改你的
ApiGatewayMethod
:
ApiGatewayMethod:
Type: AWS::ApiGateway::Method
Properties:
MethodResponses:
- StatusCode: 200
- StatusCode: 404
...
Integration:
IntegrationResponses:
- StatusCode: 200
- StatusCode: 404
SelectionPattern: '404'
...
但是,这仍然不适用于您定义的 IAM 策略 - 根据 docs:
如果您没有 s3:ListBucket 权限,Amazon S3 将返回 HTTP 状态代码 403(“访问被拒绝”)错误。
您没有配置此 IAM 权限,因此 AWS 将始终返回 403(从
<Code>AccessDenied</Code>
可以看出)以阻止 S3 目录枚举。你永远不会得到 404 来映射它。
确保您还提供
s3:ListBucket
权限,如下所示:
Policies:
- PolicyName: ApiGatewayS3ProxyPolicy
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- s3:GetObject
Resource:
Fn::Sub: arn:aws:s3:::${S3Bucket}/*
- Effect: Allow
Action:
- s3:ListBucket
Resource:
Fn::Sub: arn:aws:s3:::${S3Bucket}
这会导致 AWS 实际上针对未找到的文件返回 404 错误,并且您的 API 网关随后将 S3 404 响应“映射”(或者在本例中,只是传递)到 404 方法响应。
完整的工作YAML文件如下:
Resources:
S3Bucket:
Type: AWS::S3::Bucket
ApiGatewayRestApi:
Type: AWS::ApiGateway::RestApi
Properties:
Name: S3ProxyAPI
ApiGatewayResource:
Type: AWS::ApiGateway::Resource
Properties:
RestApiId:
Ref: ApiGatewayRestApi
ParentId:
Fn::GetAtt:
- ApiGatewayRestApi
- RootResourceId
PathPart: "{proxy+}"
ApiGatewayMethod:
Type: AWS::ApiGateway::Method
Properties:
AuthorizationType: NONE
RestApiId:
Ref: ApiGatewayRestApi
ResourceId:
Ref: ApiGatewayResource
HttpMethod: GET
RequestParameters:
method.request.path.proxy: true
MethodResponses:
- StatusCode: 200
- StatusCode: 404
Integration:
IntegrationHttpMethod: ANY
Type: AWS
Uri:
Fn::Sub: arn:aws:apigateway:${AWS::Region}:s3:path/${S3Bucket}/{proxy}
Credentials:
Fn::GetAtt:
- ApiGatewayRole
- Arn
RequestParameters:
integration.request.path.proxy: method.request.path.proxy
PassthroughBehavior: WHEN_NO_MATCH
IntegrationResponses:
- StatusCode: 200
- StatusCode: 404
SelectionPattern: '404'
ApiGatewayDeployment:
Type: AWS::ApiGateway::Deployment
DependsOn: ApiGatewayMethod
Properties:
RestApiId:
Ref: ApiGatewayRestApi
StageName: prod
ApiGatewayRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: apigateway.amazonaws.com
Action: sts:AssumeRole
Policies:
- PolicyName: ApiGatewayS3ProxyPolicy
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- s3:GetObject
Resource:
Fn::Sub: arn:aws:s3:::${S3Bucket}/*
- Effect: Allow
Action:
- s3:ListBucket
Resource:
Fn::Sub: arn:aws:s3:::${S3Bucket}
Outputs:
ApiEndpoint:
Value:
Fn::Sub: https://${ApiGatewayRestApi}.execute-api.${AWS::Region}.amazonaws.com/prod/{proxy}
BucketName:
Value:
Ref: S3Bucket
对于成功的响应,您将获得 JSON,对于失败的响应,您将获得 XML,因此您可能需要调整 CloudFormation 的其他部分,使其与您的
Content-Type
标头(如果需要)匹配。