2

I can generate the presigned url following the steps as described in this section, so I wanted to test uploading a specific image marble.jpg and I tried to use postman to test the upload. So, I copied the presigned url and hit the endpoint with a PUT request, and I got this error:

<?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>
    <Key>records/marble_cave.jpg</Key>
    <BucketName>bucket</BucketName>
    <Resource>/bucket/records/marble.jpg</Resource>
    <RequestId>17E3999B521ABB65</RequestId>
    <HostId>50abb07a-2ad0-4948-96e0-23403f661cba</HostId>
</Error>

The following resources are setup:

  • I'm using the min.io server to test this locally.
  • I'm using aws-sdk version 3 of the nodejs sdk for aws
  • I've triple checked my credentials, simple minio creds with no special characters also, I'm definitely making a PUT request.

So, The question is:

  • How to set the signatureVersion using the new javascript aws sdk version 3. ( The getSignedUrl is used to generate presigned url in v3 of the sdk, import { getSignedUrl } from '@aws-sdk/s3-request-presigner';)

  • what causes might be there such that this error is occuring?

The code I use for presigned url generation is:

import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
import { PutObjectCommand, S3Client } from '@aws-sdk/client-s3';
const s3Client = new S3Client({
        region: 'us-east-1',
        credentials: {
        accessKeyId: 'minioadmin',
        secretAccessKey: 'minioadmin',
    },
        endpoint: http://172.21.0.2:9000,
        forcePathStyle: true,
    });
  
const bucketParams = {
        Bucket: 'myBucket',
        Key: `marbles.jpg`,
};  
  
const command = new PutObjectCommand(bucketParams);

const signedUrl = await getSignedUrl(s3Client, command, {
        expiresIn: 10000,
})  
juztcode
  • 1,196
  • 2
  • 21
  • 46

2 Answers2

2

I stumbled on this issue myself a year ago, the new V3 SDK has a bug, it doesn't take the port into consideration when signing a URL. see here https://github.com/aws/aws-sdk-js-v3/issues/2726

the work around I ended up implemented overrides getSignedUrl in my code and add the missing port as follows:

import {BuildMiddleware, MetadataBearer, RequestPresigningArguments} from '@aws-sdk/types';
import {Client, Command} from '@aws-sdk/smithy-client';
import {HttpRequest} from '@aws-sdk/protocol-http';
import {formatUrl} from '@aws-sdk/util-format-url';
import {S3RequestPresigner} from '@aws-sdk/s3-request-presigner';


export const getSignedUrl = async <
  InputTypesUnion extends object,
  InputType extends InputTypesUnion,
  OutputType extends MetadataBearer = MetadataBearer
  >(
  client: Client<any, InputTypesUnion, MetadataBearer, any>,
  command: Command<InputType, OutputType, any, InputTypesUnion, MetadataBearer>,
  options: RequestPresigningArguments = {}
): Promise<string> => {
  const s3Presigner = new S3RequestPresigner({ ...client.config });
  const presignInterceptMiddleware: BuildMiddleware<InputTypesUnion, MetadataBearer> =
    (next, context) => async (args) => {
      const { request } = args;
      if (!HttpRequest.isInstance(request)) {
        throw new Error('Request to be presigned is not an valid HTTP request.');
      }
      // Retry information headers are not meaningful in presigned URLs
      delete request.headers['amz-sdk-invocation-id'];
      delete request.headers['amz-sdk-request'];
      // User agent header would leak sensitive information
      delete request.headers['x-amz-user-agent'];
      delete request.headers['x-amz-content-sha256'];

      delete request.query['x-id'];

      if (request.port) {
        request.headers['host'] = `${request.hostname}:${request.port}`;
      }

      const presigned = await s3Presigner.presign(request, {
        ...options,
        signingRegion: options.signingRegion ?? context['signing_region'],
        signingService: options.signingService ?? context['signing_service'],
      });
      return {
        // Intercept the middleware stack by returning fake response
        response: {},
        output: {
          $metadata: { httpStatusCode: 200 },
          presigned,
        },
      } as any;
    };
  const middlewareName = 'presignInterceptMiddleware';
  client.middlewareStack.addRelativeTo(presignInterceptMiddleware, {
    name: middlewareName,
    relation: 'before',
    toMiddleware: 'awsAuthMiddleware',
    override: true,
  });

  let presigned: HttpRequest;
  try {
    const output = await client.send(command);
    //@ts-ignore the output is faked, so it's not actually OutputType
    presigned = output.presigned;
  } finally {
    client.middlewareStack.remove(middlewareName);
  }

  return formatUrl(presigned);
};
Shay
  • 1,245
  • 7
  • 14
1

The solution is probably the same as in my other question, so simply copying the answer:

I was trying and changing ports, and the put command seems to work when I use only local host for url generation

so, in this above:

new S3Client({
        region: 'us-east-1',
        credentials: {
        accessKeyId: 'minioadmin',
        secretAccessKey: 'minioadmin',
    },
        endpoint: http://172.21.0.2:9000,
        forcePathStyle: true,
    });  

I use:

new S3Client({
        region: 'us-east-1',
        credentials: {
        accessKeyId: 'minioadmin',
        secretAccessKey: 'minioadmin',
    },
        endpoint: http://172.21.0.2, // or 127.0.0.1
        forcePathStyle: true,
    });  

Note, I haven't used any port number, so the default is 80

If you're using docker-compose add this config:

. 
.
.
ports:  
  - 80:9000  

and it works fine.

juztcode
  • 1,196
  • 2
  • 21
  • 46
  • Sorry why do you need to even specify an endpoint, and why is it localhost? Pre-signed url does not need an endpoint, as far as I know. – Register Sole Apr 12 '22 at 03:23
  • um... if you're talking about hitting S3 itself, I think we are able to omit it. But, if you're hitting something else, as far as I know, an endpoint is to be specified – juztcode Apr 12 '22 at 03:40
  • 1
    Oh didn't realize you are running a local server! Thanks. – Register Sole Apr 12 '22 at 03:57