Search code examples
amazon-web-servicesamazon-s3aws-api-gateway

API Gateway S3 Integration Signature does not match with blank folder name


I want to access a file in an S3 bucket from an API Gateway endpoint.

I have uploaded the file to an S3 bucket with a key: path//filename.txt (note the empty sub-folder name is intentional)

However, when I issue the following HTTP request:

curl -X GET 'https://api.cloud/v1/files/filename.txt'

The integration fails and returns an XML error. The API Gateway logs state:

Received response. Status: 403, Integration latency: 8 ms

Endpoint response body before transformations: 
<?xml version="1.0" encoding="UTF-8"?>
<Error>
    <Code>SignatureDoesNotMatch</Code>
    <Message>The request signature we calculated does not match the signature you provided. Check your key and signing method.</Message>
    ...
</Error>

I cannot for the life of me figure out what I am missing.

Am I not allowed to have an API Gateway integration to an S3 object that contains an empty folder in the key?


I have deployed the S3 bucket using CloudFormation, here are some relevant pieces:

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Resources:
  MyBucket:
    Type: 'AWS::S3::Bucket'
    Properties:
      PublicAccessBlockConfiguration:
        BlockPublicAcls: true
        BlockPublicPolicy: true
        IgnorePublicAcls: true
        RestrictPublicBuckets: true
      CorsConfiguration:
        CorsRules:
        - AllowedHeaders: ['*']
          AllowedMethods: [GET,PUT]
          AllowedOrigins: ['*']
          ExposedHeaders: [Date]
          Id: CORSRules
          MaxAge: 3600

  BucketReadRole:
    Type: 'AWS::IAM::Role'
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - apigateway.amazonaws.com
            Action: 'sts:AssumeRole'
      Policies:
        - PolicyName: bucket-read
          PolicyDocument:
            Version: 2012-10-17
            Statement:
            - Effect: Allow
              Action:
                - 's3:GetObject'
              Resource: !Join
                - '/'
                - - !GetAtt MyBucket.Arn
                  - '*'

I have configured the API Gateway with the following integration (note the %2F to account for the empty sub-folder name):

paths:
  /files/{filename}:
    get:
      parameters:
      - in: path
        name: filename
        required: true
        schema:
          type: string
      x-amazon-apigateway-integration:
        credentials:
          Fn::Sub: ${BucketReadRole.Arn}
        uri:
          Fn::Sub:
            - arn:${AWS::Partition}:apigateway:${AWS::Region}:s3:path/${MyBucket}/path/%2F{filename}
            - MyBucket:
                Fn::ImportValue: MyBucket
        responses:
          default:
            statusCode: "200"
            responseParameters:
              method.response.header.Content-Length: "integration.response.header.Content-Length"
              method.response.header.Content-Type: "integration.response.header.Content-Type"
              method.response.header.ETag: "integration.response.header.ETag"
        requestParameters:
          integration.request.path.filename: "method.request.path.filename"
        passthroughBehavior: "when_no_match"
        httpMethod: "GET"
        type: "aws"

Solution

  • I came across this issue due to a + in the filename (S3 object key). When trying to download the file via the ApiGateway S3 integration it was URL encoded to %2B and I saw the SignatureDoesNotMatch error. To fix I removed the + from the S3 key.

    Your x-amazon-apigateway-integration.uri configuration has a %2F encoded slash that looks out of place:

    ${AWS::Partition}:apigateway:${AWS::Region}:s3:path/%2F{filename}

    I suspect removing this slash will also fix your problem.