Last active
June 26, 2024 16:09
-
-
Save emeraldsanto/76c371e2a10d663baf92f82f9590bb1e to your computer and use it in GitHub Desktop.
Presign an AWS STS GetCallerIdentity request for later use, adapted to JavaScript from https://donchev.is/post/aws-lambda-invoker-identification
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { defaultProvider } from "@aws-sdk/credential-provider-node"; | |
import { HttpRequest } from "@aws-sdk/protocol-http"; | |
import { Sha256 } from '@aws-crypto/sha256-js'; | |
import { SignatureV4 } from "@aws-sdk/signature-v4"; | |
import { stringify } from "qs"; | |
async function main(): Promise<string> { | |
const signer = new SignatureV4({ | |
credentials: defaultProvider(), | |
region: process.env.AWS_REGION, | |
service: "sts", | |
sha256: Sha256, | |
}); | |
const host = `sts.${process.env.AWS_REGION}.amazonaws.com`; | |
const req = new HttpRequest({ | |
headers: { | |
'Content-Type': 'application/json', | |
host | |
}, | |
hostname: host, | |
method: "GET", | |
path: '/', | |
query: { | |
Action: 'GetCallerIdentity', | |
Version: '2011-06-15', | |
}, | |
}); | |
const signed = await signer.presign(req, { expiresIn: 60 * 10 }); | |
return `https://${signed.hostname}${signed.path}?${stringify(signed.query)}`; | |
} | |
main() | |
.then((output) => console.log(output)) | |
.catch((e) => console.log(e)); |
In case this helps someone else -- I needed this to create an EKS (kubernetes) token to authenticate calls to the kubernetes API. Using the gist as a starting point, I created this. One notable difference is the headers property in the HttpRequest constructor. This change was required to create a valid kubernetes token.
/**
* Creates a bearer token that can be used to authenticate with the kubernetes cluster.
* It's obtained by signing a request to the STS service with the k8s cluster name as the x-k8s-aws-id header.
* @param {string} k8sClusterName The name of the kubernetes cluster in eks
* @returns A bearer token that can be used to authenticate with the kubernetes cluster
*/
async function getKubernetesToken(k8sClusterName) {
const region = process.env.AWS_REGION || "us-east-1"
const signer = new SignatureV4({
credentials: defaultProvider(),
region,
service: "sts",
sha256: Sha256,
});
const host = `sts.amazonaws.com`;
const req = new HttpRequest({
headers: {
host,
'x-k8s-aws-id': k8sClusterName,
},
hostname: host,
method: "GET",
path: '/',
query: {
Action: 'GetCallerIdentity',
Version: '2011-06-15',
},
});
const signed = await signer.presign(req, { expiresIn: 60 * 10 });
const url = `https://${signed.hostname}${signed.path}?${stringify(signed.query)}`;
const token = `k8s-aws-v1.${Buffer.from(url).toString('base64url')}`
return token
}
Thanks for this, it saved me some time! I made a few tweaks for my use case:
- use
fromNodeProviderChain
for credentials to support all the same mechanisms as the AWS CLI - switched to non-deprecated
@smithy/...
packages - switched to the native
querystring
library built into node
presign-sts-get-caller-identity.mjs
import { Sha256 } from '@aws-crypto/sha256-js';
import { fromNodeProviderChain } from '@aws-sdk/credential-providers';
import { HttpRequest } from '@smithy/protocol-http';
import { SignatureV4 } from '@smithy/signature-v4';
import { stringify } from 'querystring';
/**
* Return a presigned URL that can be used to invoke STS GetCallerIdentity
* with the credentials that were used to sign the URL.
*
* @param {string} region - AWS region (default: us-east-1)
* @returns {Promise<string>}
*/
async function presignGetCallerIdentity(region = 'us-east-1') {
const signer = new SignatureV4({
credentials: fromNodeProviderChain(),
region,
service: 'sts',
sha256: Sha256,
});
const host = `sts.${region}.amazonaws.com`;
const req = new HttpRequest({
headers: {
'Content-Type': 'application/json',
host,
},
hostname: host,
method: 'GET',
path: '/',
query: {
Action: 'GetCallerIdentity',
Version: '2011-06-15',
},
});
const { hostname, path, query } = await signer.presign(req, { expiresIn: 60 * 10 });
return `https://${hostname}${path}?${stringify(query)}`;
}
console.log(await presignGetCallerIdentity(process.env.AWS_REGION));
Usage example with curl:
curl -H 'Content-Type: application/json' -H 'host: sts.us-east-1.amazonaws.com' $(node presign-sts-get-caller-identity.mjs)
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Thank you. this should be included in the javascript v3 sts api.