PersianIronwood
PersianIronwood

Reputation: 780

aws elasticsearch getting signature error on post request

Got a 403 signature error , when using the below fetch function:

    function elasticsearchFetch(AWS, elasticsearchDomain, endpointPath, options = {}, region = process.env.AWS_REGION) {
  return new Promise((resolve, reject) => {
    const { body, method = 'GET' } = options;
    const endpoint = new AWS.Endpoint(elasticsearchDomain);
    const request = new AWS.HttpRequest(endpoint, region);
    request.method = method;
    request.path += endpointPath;
    request.headers.host = elasticsearchDomain;
    if (body) {
      request.body = body;
      request.headers['Content-Type'] = 'application/json';
      request.headers['Content-Length'] = request.body.length;
    }
    const credentials = new AWS.EnvironmentCredentials('AWS');
    const signer = new AWS.Signers.V4(request, 'es');
    signer.addAuthorization(credentials, new Date());
    const client = new AWS.HttpClient();
    client.handleRequest(request, null, (res) => {
      let chunks = '';
      res.on('data', (chunk) => {
        chunks += chunk;
      });
      res.on('end', () => {
        if (res.statusCode !== 201) console.log('Got these options STATUSCODE', JSON.stringify(options, false, 2));
        return resolve({ statusCode: res.statusCode, body: chunks });
      });
    }, (error) => {
      console.log('Got these options ERROR', JSON.stringify(options, false, 2));
      return reject(error);
    });
  });
}

This is the options used for the request in above function :

{
    "method": "POST",
    "body": "{\"prefix\":\"image_233/ArtService/articles-0/GB/ART-60297885/\",\"id\":\"ART-60297885\",\"retailUnit\":\"GB\",\"commercial\":{\"name\":{\"en-GB\":\"FÖRBÄTTRA\"}},\"schemaType\":\"product\",\"productType\":\"ART\"}"
}

and got this error :

{
    "statusCode": 403,
    "body": "{\"message\":\"The request signature we calculated does not match the signature you provided. Check your AWS Secret Access Key and signing method. Consult the service documentation for details.\"}"
}

This is the endpoint : 233/_doc/

Upvotes: 2

Views: 1276

Answers (2)

Ashish Modi
Ashish Modi

Reputation: 7770

By the way, why not use elasticsearch client for nodejs to communicate with elasticsearch rather than writing your own logic. You can consider using http-aws-es which does the request signing part for you. The code will look like

const { Client } = require("elasticsearch");
const esConnectionClass = require("http-aws-es");

const elasticsearchConfig = {
  host: "somePath",
  connectionClass: esConnectionClass
};

const nativeClient = new Client(elasticsearchConfig);

const result = await nativeClient.search({});

Upvotes: 0

Mike Patrick
Mike Patrick

Reputation: 11007

I believe your Content-Length header is incorrect, causing the signature mismatch.

  • Your payload includes the string FÖRBÄTTRA, which has two double-byte characters.
  • You're setting the Content-Length to request.body.length, which comes to 186.
  • While this is the number of characters in the body, it is not the number of bytes in the body (188).

To calculate the Content-Length, use Buffer.byteLength(request.body). For a POST request like this, you can even remove that line of code altogether, and the request will succeed.

  // Content-Length is only needed for DELETE requests that include a request
  // body, but including it for all requests doesn't seem to hurt anything.
  request.headers['Content-Length'] = Buffer.byteLength(request.body);

Source: https://docs.aws.amazon.com/elasticsearch-service/latest/developerguide/es-request-signing.html#es-request-signing-node

Upvotes: 5

Related Questions