Open trangvu9 opened 1 year ago
I am also facing this issue
I was able to hack one up in typescript for non-temporary access keys (no tokens) using only v3 APIs. Needs additional work, but might start as a base.
/* eslint-disable max-classes-per-file */
import { Sha256 } from '@aws-crypto/sha256-js';
import { createScope, SignatureV4 } from '@smithy/signature-v4';
import { auth, Client as Cassandra } from 'cassandra-driver';
import { createHash } from 'crypto';
import fs from 'fs';
import _ from 'lodash';
class SigV4Authenticator implements auth.Authenticator {
private readonly region: string;
private readonly accessKeyId: string;
private readonly secretAccessKey: string;
public static readonly AWS4_SIGNING_ALGORITHM = 'AWS4-HMAC-SHA256';
constructor(props: { region: string; accessKeyId: string; secretAccessKey: string }) {
this.region = props.region;
this.accessKeyId = props.accessKeyId;
this.secretAccessKey = props.secretAccessKey;
}
/* Calling class expects to be a method, so cannot make static even though it is a static function */
/* eslint-disable-next-line class-methods-use-this */
initialResponse = (callback: (error: Error | null, buffer: Buffer | null) => void): void => {
callback(null, Buffer.from('SigV4\0\0', 'utf8'));
};
private static handleError =
(callback: (error: Error | null, buffer: Buffer | null) => void) =>
(err: unknown): void => {
callback(_.isError(err) ? err : new Error(JSON.stringify(err)), null);
};
private handleSignature =
(timestamp: string, callback: (error: Error | null, buffer: Buffer | null) => void) =>
(signature: string): void => {
const payload = `signature=${signature},access_key=${this.accessKeyId},amzdate=${timestamp}`;
callback(null, Buffer.from(payload, 'utf-8'));
};
evaluateChallenge = (challenge: Buffer, callback: (error: Error | null, buffer: Buffer | null) => void) => {
const res = challenge.toString().split('nonce=');
if (res.length < 2) {
callback(new Error(`could not parse nonce: ${challenge.toString()}`), null);
return;
}
const nonce = res[1].split(',')[0];
const timestamp = new Date().toISOString();
/* eslint-disable-next-line no-useless-escape */
const timestampDate = timestamp.replace(/[:\-]|\.\d{3}/g, '').slice(0, 8);
const nonceHash = Buffer.from(createHash('sha256').update(nonce, 'utf-8').digest()).toString('hex');
const scope = createScope(timestampDate, this.region, 'cassandra');
const headers = [
`X-Amz-Algorithm=${SigV4Authenticator.AWS4_SIGNING_ALGORITHM}`,
`X-Amz-Credential=${this.accessKeyId}%2F${encodeURIComponent(scope)}`,
`X-Amz-Date=${encodeURIComponent(timestamp)}`,
'X-Amz-Expires=900',
].sort();
const canonicalRequest = `PUT\n/authenticate\n${headers.join('&')}\nhost:cassandra\n\nhost\n${nonceHash}`;
const digest = Buffer.from(createHash('sha256').update(canonicalRequest, 'utf-8').digest()).toString('hex');
const stringToSign = `${SigV4Authenticator.AWS4_SIGNING_ALGORITHM}\n${timestamp}\n${scope}\n${digest}`;
const signer = new SignatureV4({
service: 'cassandra',
region: this.region,
credentials: {
accessKeyId: this.accessKeyId,
secretAccessKey: this.secretAccessKey,
},
sha256: Sha256,
});
signer
.sign(stringToSign)
.then(this.handleSignature(timestamp, callback))
.catch(SigV4Authenticator.handleError(callback));
};
/* Calling class expects to be a method, so cannot make static even though it is a static function */
/* eslint-disable-next-line class-methods-use-this */
onAuthenticationSuccess = (): void => {};
}
class SigV4AuthProvider implements auth.AuthProvider {
private readonly region: string;
private readonly accessKeyId: string;
private readonly secretAccessKey: string;
constructor(props: { region?: string; accessKeyId: string; secretAccessKey: string }) {
Object.setPrototypeOf(this, auth.PlainTextAuthProvider.prototype);
const region = props.region || process.env.AWS_REGION;
if (_.isNil(region)) throw new Error('no AWS region specified');
this.region = region;
this.accessKeyId = props.accessKeyId;
this.secretAccessKey = props.secretAccessKey;
}
newAuthenticator = (): auth.Authenticator =>
new SigV4Authenticator({
region: this.region,
accessKeyId: this.accessKeyId,
secretAccessKey: this.secretAccessKey,
});
}
const client = new Cassandra({
contactPoints: ['cassandra.us-east-1.amazonaws.com'],
localDataCenter: 'us-east-1',
authProvider: new SigV4AuthProvider({
region: 'us-east-1',
accessKeyId: '<REDACTED>',
secretAccessKey: '<REDACTED>',
}),
sslOptions: {
ca: [fs.readFileSync('/home/<REDACTED>/.cassandra/sf-class2-root.crt', 'utf-8')],
host: 'cassandra.us-east-1.amazonaws.com',
rejectUnauthorized: true,
},
protocolOptions: { port: 9142 },
});
const query = 'SELECT * FROM system_schema.keyspaces';
client.execute(query).then((result) => console.log('Row from Keyspaces:', result));
2024 and this issue is still open.
is there any news about that?
Any roadmap to migrate to AWS SDK V3?