Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AWS SignatureV4 got broken after 0.9.0 version - even example in documentation produces an error #102

Closed
huima opened this issue May 3, 2024 · 2 comments
Assignees
Labels
bug Something isn't working documentation Improvements or additions to documentation

Comments

@huima
Copy link

huima commented May 3, 2024

Hi,

I am trying to take k6 into use and call Api Gateway apis that need calls to be signed. I immediately stumbled upon problems and it seems that what is documented and described in examples is no longer valid.

If I tried to use the latest version 0.11.0 as described in the documentation: https://grafana.com/docs/k6/latest/javascript-api/jslib/aws/signaturev4/

I got an error:

ERRO[0001] TypeError: Cannot read property 'hostname' of undefined running at value (webpack://k6-jslib-aws/./src/internal/signature.ts:102:66(87))

Minimal example code:

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

const AWS_ACCESS_KEY_ID = __ENV.AWS_ACCESS_KEY_ID;
const AWS_SECRET_ACCESS_KEY = __ENV.AWS_SECRET_ACCESS_KEY;
const AWS_SESSION_TOKEN = __ENV.AWS_SESSION_TOKEN;
const AWS_REGION = "us-east-1"

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

export const options = {
vus: 1,
duration: '5s',
};

export default function() {

const signer = new SignatureV4({
region: awsConfig.region,
service: 's3',
credentials: {
accessKeyId: awsConfig.accessKeyId,
secretAccessKey: awsConfig.secretAccessKey,
sessionToken: awsConfig.sessionToken,
},
uriEscapePath: true,
applyChecksum: true,
})

const req = { method: 'GET',
protocol: 'https',
hostname: 'mybucket.s3.us-east-1.amazonaws.com',
path: '/myfile.txt',
headers: {}}

const signedRequest = signer.sign(req)
const res = http.get(signedRequest.url, { headers: signedRequest.headers })

console.log(res.body)
}
`

So the sign function itself seems to fail.

Same script with the version 0.9.0 does not produce that error, so clearly something has changed and existing documentation / examples do not work anymore out of the box.

Tested on Windows on WSL - k6 v0.50.0 (commit/f18209a5e3, go1.21.8, linux/amd64)

And same version also on OS X from homebrew - go1.22.1, darwin/amd64.

@oleiade
Copy link
Member

oleiade commented May 13, 2024

Hi @huima 👋🏻

Thanks a lot for bringing this to our attention. We have indeed recently changed the way the SignatureV4 handles the endpoint, and the documentation appears to be outdated.

While we're updating the documentation, you should be able to produce a valid signature using the following argument structure instead:

import http from 'k6/http'

import { AWSConfig, Endpoint, SignatureV4 } from '../build/signature.js'

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

export default function () {
    /**
     * In order to be able to sign an HTTP request's,
     * we need to instantiate a SignatureV4 object.
     */
    const signer = new SignatureV4({
        service: 's3',
        region: awsConfig.region,
        credentials: {
            accessKeyId: awsConfig.accessKeyId,
            secretAccessKey: awsConfig.secretAccessKey,
            sessionToken: awsConfig.sessionToken,
        },
    })

    /**
     * The sign operation will return a new HTTP request with the
     * AWS signature v4 protocol headers added. It returns an Object
     * implementing the SignedHTTPRequest interface, holding a `url` and a `headers`
     * properties, ready to use in the context of k6's http call.
     */
    const signedRequest = signer.sign(
        /**
         * HTTP request description
         */
        {
            /**
             * The HTTP method we will use in the request.
             */
            method: 'GET',

            /**
             * The endpoint of the service we will be making the request to.
             *
             * The endpoint is instantiated from a URL string, of the format: `{scheme}://{hostname}[:{port}]`
             */
            endpoint: new Endpoint('https://test-jslib-aws.s3.us-east-1.amazonaws.com/'),

            /**
             * The path of the request.
             */
            path: '/bonjour.txt',

            /**
             * The headers we will be sending in the request.
             */
            headers: {},

            /**
             * Whether the URI should be escaped or not.
             */
            uriEscapePath: false,

            /**
             * Whether or not the body's hash should be calculated and included
             * in the request.
             */
            applyChecksum: false,
        },
        /**
         * (optional) Signature operation options.
         */
        {
            /**
             * The date and time to be used as signature metadata. This value should be
             * a Date object, a unix (epoch) timestamp, or a string that can be
             * understood by the JavaScript `Date` constructor.If not supplied, the
             * value returned by `new Date()` will be used.
             */
            signingDate: new Date(),

            /**
             * The service signing name. It will override the service name of the signer
             * in current invocation
             */
            signingService: 's3',

            /**
             * The region name to sign the request. It will override the signing region of the
             * signer in current invocation
             */
            signingRegion: 'us-east-1',
        }
    )

    const response = http.get(signedRequest.url, { headers: signedRequest.headers })
}

Note that we import the Endpoint type, and use it as part of the first sign argument to specify the endpoint to use. Let me know if you need any further support 🙇🏻

@oleiade oleiade added bug Something isn't working documentation Improvements or additions to documentation and removed triage labels May 13, 2024
@oleiade
Copy link
Member

oleiade commented May 13, 2024

This will be addressed by grafana/k6-docs#1585

@oleiade oleiade closed this as completed Jul 31, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working documentation Improvements or additions to documentation
Projects
None yet
Development

No branches or pull requests

3 participants