grafana / k6-jslib-aws

Javascript Library allowing to interact with AWS resources from k6 scripts
Apache License 2.0
18 stars 28 forks source link

AWS Authentication - Missing Authentication Token #109

Closed jvelasquez closed 2 weeks ago

jvelasquez commented 2 weeks ago

Hi all,

I am getting an error while trying this with k6 cloud in grafana, and also running k6 locally.

This is my code

import { sleep } from 'k6'
import http from 'k6/http'
import {
  AWSConfig,
  Endpoint,
  SignatureV4,
} from 'https://jslib.k6.io/aws/0.12.3/signature.js';

const AWS_REGION = 'us-east-1'
const AWS_ACCESS_KEY_ID = '***'
const AWS_SECRET_ACCESS_KEY = '***'

const awsConfig = new AWSConfig({
  region: AWS_REGION,
  accessKeyId: AWS_ACCESS_KEY_ID,
  secretAccessKey: AWS_SECRET_ACCESS_KEY,
})

const RPC_BASE_URL = `https://mainnet.bitcoin.managedblockchain.${AWS_REGION}.amazonaws.com`

export const options = {
  thresholds: {
    http_req_failed: ['rate<0.02'], // http errors should be less than 2%
    http_req_duration: ['p(95)<2000'], // 95% requests should be below 2s
  },
  scenarios: {
    getFeeTransaction: {
      executor: 'per-vu-iterations',
      vus: 1,
      iterations: 1,
      maxDuration: '1s',
      exec: 'getFeeTransaction',
    },
  },
}

export function signedRequest(method, service, endpoint, path, body){
  const signer = new SignatureV4({
    service: 'managedblockchain',
    region: awsConfig.region,
    credentials: {
        accessKeyId: awsConfig.accessKeyId,
        secretAccessKey: awsConfig.secretAccessKey,
        sessionToken: awsConfig.sessionToken,
        endpoint: new Endpoint(endpoint),
    },
    uriEscapePath: true,
    applyChecksum: true,
  })

  const signedRequest = signer.sign(
    {
        method: method,
        endpoint: new Endpoint(endpoint),
        path: path,
        body: body,
        headers: {
          'Content-Type': 'application/json',
          'Accept-Encoding': 'gzip, deflate, br',
          'Accept': '*/*',
        },
    },
    {
      signingDate: new Date(),
      signingService: service,
      signingRegion: AWS_REGION,
      signableHeaders: new Set(['Content-Type', 'Content-Length', 'Host', 'X-Amz-Content-Sha256', 'X-Amz-Date']),
    },)

  console.log(`[info] Request - ${JSON.stringify(signedRequest)}`)
  return signedRequest
}

export function getFeeTransaction() {
  const body = JSON.stringify(
    {
    "method": "estimatesmartfee",
    "params": [1008, "UNSET"],
    }
  )
  const req = signedRequest('POST', 'managedblockchain', RPC_BASE_URL, '', body)
  let response = http.post(req.url,{headers: req.headers,})
  console.log(`[info] Response - ${JSON.stringify(response)}`)
  sleep(1)
}

Request

{
  "url": "https://mainnet.bitcoin.managedblockchain.us-east-1.amazonaws.com",
  "method": "POST",
  "endpoint": {
    "_protocol": "https",
    "_hostname": "mainnet.bitcoin.managedblockchain.us-east-1.amazonaws.com"
  },
  "path": "",
  "body": "{\"method\":\"estimatesmartfee\",\"params\":[1008,\"UNSET\"]}",
  "headers": {
    "Content-Type": "application/json",
    "Accept-Encoding": "gzip, deflate, br",
    "Accept": "*/*",
    "host": "mainnet.bitcoin.managedblockchain.us-east-1.amazonaws.com",
    "x-amz-date": "20240628T121450Z",
    "x-amz-content-sha256": "d2d6fb657bd673d493641d1cfbe3649bfcd91f2ab46b2824bcb03ca22ce583a8",
    "authorization": "AWS4-HMAC-SHA256 Credential=***/20240628/us-east-1/managedblockchain/aws4_request, SignedHeaders=accept;accept-encoding;content-type;host;x-amz-content-sha256;x-amz-date, Signature=dc3cd50bf50059c625cc2c59026b105a97d40c7fce4e5391bb7e4ef779014f56"
  }
}

Response

{
  "remote_ip": "18.213.106.170",
  "remote_port": 443,
  "url": "https://mainnet.bitcoin.managedblockchain.us-east-1.amazonaws.com",
  "status": 403,
  "status_text": "403 Forbidden",
  "proto": "HTTP/2.0",
  "headers": {
    "Date": "Fri, 28 Jun 2024 12:14:50 GMT",
    "Content-Type": "application/json",
    "Content-Length": "42",
    "X-Amzn-Requestid": "cb00785e-6823-434a-a8b8-22e3bbd61693",
    "X-Amzn-Errortype": "MissingAuthenticationTokenException",
    "X-Amz-Apigw-Id": "aE9hMGgvoAMERtA="
  },
  "cookies": {},
  "body": "{\"message\":\"Missing Authentication Token\"}",
  "timings": {
    "duration": 173.828,
    "blocked": 230.681,
    "looking_up": 0,
    "connecting": 7.111,
    "tls_handshaking": 173.597,
    "sending": 0.228,
    "waiting": 173.439,
    "receiving": 0.161
  },
  "tls_version": "tls1.3",
  "tls_cipher_suite": "TLS_AES_128_GCM_SHA256",
  "ocsp": {
    "produced_at": 0,
    "this_update": 0,
    "next_update": 0,
    "revoked_at": 0,
    "revocation_reason": "",
    "status": "unknown"
  },
  "error": "",
  "error_code": 1403,
  "request": {
    "method": "POST",
    "url": "https://mainnet.bitcoin.managedblockchain.us-east-1.amazonaws.com",
    "headers": {
      "Content-Type": [
        "application/x-www-form-urlencoded"
      ],
      "User-Agent": [
        "k6/0.52.0 (https://k6.io/)"
      ]
    },
    "body": "headers=map%5BAccept%3A%2A%2F%2A+Accept-Encoding%3Agzip%2C+deflate%2C+br+Content-Type%3Aapplication%2Fjson+authorization%3AAWS4-HMAC-SHA256+Credential%3D***%2F20240628%2Fus-east-1%2Fmanagedblockchain%2Faws4_request%2C+SignedHeaders%3Daccept%3Baccept-encoding%3Bcontent-type%3Bhost%3Bx-amz-content-sha256%3Bx-amz-date%2C+Signature%3Ddc3cd50bf50059c625cc2c59026b105a97d40c7fce4e5391bb7e4ef779014f56+host%3Amainnet.bitcoin.managedblockchain.us-east-1.amazonaws.com+x-amz-content-sha256%3Ad2d6fb657bd673d493641d1cfbe3649bfcd91f2ab46b2824bcb03ca22ce583a8+x-amz-date%3A20240628T121450Z%5D",
    "cookies": {}
  }
}

I hope you can help me out with this issue, thanks!

mstoykov commented 2 weeks ago

Hi @jvelasquez, I am not certian how you got those request and reponse objects in the latter part of the issue?

But http.post takes params (and the headers) as the third argumnet not the second. The second one is body.

So I will first try to do

  let response = http.post(req.url, null, {headers: req.headers,})
jvelasquez commented 2 weeks ago

Hi @mstoykov , thanks for replying. Oh, just seen this, thanks I have amended the call now it is changed to:

export function getFeeTransaction() {
  const body = JSON.stringify(
    {
    "method": "estimatesmartfee",
    "params": [1008, "UNSET"],
    }
  )
  const req = signedRequest('POST', 'managedblockchain', RPC_BASE_URL, '', body)
  let response = http.post(req.url,body,{headers: req.headers,})
  console.log(`[info] Response - ${JSON.stringify(response)}`)
  sleep(1)
}

I have been banging my head around this for a couple of hours :(. Strange is, now I am getting a different error:

{
   "remote_ip":"52.45.191.149",
   "remote_port":443,
   "url":"https://mainnet.bitcoin.managedblockchain.us-east-1.amazonaws.com",
   "status":403,
   "status_text":"403 Forbidden",
   "proto":"HTTP/2.0",
   "headers":{
      "X-Amzn-Requestid":"e5e4a083-3e5a-4756-923d-75c103846598",
      "X-Amzn-Errortype":"InvalidSignatureException",
      "X-Amz-Apigw-Id":"aFGbVEHtoAMEC4g=",
      "Date":"Fri, 28 Jun 2024 13:15:39 GMT",
      "Content-Type":"application/json",
      "Content-Length":"859"
   },
   "cookies":{

   },
   "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.\\n\\nThe Canonical String for this request should have been\\n'POST\\n/\\n\\naccept:*/*\\naccept-encoding:gzip, deflate, br\\ncontent-type:application/json\\nhost:mainnet.bitcoin.managedblockchain.us-east-1.amazonaws.com\\nx-amz-content-sha256:d2d6fb657bd673d493641d1cfbe3649bfcd91f2ab46b2824bcb03ca22ce583a8\\nx-amz-date:20240628T131539Z\\n\\naccept;accept-encoding;content-type;host;x-amz-content-sha256;x-amz-date\\nd2d6fb657bd673d493641d1cfbe3649bfcd91f2ab46b2824bcb03ca22ce583a8'\\n\\nThe String-to-Sign should have been\\n'AWS4-HMAC-SHA256\\n20240628T131539Z\\n20240628/us-east-1/managedblockchain/aws4_request\\n5b414ebc967188e6ca40f87d4545d48889a53dd2902a5367e145ae37b323f587'\\n\"}",
   "timings":{
      "duration":174.759,
      "blocked":208.476,
      "looking_up":0,
      "connecting":6.504,
      "tls_handshaking":169.309,
      "sending":0.364,
      "waiting":174.247,
      "receiving":0.148
   },
   "tls_version":"tls1.3",
   "tls_cipher_suite":"TLS_AES_128_GCM_SHA256",
   "ocsp":{
      "produced_at":0,
      "this_update":0,
      "next_update":0,
      "revoked_at":0,
      "revocation_reason":"",
      "status":"unknown"
   },
   "error":"",
   "error_code":1403,
   "request":{
      "method":"POST",
      "url":"https://mainnet.bitcoin.managedblockchain.us-east-1.amazonaws.com",
      "headers":{
         "X-Amz-Date":[
            "20240628T131539Z"
         ],
         "X-Amz-Content-Sha256":[
            "d2d6fb657bd673d493641d1cfbe3649bfcd91f2ab46b2824bcb03ca22ce583a8"
         ],
         "Authorization":[
            "AWS4-HMAC-SHA256 Credential=***/20240628/us-east-1/managedblockchain/aws4_request, SignedHeaders=accept;accept-encoding;content-type;host;x-amz-content-sha256;x-amz-date, Signature=f7b1aa8e94d6fdd2fd34339de102c5ebfd931cd0688164d05f7a3b4b031f17e4"
         ],
         "User-Agent":[
            "k6/0.52.0 (https://k6.io/)"
         ],
         "Content-Type":[
            "application/json"
         ],
         "Accept-Encoding":[
            "gzip, deflate, br"
         ],
         "Accept":[
            "*/*"
         ],
         "Host":[
            "mainnet.bitcoin.managedblockchain.us-east-1.amazonaws.com"
         ]
      },
      "body":"{\"method\":\"estimatesmartfee\",\"params\":[1008,\"UNSET\"]}",
      "cookies":{

      }
   }
}

🤔 The Credentials are fine as I am testing with POSTMAN in parallel. Anything else you could spot?

Thanks a lot

mstoykov commented 2 weeks ago

Given the error I will expect the signable headers field should be dropped?

jvelasquez commented 2 weeks ago

Thanks again @mstoykov,

I have tried dropping the signableHeaders and unsignableHedaers fields. Still getting the same error The request signature we calculated does not match the signature you provided. [...]

Also tried the signing with only

  const signedRequest = signer.sign(
    {
        method: method,
        endpoint: new Endpoint(endpoint),
        path: path,
        body: body,
        headers: {},
    },
    {
      signingDate: new Date(),
      signingService: service,
      signingRegion: AWS_REGION,
    },)

Finally also tried with empty sets in signableHeaders / unsignableHeaders fields , i.e. new Set([]) but with no avail, same InvalidSignatureException error.

jvelasquez commented 2 weeks ago

Got the problem, I was missing the path '/' in the signature method. I previosuly left is as '' and it caused the error.

export function aws_signedRequest(method, service, endpoint, path, body){
  const signer = new SignatureV4({
    service: 'managedblockchain',
    region: awsConfig.region,
    credentials: {
        accessKeyId: awsConfig.accessKeyId,
        secretAccessKey: awsConfig.secretAccessKey,
    },
    uriEscapePath: true,
    applyChecksum: true,
  })

  const signedRequest = signer.sign(
    {
        method: method,
        endpoint: new Endpoint(endpoint),
        path: path,
        body: body,
        headers: {},
    },
    {
      signingDate: new Date(),
      signingService: service,
      signingRegion: AWS_REGION,
    },
  )

  // console.log(`[info] Request - ${JSON.stringify(signedRequest)}`)

  return signedRequest
}

export function setup() {
  const body = JSON.stringify(
    {
      "method": "estimatesmartfee",
      "params": [
        1008,
        "UNSET",
      ],
    }
  )
  return { signedRequest: aws_signedRequest('POST', 'managedblockchain', AWS_RPC_BASE_URL, '/', body) };
}