Compare commits

...

10 Commits

Author SHA1 Message Date
Anurag Mittal d90bc4f741
updated data message 2024-07-04 00:51:11 +02:00
Anurag Mittal b9dd7139ad
updated events 2024-07-03 14:14:08 +02:00
Anurag Mittal 6543d9f88d
updated events for active span 2024-07-01 14:22:51 +02:00
Anurag Mittal 44efcd625c
updated authv4 2024-07-01 14:18:20 +02:00
Anurag Mittal c6bb489ade
updated signed headers request 2024-07-01 14:06:20 +02:00
Anurag Mittal 76c4c2b2bb
added details about headers 2024-07-01 13:51:44 +02:00
Anurag Mittal d15d6f8a06
updated authv4 spans 2024-07-01 13:37:06 +02:00
Anurag Mittal 8d40bab08f
updated more auth details 2024-07-01 00:58:41 +02:00
Anurag Mittal 24f6d8374e
added more details to authv4 2024-07-01 00:44:00 +02:00
Anurag Mittal bb3b448757
added auth spans 2024-07-01 00:31:12 +02:00
9 changed files with 503 additions and 508 deletions

View File

@ -68,6 +68,12 @@ function extractParams(
} = oTel; } = oTel;
activeSpan?.addEvent('Arsenal:: entered Arsenal.auth.server.extractParams'); activeSpan?.addEvent('Arsenal:: entered Arsenal.auth.server.extractParams');
return tracer.startActiveSpan('Check validity of request parameters to authenticate using Arsenal', undefined, activeTracerContext, extractParamsSpan => { return tracer.startActiveSpan('Check validity of request parameters to authenticate using Arsenal', undefined, activeTracerContext, extractParamsSpan => {
extractParamsSpan.setAttributes({
'code.lineno': 75,
'code.filename': 'lib/auth/auth.ts',
'code.function': 'extractParams',
'code.url': 'https://github.com/scality/arsenal/blob/892dee6c1333fcc25c88333ee991f02830cb3c51/lib/auth/auth.ts',
});
log.trace('entered', { method: 'Arsenal.auth.server.extractParams' }); log.trace('entered', { method: 'Arsenal.auth.server.extractParams' });
const authHeader = request.headers.authorization; const authHeader = request.headers.authorization;
let version: 'v2' |'v4' | null = null; let version: 'v2' |'v4' | null = null;
@ -108,8 +114,7 @@ function extractParams(
activeSpan?.addEvent(`Arsenal:: Identified auth method version: ${version} and method: ${method}`); activeSpan?.addEvent(`Arsenal:: Identified auth method version: ${version} and method: ${method}`);
activeSpan?.addEvent('Arsenal:: Checking if valid request headers and query are used to make request to vault'); activeSpan?.addEvent('Arsenal:: Checking if valid request headers and query are used to make request to vault');
log.trace('identified auth method', { version, authMethod: method }); log.trace('identified auth method', { version, authMethod: method });
extractParamsSpan.end(); return checkFunctions[version][method](request, log, data, awsService, { activeSpan, extractParamsSpan, activeTracerContext, tracer });
return checkFunctions[version][method](request, log, data, awsService, { activeSpan, activeTracerContext, tracer });
} }
// no auth info identified // no auth info identified

View File

@ -6,109 +6,97 @@ import checkRequestExpiry from './checkRequestExpiry';
import algoCheck from './algoCheck'; import algoCheck from './algoCheck';
export function check(request: any, log: Logger, data: { [key: string]: string }, oTel: any) { export function check(request: any, log: Logger, data: { [key: string]: string }, oTel: any) {
const { activeSpan, activeTracerContext, tracer } = oTel; const { activeSpan, extractParamsSpan, activeTracerContext, tracer } = oTel;
activeSpan?.addEvent('Entered V2 header auth check'); activeSpan?.addEvent('Entered V2 header auth check');
return tracer.startActiveSpan('V2 Header Auth Check', undefined, activeTracerContext, authCheckSpan => { log.trace('running header auth check');
log.trace('running header auth check'); activeSpan?.addEvent('Running header auth check');
activeSpan?.addEvent('Running header auth check'); const headers = request.headers;
activeSpan?.addEvent('Extracting security token');
const headers = request.headers; const token = headers['x-amz-security-token'];
if (token && !constants.iamSecurityToken.pattern.test(token)) {
activeSpan?.addEvent('Extracting security token'); log.debug('invalid security token', { token });
const token = headers['x-amz-security-token']; activeSpan.recordException(errors.InvalidToken);
if (token && !constants.iamSecurityToken.pattern.test(token)) { extractParamsSpan.end();
log.debug('invalid security token', { token }); return { err: errors.InvalidToken };
activeSpan.recordException(errors.InvalidToken); }
authCheckSpan.end(); activeSpan?.addEvent('Extracted security token');
return { err: errors.InvalidToken }; activeSpan?.addEvent('Checking timestamp');
} // Check to make sure timestamp is within 15 minutes of current time
activeSpan?.addEvent('Extracted security token'); let timestamp = headers['x-amz-date'] ? headers['x-amz-date'] : headers.date;
timestamp = Date.parse(timestamp);
activeSpan?.addEvent('Checking timestamp'); if (!timestamp) {
// Check to make sure timestamp is within 15 minutes of current time log.debug('missing or invalid date header', { method: 'auth/v2/headerAuthCheck.check' });
let timestamp = headers['x-amz-date'] ? headers['x-amz-date'] : headers.date; activeSpan.recordException(errors.AccessDenied.customizeDescription('Authentication requires a valid Date or x-amz-date header'));
timestamp = Date.parse(timestamp); extractParamsSpan.end();
if (!timestamp) { return { err: errors.AccessDenied.customizeDescription('Authentication requires a valid Date or x-amz-date header') };
log.debug('missing or invalid date header', { method: 'auth/v2/headerAuthCheck.check' }); }
activeSpan.recordException(errors.AccessDenied.customizeDescription('Authentication requires a valid Date or x-amz-date header')); activeSpan?.addEvent('Checked timestamp');
authCheckSpan.end(); activeSpan?.addEvent('Checking request expiry');
return { err: errors.AccessDenied.customizeDescription('Authentication requires a valid Date or x-amz-date header') }; const err = checkRequestExpiry(timestamp, log);
} if (err) {
activeSpan?.addEvent('Checked timestamp'); activeSpan.recordException(err);
extractParamsSpan.end();
activeSpan?.addEvent('Checking request expiry'); return { err };
const err = checkRequestExpiry(timestamp, log); }
if (err) { activeSpan?.addEvent('Checked request expiry');
activeSpan.recordException(err); activeSpan?.addEvent('Extracting authorization header');
authCheckSpan.end(); // Authorization Header should be in the format of 'AWS AccessKey:Signature'
return { err }; const authInfo = headers.authorization;
} activeSpan?.addEvent('Extracted authorization header');
activeSpan?.addEvent('Checked request expiry'); if (!authInfo) {
log.debug('missing authorization security header');
activeSpan?.addEvent('Extracting authorization header'); activeSpan.recordException(errors.MissingSecurityHeader);
// Authorization Header should be in the format of 'AWS AccessKey:Signature' extractParamsSpan.end();
const authInfo = headers.authorization; return { err: errors.MissingSecurityHeader };
activeSpan?.addEvent('Extracted authorization header'); }
const semicolonIndex = authInfo.indexOf(':');
if (!authInfo) { if (semicolonIndex < 0) {
log.debug('missing authorization security header'); log.debug('invalid authorization header', { authInfo });
activeSpan.recordException(errors.MissingSecurityHeader); activeSpan.recordException(errors.InvalidArgument);
authCheckSpan.end(); extractParamsSpan.end();
return { err: errors.MissingSecurityHeader }; return { err: errors.InvalidArgument };
} }
const semicolonIndex = authInfo.indexOf(':'); const accessKey = semicolonIndex > 4 ? authInfo.substring(4, semicolonIndex).trim() : undefined;
if (semicolonIndex < 0) { if (typeof accessKey !== 'string' || accessKey.length === 0) {
log.debug('invalid authorization header', { authInfo }); log.trace('invalid authorization header', { authInfo });
activeSpan.recordException(errors.InvalidArgument); activeSpan.recordException(errors.MissingSecurityHeader);
authCheckSpan.end(); extractParamsSpan.end();
return { err: errors.InvalidArgument }; return { err: errors.MissingSecurityHeader };
} }
const accessKey = semicolonIndex > 4 ? authInfo.substring(4, semicolonIndex).trim() : undefined; // @ts-ignore
if (typeof accessKey !== 'string' || accessKey.length === 0) { log.addDefaultFields({ accessKey });
log.trace('invalid authorization header', { authInfo }); const signatureFromRequest = authInfo.substring(semicolonIndex + 1).trim();
activeSpan.recordException(errors.MissingSecurityHeader); log.trace('signature from request', { signatureFromRequest });
authCheckSpan.end(); activeSpan?.addEvent('Extracting signature from request');
return { err: errors.MissingSecurityHeader }; activeSpan?.addEvent('Constructing string to sign');
} const stringToSign = constructStringToSign(request, data, log);
// @ts-ignore log.trace('constructed string to sign', { stringToSign });
log.addDefaultFields({ accessKey }); activeSpan?.addEvent('Constructed string to sign v2 headers');
const algo = algoCheck(signatureFromRequest.length);
const signatureFromRequest = authInfo.substring(semicolonIndex + 1).trim(); log.trace('algo for calculating signature', { algo });
log.trace('signature from request', { signatureFromRequest }); activeSpan?.addEvent('Checked algorithm for calculating signature');
activeSpan?.addEvent('Extracting signature from request'); if (algo === undefined) {
activeSpan.recordException(errors.InvalidArgument);
const stringToSign = constructStringToSign(request, data, log); extractParamsSpan.end();
log.trace('constructed string to sign', { stringToSign }); return { err: errors.InvalidArgument };
activeSpan?.addEvent('Constructed string to sign'); }
activeSpan?.addEvent('Exiting V2 header auth check');
const algo = algoCheck(signatureFromRequest.length); extractParamsSpan.end();
log.trace('algo for calculating signature', { algo }); return {
activeSpan?.addEvent('Checked algorithm for calculating signature'); err: null,
params: {
if (algo === undefined) { version: 2,
activeSpan.recordException(errors.InvalidArgument); data: {
authCheckSpan.end(); accessKey,
return { err: errors.InvalidArgument }; signatureFromRequest,
} stringToSign,
algo,
activeSpan?.addEvent('Exiting V2 header auth check'); authType: 'REST-HEADER',
authCheckSpan.end(); signatureVersion: 'AWS',
return { signatureAge: Date.now() - timestamp,
err: null, securityToken: token,
params: {
version: 2,
data: {
accessKey,
signatureFromRequest,
stringToSign,
algo,
authType: 'REST-HEADER',
signatureVersion: 'AWS',
signatureAge: Date.now() - timestamp,
securityToken: token,
},
}, },
}; },
}); };
} }

View File

@ -5,113 +5,98 @@ import algoCheck from './algoCheck';
import constructStringToSign from './constructStringToSign'; import constructStringToSign from './constructStringToSign';
export function check(request: any, log: Logger, data: { [key: string]: string }, oTel: any) { export function check(request: any, log: Logger, data: { [key: string]: string }, oTel: any) {
const { activeSpan, activeTracerContext, tracer } = oTel; const { activeSpan, extractParamsSpan, activeTracerContext, tracer } = oTel;
activeSpan?.addEvent('Entered query auth check'); activeSpan?.addEvent('Entered query auth check');
return tracer.startActiveSpan('Query Auth Check', undefined, activeTracerContext, authCheckSpan => { log.trace('running query auth check');
log.trace('running query auth check'); activeSpan?.addEvent('Running query auth check');
activeSpan?.addEvent('Running query auth check'); if (request.method === 'POST') {
log.debug('query string auth not supported for post requests');
if (request.method === 'POST') { activeSpan.recordException(errors.NotImplemented);
log.debug('query string auth not supported for post requests'); extractParamsSpan.end();
activeSpan.recordException(errors.NotImplemented); return { err: errors.NotImplemented };
authCheckSpan.end(); }
return { err: errors.NotImplemented }; const token = data.SecurityToken;
} activeSpan?.addEvent('Extracting security token');
if (token && !constants.iamSecurityToken.pattern.test(token)) {
const token = data.SecurityToken; log.debug('invalid security token', { token });
activeSpan?.addEvent('Extracting security token'); activeSpan.recordException(errors.InvalidToken);
if (token && !constants.iamSecurityToken.pattern.test(token)) { extractParamsSpan.end();
log.debug('invalid security token', { token }); return { err: errors.InvalidToken };
activeSpan.recordException(errors.InvalidToken); }
authCheckSpan.end(); activeSpan?.addEvent('Extracted security token');
return { err: errors.InvalidToken }; /*
} Check whether request has expired or if
activeSpan?.addEvent('Extracted security token'); expires parameter is more than 604800000 milliseconds
(7 days) in the future.
/* Expires time is provided in seconds so need to
Check whether request has expired or if multiply by 1000 to obtain
expires parameter is more than 604800000 milliseconds milliseconds to compare to Date.now()
(7 days) in the future. */
Expires time is provided in seconds so need to activeSpan?.addEvent('Checking expiration time');
multiply by 1000 to obtain const expirationTime = parseInt(data.Expires, 10) * 1000;
milliseconds to compare to Date.now() if (Number.isNaN(expirationTime)) {
*/ log.debug('invalid expires parameter', { expires: data.Expires });
activeSpan?.addEvent('Checking expiration time'); activeSpan.recordException(errors.MissingSecurityHeader);
const expirationTime = parseInt(data.Expires, 10) * 1000; extractParamsSpan.end();
if (Number.isNaN(expirationTime)) { return { err: errors.MissingSecurityHeader };
log.debug('invalid expires parameter', { expires: data.Expires }); }
activeSpan.recordException(errors.MissingSecurityHeader); activeSpan?.addEvent('Checked expiration time');
authCheckSpan.end(); const currentTime = Date.now();
return { err: errors.MissingSecurityHeader }; const preSignedURLExpiry = process.env.PRE_SIGN_URL_EXPIRY
} && !Number.isNaN(process.env.PRE_SIGN_URL_EXPIRY)
activeSpan?.addEvent('Checked expiration time'); ? Number.parseInt(process.env.PRE_SIGN_URL_EXPIRY, 10)
: constants.defaultPreSignedURLExpiry * 1000;
const currentTime = Date.now(); if (expirationTime > currentTime + preSignedURLExpiry) {
log.debug('expires parameter too far in future', { expires: request.query.Expires });
const preSignedURLExpiry = process.env.PRE_SIGN_URL_EXPIRY activeSpan.recordException(errors.AccessDenied);
&& !Number.isNaN(process.env.PRE_SIGN_URL_EXPIRY) extractParamsSpan.end();
? Number.parseInt(process.env.PRE_SIGN_URL_EXPIRY, 10) return { err: errors.AccessDenied };
: constants.defaultPreSignedURLExpiry * 1000; }
if (currentTime > expirationTime) {
if (expirationTime > currentTime + preSignedURLExpiry) { log.debug('current time exceeds expires time', { expires: request.query.Expires });
log.debug('expires parameter too far in future', { expires: request.query.Expires }); activeSpan.recordException(errors.RequestTimeTooSkewed);
activeSpan.recordException(errors.AccessDenied); extractParamsSpan.end();
authCheckSpan.end(); return { err: errors.RequestTimeTooSkewed };
return { err: errors.AccessDenied }; }
} const accessKey = data.AWSAccessKeyId;
if (currentTime > expirationTime) { // @ts-ignore
log.debug('current time exceeds expires time', { expires: request.query.Expires }); log.addDefaultFields({ accessKey });
activeSpan.recordException(errors.RequestTimeTooSkewed); const signatureFromRequest = decodeURIComponent(data.Signature);
authCheckSpan.end(); log.trace('signature from request', { signatureFromRequest });
return { err: errors.RequestTimeTooSkewed }; activeSpan?.addEvent('Extracting signature from request');
} if (!accessKey || !signatureFromRequest) {
log.debug('invalid access key/signature parameters');
const accessKey = data.AWSAccessKeyId; activeSpan.recordException(errors.MissingSecurityHeader);
// @ts-ignore extractParamsSpan.end();
log.addDefaultFields({ accessKey }); return { err: errors.MissingSecurityHeader };
}
const signatureFromRequest = decodeURIComponent(data.Signature); const stringToSign = constructStringToSign(request, data, log);
log.trace('signature from request', { signatureFromRequest }); log.trace('constructed string to sign', { stringToSign });
activeSpan?.addEvent('Extracting signature from request'); activeSpan?.addEvent('Constructed string to sign v2 query');
const algo = algoCheck(signatureFromRequest.length);
if (!accessKey || !signatureFromRequest) { log.trace('algo for calculating signature', { algo });
log.debug('invalid access key/signature parameters'); activeSpan?.addEvent('Checked algorithm for calculating signature');
activeSpan.recordException(errors.MissingSecurityHeader); if (algo === undefined) {
authCheckSpan.end(); activeSpan.recordException(errors.InvalidArgument);
return { err: errors.MissingSecurityHeader }; extractParamsSpan.end();
} return { err: errors.InvalidArgument };
}
const stringToSign = constructStringToSign(request, data, log); activeSpan?.addEvent('Exiting query auth check');
log.trace('constructed string to sign', { stringToSign }); extractParamsSpan.end();
activeSpan?.addEvent('Constructed string to sign'); return {
err: null,
const algo = algoCheck(signatureFromRequest.length); params: {
log.trace('algo for calculating signature', { algo }); version: 2,
activeSpan?.addEvent('Checked algorithm for calculating signature'); data: {
accessKey,
if (algo === undefined) { signatureFromRequest,
activeSpan.recordException(errors.InvalidArgument); stringToSign,
authCheckSpan.end(); algo,
return { err: errors.InvalidArgument }; authType: 'REST-QUERY-STRING',
} signatureVersion: 'AWS',
securityToken: token,
activeSpan?.addEvent('Exiting query auth check');
authCheckSpan.end();
return {
err: null,
params: {
version: 2,
data: {
accessKey,
signatureFromRequest,
stringToSign,
algo,
authType: 'REST-QUERY-STRING',
signatureVersion: 'AWS',
securityToken: token,
},
}, },
}; },
}); };
} }

View File

@ -17,7 +17,7 @@ export default function constructStringToSign(params: {
log?: Logger; log?: Logger;
proxyPath?: string; proxyPath?: string;
awsService: string; awsService: string;
}): string | Error { }, oTel?: any,): string | Error {
const { const {
request, request,
signedHeaders, signedHeaders,
@ -29,7 +29,12 @@ export default function constructStringToSign(params: {
proxyPath, proxyPath,
} = params; } = params;
const path = proxyPath || request.path; const path = proxyPath || request.path;
const {
activeSpan,
activeTracerContext,
tracer,
} = oTel;
activeSpan?.addEvent('Constructing canonical request for Authv4');
const canonicalReqResult = createCanonicalRequest({ const canonicalReqResult = createCanonicalRequest({
pHttpVerb: request.method, pHttpVerb: request.method,
pResource: path, pResource: path,
@ -38,8 +43,7 @@ export default function constructStringToSign(params: {
pSignedHeaders: signedHeaders, pSignedHeaders: signedHeaders,
payloadChecksum, payloadChecksum,
service: params.awsService, service: params.awsService,
}); }, oTel);
// TODO Why that line? // TODO Why that line?
// @ts-ignore // @ts-ignore
if (canonicalReqResult instanceof Error) { if (canonicalReqResult instanceof Error) {
@ -51,9 +55,13 @@ export default function constructStringToSign(params: {
if (log) { if (log) {
log.debug('constructed canonicalRequest', { canonicalReqResult }); log.debug('constructed canonicalRequest', { canonicalReqResult });
} }
const createSignatureSpan = tracer.startSpan('Creating signature hash for AuthV4 using crypto sha256');
activeSpan?.addEvent('Creating signature hash for AuthV4 using crypto sha256');
const sha256 = crypto.createHash('sha256'); const sha256 = crypto.createHash('sha256');
const canonicalHex = sha256.update(canonicalReqResult, 'binary') const canonicalHex = sha256.update(canonicalReqResult, 'binary')
.digest('hex'); .digest('hex');
activeSpan?.addEvent('Created signature hash for AuthV4 using crypto sha256');
createSignatureSpan.end();
const stringToSign = `AWS4-HMAC-SHA256\n${timestamp}\n` + const stringToSign = `AWS4-HMAC-SHA256\n${timestamp}\n` +
`${credentialScope}\n${canonicalHex}`; `${credentialScope}\n${canonicalHex}`;
return stringToSign; return stringToSign;

View File

@ -19,8 +19,16 @@ export default function createCanonicalRequest(
pSignedHeaders: any; pSignedHeaders: any;
service: string; service: string;
payloadChecksum: string; payloadChecksum: string;
} },
oTel?: any,
) { ) {
const {
activeSpan,
activeTracerContext,
tracer,
} = oTel;
activeSpan?.addEvent('Entered createCanonicalRequest');
const pHttpVerb = params.pHttpVerb; const pHttpVerb = params.pHttpVerb;
const pResource = params.pResource; const pResource = params.pResource;
const pQuery = params.pQuery; const pQuery = params.pQuery;
@ -28,35 +36,34 @@ export default function createCanonicalRequest(
const pSignedHeaders = params.pSignedHeaders; const pSignedHeaders = params.pSignedHeaders;
const service = params.service; const service = params.service;
let payloadChecksum = params.payloadChecksum; let payloadChecksum = params.payloadChecksum;
const payloadChecksumSpan = tracer.startSpan('ComputePayloadChecksum');
if (!payloadChecksum) { if (!payloadChecksum) {
if (pHttpVerb === 'GET') { if (pHttpVerb === 'GET') {
payloadChecksum = 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b' + payloadChecksum = 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b' +
'934ca495991b7852b855'; '934ca495991b7852b855';
} else if (pHttpVerb === 'POST') { } else if (pHttpVerb === 'POST') {
let notEncodeStar = false; let notEncodeStar = false;
// The java sdk does not encode the '*' parameter to compute the
// signature, if the user-agent is recognized, we need to keep
// the plain '*' as well.
if (/aws-sdk-java\/[0-9.]+/.test(pHeaders['user-agent'])) { if (/aws-sdk-java\/[0-9.]+/.test(pHeaders['user-agent'])) {
notEncodeStar = true; notEncodeStar = true;
} }
let payload = queryString.stringify(pQuery, undefined, undefined, { let payload = queryString.stringify(pQuery, undefined, undefined, {
encodeURIComponent: input => awsURIencode(input, true, encodeURIComponent: input => awsURIencode(input, true, notEncodeStar),
notEncodeStar),
}); });
payload = payload.replace(/%20/g, '+'); payload = payload.replace(/%20/g, '+');
payloadChecksum = crypto.createHash('sha256') payloadChecksum = crypto.createHash('sha256')
.update(payload, 'binary').digest('hex').toLowerCase(); .update(payload, 'binary').digest('hex').toLowerCase();
} }
} }
payloadChecksumSpan.end();
const canonicalURISpan = tracer.startSpan('ComputeCanonicalURI');
const canonicalURI = !!pResource ? awsURIencode(pResource, false) : '/'; const canonicalURI = !!pResource ? awsURIencode(pResource, false) : '/';
canonicalURISpan.end();
// canonical query string const canonicalQueryStrSpan = tracer.startSpan('ComputeCanonicalQueryStr');
let canonicalQueryStr = ''; let canonicalQueryStr = '';
if (pQuery && !((service === 'iam' || service === 'ring' || if (pQuery && !((service === 'iam' || service === 'ring' || service === 'sts') && pHttpVerb === 'POST')) {
service === 'sts') &&
pHttpVerb === 'POST')) {
const sortedQueryParams = Object.keys(pQuery).sort().map(key => { const sortedQueryParams = Object.keys(pQuery).sort().map(key => {
const encodedKey = awsURIencode(key); const encodedKey = awsURIencode(key);
const value = pQuery[key] ? awsURIencode(pQuery[key]) : ''; const value = pQuery[key] ? awsURIencode(pQuery[key]) : '';
@ -64,32 +71,54 @@ export default function createCanonicalRequest(
}); });
canonicalQueryStr = sortedQueryParams.join('&'); canonicalQueryStr = sortedQueryParams.join('&');
} }
canonicalQueryStrSpan.end();
// signed headers const signedHeadersSpan = tracer.startSpan('SortSignedHeadersAlphabetically');
activeSpan?.addEvent('Splitting signed headers using deliminator: ;');
const signedHeadersList = pSignedHeaders.split(';'); const signedHeadersList = pSignedHeaders.split(';');
activeSpan?.addEvent('Split signed headers using ; as deliminator');
activeSpan?.addEvent('Sorting signed headers alphabetically');
signedHeadersList.sort((a: any, b: any) => a.localeCompare(b)); signedHeadersList.sort((a: any, b: any) => a.localeCompare(b));
activeSpan?.addEvent('Sorted signed headers alphabetically');
activeSpan?.addEvent('Joining signed headers using deliminator: ;');
const signedHeaders = signedHeadersList.join(';'); const signedHeaders = signedHeadersList.join(';');
activeSpan?.addEvent('Joined signed headers using ; as deliminator');
activeSpan.setAttributes({
'signedHeaders.request': pSignedHeaders,
'signedHeaders.request.authv4': signedHeaders,
});
signedHeadersSpan.setAttributes({
'signedHeaders.request': pSignedHeaders,
'signedHeaders.request.authv4': signedHeaders,
'code.url': 'https://github.com/scality/arsenal/blob/c6bb489adeb7419fdbcdf01db2b46a593747530d/lib/auth/v4/createCanonicalRequest.ts#L76',
'code.function': 'createCanonicalRequest',
'code.lineno': 76,
'code.filename': 'lib/auth/v4/createCanonicalRequest.ts',
});
signedHeadersSpan.end();
// canonical headers const canonicalHeadersListSpan = tracer.startSpan('FormatHeadersToMatch CanonicalHeadersList');
const canonicalHeadersList = signedHeadersList.map((signedHeader: any) => { const canonicalHeadersList = signedHeadersList.map((signedHeader: any) => {
if (pHeaders[signedHeader] !== undefined) { if (pHeaders[signedHeader] !== undefined) {
const trimmedHeader = pHeaders[signedHeader] const trimmedHeader = pHeaders[signedHeader]
.trim().replace(/\s+/g, ' '); .trim().replace(/\s+/g, ' ');
return `${signedHeader}:${trimmedHeader}\n`; return `${signedHeader}:${trimmedHeader}\n`;
} }
// nginx will strip the actual expect header so add value of
// header back here if it was included as a signed header
if (signedHeader === 'expect') { if (signedHeader === 'expect') {
return `${signedHeader}:100-continue\n`; return `${signedHeader}:100-continue\n`;
} }
// handle case where signed 'header' is actually query param
return `${signedHeader}:${pQuery[signedHeader]}\n`; return `${signedHeader}:${pQuery[signedHeader]}\n`;
}); });
canonicalHeadersListSpan.end();
const canonicalHeadersSpan = tracer.startSpan('JoinAllCanonicalHeaders using no deliminator');
const canonicalHeaders = canonicalHeadersList.join(''); const canonicalHeaders = canonicalHeadersList.join('');
canonicalHeadersSpan.end();
const canonicalRequestSpan = tracer.startSpan('ConstructCanonicalRequest');
const canonicalRequest = `${pHttpVerb}\n${canonicalURI}\n` + const canonicalRequest = `${pHttpVerb}\n${canonicalURI}\n` +
`${canonicalQueryStr}\n${canonicalHeaders}\n` + `${canonicalQueryStr}\n${canonicalHeaders}\n` +
`${signedHeaders}\n${payloadChecksum}`; `${signedHeaders}\n${payloadChecksum}`;
canonicalRequestSpan.end();
return canonicalRequest; return canonicalRequest;
} }

View File

@ -22,181 +22,165 @@ import {
* @param awsService - Aws service ('iam' or 's3') * @param awsService - Aws service ('iam' or 's3')
*/ */
export function check(request: any, log: Logger, data: { [key: string]: string }, awsService: string, oTel: any) { export function check(request: any, log: Logger, data: { [key: string]: string }, awsService: string, oTel: any) {
const { activeSpan, activeTracerContext, tracer } = oTel; const { activeSpan, extractParamsSpan, activeTracerContext, tracer } = oTel;
activeSpan?.addEvent('Entered V4 header auth check'); activeSpan?.addEvent('Entered V4 header auth check');
return tracer.startActiveSpan('V4 Header Auth Check', undefined, activeTracerContext, authCheckSpan => { log.trace('running header auth check');
log.trace('running header auth check'); activeSpan?.addEvent('Extracting security token');
const token = request.headers['x-amz-security-token'];
activeSpan?.addEvent('Extracting security token'); if (token && !constants.iamSecurityToken.pattern.test(token)) {
const token = request.headers['x-amz-security-token']; log.debug('invalid security token', { token });
if (token && !constants.iamSecurityToken.pattern.test(token)) { activeSpan.recordException(errors.InvalidToken);
log.debug('invalid security token', { token }); extractParamsSpan.end();
activeSpan.recordException(errors.InvalidToken); return { err: errors.InvalidToken };
authCheckSpan.end(); }
return { err: errors.InvalidToken }; activeSpan?.addEvent('Extracted security token');
} activeSpan?.addEvent('Extracting authorization header');
activeSpan?.addEvent('Extracted security token'); const authHeader = request.headers.authorization;
if (!authHeader) {
activeSpan?.addEvent('Extracting authorization header'); log.debug('missing authorization header');
const authHeader = request.headers.authorization; activeSpan.recordException(errors.MissingSecurityHeader);
if (!authHeader) { extractParamsSpan.end();
log.debug('missing authorization header'); return { err: errors.MissingSecurityHeader };
activeSpan.recordException(errors.MissingSecurityHeader); }
authCheckSpan.end(); activeSpan?.addEvent('Extracted authorization header');
return { err: errors.MissingSecurityHeader }; activeSpan?.addEvent('Extracting auth header items');
} const authHeaderItems = extractAuthItems(authHeader, log);
activeSpan?.addEvent('Extracted authorization header'); if (Object.keys(authHeaderItems).length < 3) {
log.debug('invalid authorization header', { authHeader });
activeSpan?.addEvent('Extracting auth header items'); activeSpan.recordException(errors.InvalidArgument);
const authHeaderItems = extractAuthItems(authHeader, log); extractParamsSpan.end();
if (Object.keys(authHeaderItems).length < 3) { return { err: errors.InvalidArgument };
log.debug('invalid authorization header', { authHeader }); }
activeSpan?.addEvent('Extracted auth header items');
const payloadChecksum = request.headers['x-amz-content-sha256'];
if (!payloadChecksum && awsService !== 'iam') {
log.debug('missing payload checksum');
activeSpan.recordException(errors.MissingSecurityHeader);
extractParamsSpan.end();
return { err: errors.MissingSecurityHeader };
}
if (payloadChecksum === 'STREAMING-AWS4-HMAC-SHA256-PAYLOAD') {
log.trace('requesting streaming v4 auth');
if (request.method !== 'PUT') {
log.debug('streaming v4 auth for put only', { method: 'auth/v4/headerAuthCheck.check' });
activeSpan.recordException(errors.InvalidArgument); activeSpan.recordException(errors.InvalidArgument);
authCheckSpan.end(); extractParamsSpan.end();
return { err: errors.InvalidArgument }; return { err: errors.InvalidArgument };
} }
activeSpan?.addEvent('Extracted auth header items'); if (!request.headers['x-amz-decoded-content-length']) {
const payloadChecksum = request.headers['x-amz-content-sha256'];
if (!payloadChecksum && awsService !== 'iam') {
log.debug('missing payload checksum');
activeSpan.recordException(errors.MissingSecurityHeader); activeSpan.recordException(errors.MissingSecurityHeader);
authCheckSpan.end(); extractParamsSpan.end();
return { err: errors.MissingSecurityHeader }; return { err: errors.MissingSecurityHeader };
} }
if (payloadChecksum === 'STREAMING-AWS4-HMAC-SHA256-PAYLOAD') { }
log.trace('requesting streaming v4 auth'); log.trace('authorization header from request', { authHeader });
if (request.method !== 'PUT') { const signatureFromRequest = authHeaderItems.signatureFromRequest!;
log.debug('streaming v4 auth for put only', { method: 'auth/v4/headerAuthCheck.check' }); const credentialsArr = authHeaderItems.credentialsArr!;
activeSpan.recordException(errors.InvalidArgument); const signedHeaders = authHeaderItems.signedHeaders!;
authCheckSpan.end(); activeSpan.addEvent('Checking if signed headers are complete');
return { err: errors.InvalidArgument }; if (!areSignedHeadersComplete(signedHeaders, request.headers)) {
} log.debug('signedHeaders are incomplete', { signedHeaders });
if (!request.headers['x-amz-decoded-content-length']) { activeSpan.recordException(errors.AccessDenied);
activeSpan.recordException(errors.MissingSecurityHeader); extractParamsSpan.end();
authCheckSpan.end(); return { err: errors.AccessDenied };
return { err: errors.MissingSecurityHeader }; }
} activeSpan.addEvent('Signed headers are complete');
let timestamp: string | undefined;
// check request timestamp
activeSpan.addEvent('Checking request timestamp');
const xAmzDate = request.headers['x-amz-date'];
if (xAmzDate) {
const xAmzDateArr = xAmzDate.split('T');
// check that x-amz- date has the correct format and after epochTime
if (xAmzDateArr.length === 2 && xAmzDateArr[0].length === 8 && xAmzDateArr[1].length === 7 && Number.parseInt(xAmzDateArr[0], 10) > 19700101) {
// format of x-amz- date is ISO 8601: YYYYMMDDTHHMMSSZ
timestamp = request.headers['x-amz-date'];
} }
} else if (request.headers.date) {
log.trace('authorization header from request', { authHeader }); timestamp = convertUTCtoISO8601(request.headers.date);
}
const signatureFromRequest = authHeaderItems.signatureFromRequest!; if (!timestamp) {
const credentialsArr = authHeaderItems.credentialsArr!; log.debug('missing or invalid date header', { method: 'auth/v4/headerAuthCheck.check' });
const signedHeaders = authHeaderItems.signedHeaders!; activeSpan.recordException(errors.AccessDenied.customizeDescription('Authentication requires a valid Date or x-amz-date header'));
extractParamsSpan.end();
activeSpan.addEvent('Checking if signed headers are complete'); return { err: errors.AccessDenied.customizeDescription('Authentication requires a valid Date or x-amz-date header') };
if (!areSignedHeadersComplete(signedHeaders, request.headers)) { }
log.debug('signedHeaders are incomplete', { signedHeaders }); activeSpan.addEvent('Request timestamp is valid');
activeSpan.recordException(errors.AccessDenied); activeSpan.addEvent('Validating credentials');
authCheckSpan.end(); const validationResult = validateCredentials(credentialsArr, timestamp, log);
return { err: errors.AccessDenied }; if (validationResult instanceof Error) {
} log.debug('credentials in improper format', { credentialsArr, timestamp, validationResult });
activeSpan.addEvent('Signed headers are complete'); activeSpan.recordException(validationResult);
extractParamsSpan.end();
let timestamp: string | undefined; return { err: validationResult };
// check request timestamp }
activeSpan.addEvent('Checking request timestamp'); activeSpan.addEvent('Credentials are valid');
const xAmzDate = request.headers['x-amz-date']; // credentialsArr is [accessKey, date, region, aws-service, aws4_request]
if (xAmzDate) { const scopeDate = credentialsArr[1];
const xAmzDateArr = xAmzDate.split('T'); const region = credentialsArr[2];
// check that x-amz- date has the correct format and after epochTime const service = credentialsArr[3];
if (xAmzDateArr.length === 2 && xAmzDateArr[0].length === 8 && xAmzDateArr[1].length === 7 && Number.parseInt(xAmzDateArr[0], 10) > 19700101) { const accessKey = credentialsArr.shift();
// format of x-amz- date is ISO 8601: YYYYMMDDTHHMMSSZ const credentialScope = credentialsArr.join('/');
timestamp = request.headers['x-amz-date']; // In AWS Signature Version 4, the signing key is valid for up to seven days
} // (see Introduction to Signing Requests.
} else if (request.headers.date) { // Therefore, a signature is also valid for up to seven days or
timestamp = convertUTCtoISO8601(request.headers.date); // less if specified by a bucket policy.
} // Since policies are not yet implemented, we will have a 15
if (!timestamp) { // minute default like in v2 Auth.
log.debug('missing or invalid date header', { method: 'auth/v4/headerAuthCheck.check' }); // See http://docs.aws.amazon.com/AmazonS3/latest/API/
activeSpan.recordException(errors.AccessDenied.customizeDescription('Authentication requires a valid Date or x-amz-date header')); // bucket-policy-s3-sigv4-conditions.html
authCheckSpan.end(); // TODO: When implementing bucket policies,
return { err: errors.AccessDenied.customizeDescription('Authentication requires a valid Date or x-amz-date header') }; // note that expiration can be shortened so
} // expiry is as set out in the policy.
activeSpan.addEvent('Request timestamp is valid'); // 15 minutes in seconds
activeSpan.addEvent('checking if signature is expired')
activeSpan.addEvent('Validating credentials'); const expiry = (15 * 60);
const validationResult = validateCredentials(credentialsArr, timestamp, log); const isTimeSkewed = checkTimeSkew(timestamp, expiry, log);
if (validationResult instanceof Error) { if (isTimeSkewed) {
log.debug('credentials in improper format', { credentialsArr, timestamp, validationResult }); activeSpan.recordException(errors.RequestTimeTooSkewed);
activeSpan.recordException(validationResult); extractParamsSpan.end();
authCheckSpan.end(); return { err: errors.RequestTimeTooSkewed };
return { err: validationResult }; }
} activeSpan.addEvent('signature is not expired');
activeSpan.addEvent('Credentials are valid'); activeSpan.addEvent('Constructing string to sign');
// credentialsArr is [accessKey, date, region, aws-service, aws4_request] const stringToSign = constructStringToSign({
const scopeDate = credentialsArr[1]; log,
const region = credentialsArr[2]; request,
const service = credentialsArr[3]; query: data,
const accessKey = credentialsArr.shift(); signedHeaders,
const credentialScope = credentialsArr.join('/'); credentialScope,
timestamp,
// In AWS Signature Version 4, the signing key is valid for up to seven days payloadChecksum,
// (see Introduction to Signing Requests. awsService: service,
// Therefore, a signature is also valid for up to seven days or }, oTel);
// less if specified by a bucket policy. log.trace('constructed stringToSign', { stringToSign });
// Since policies are not yet implemented, we will have a 15 if (stringToSign instanceof Error) {
// minute default like in v2 Auth. activeSpan.recordException(stringToSign);
// See http://docs.aws.amazon.com/AmazonS3/latest/API/ extractParamsSpan.end();
// bucket-policy-s3-sigv4-conditions.html return { err: stringToSign };
// TODO: When implementing bucket policies, }
// note that expiration can be shortened so activeSpan.addEvent('Constructed string to sign v4 headers');
// expiry is as set out in the policy. activeSpan.addEvent('Exiting V4 header auth check');
extractParamsSpan.end();
// 15 minutes in seconds return {
activeSpan.addEvent('checking if signature is expired') err: null,
const expiry = (15 * 60); params: {
const isTimeSkewed = checkTimeSkew(timestamp, expiry, log); version: 4,
if (isTimeSkewed) { data: {
activeSpan.recordException(errors.RequestTimeTooSkewed); accessKey,
authCheckSpan.end(); signatureFromRequest,
return { err: errors.RequestTimeTooSkewed }; region,
} service,
activeSpan.addEvent('signature is not expired'); scopeDate,
stringToSign,
activeSpan.addEvent('Constructing string to sign'); authType: 'REST-HEADER',
const stringToSign = constructStringToSign({ signatureVersion: 'AWS4-HMAC-SHA256',
log, signatureAge: Date.now() - convertAmzTimeToMs(timestamp),
request, credentialScope,
query: data, timestamp,
signedHeaders, securityToken: token,
credentialScope,
timestamp,
payloadChecksum,
awsService: service,
});
log.trace('constructed stringToSign', { stringToSign });
if (stringToSign instanceof Error) {
activeSpan.recordException(stringToSign);
authCheckSpan.end();
return { err: stringToSign };
}
activeSpan.addEvent('Constructed string to sign');
activeSpan.addEvent('Exiting V4 header auth check');
authCheckSpan.end();
return {
err: null,
params: {
version: 4,
data: {
accessKey,
signatureFromRequest,
region,
service,
scopeDate,
stringToSign,
authType: 'REST-HEADER',
signatureVersion: 'AWS4-HMAC-SHA256',
signatureAge: Date.now() - convertAmzTimeToMs(timestamp),
credentialScope,
timestamp,
securityToken: token,
},
}, },
}; },
}); };
} }

View File

@ -15,115 +15,116 @@ import { areSignedHeadersComplete } from './validateInputs';
export function check(request: any, log: Logger, data: { [key: string]: string }, oTel: any) { export function check(request: any, log: Logger, data: { [key: string]: string }, oTel: any) {
const { const {
activeSpan, activeSpan,
extractParamsSpan,
activeTracerContext, activeTracerContext,
tracer, tracer,
} = oTel; } = oTel;
activeSpan?.addEvent('Arsenal:: entered Arsenal.auth.v4.queryAuthCheck'); activeSpan?.addEvent('Arsenal:: entered Arsenal.auth.v4.queryAuthCheck');
return tracer.startActiveSpan('Arsenal::Arsenal.auth.v4.queryAuthCheck', undefined, activeTracerContext, authCheckSpan => { activeSpan?.addEvent('Arsenal:: extracting query parameters')
activeSpan?.addEvent('Arsenal:: extracting query parameters') const authParams = extractQueryParams(data, log);
const authParams = extractQueryParams(data, log); activeSpan?.addEvent('Arsenal:: extracting query params');
activeSpan?.addEvent('Arsenal:: extracting query params'); if (Object.keys(authParams).length !== 5) {
if (Object.keys(authParams).length !== 5) { activeSpan.recordException(errors.InvalidArgument);
activeSpan.recordException(errors.InvalidArgument); extractParamsSpan.end();
authCheckSpan.end(); return { err: errors.InvalidArgument };
return { err: errors.InvalidArgument }; }
}
// Query params are not specified in AWS documentation as case-insensitive,
// Query params are not specified in AWS documentation as case-insensitive, // so we use case-sensitive
// so we use case-sensitive const token = data['X-Amz-Security-Token'];
const token = data['X-Amz-Security-Token']; if (token && !constants.iamSecurityToken.pattern.test(token)) {
if (token && !constants.iamSecurityToken.pattern.test(token)) { log.debug('invalid security token', { token });
log.debug('invalid security token', { token }); activeSpan.recordException(errors.InvalidToken);
activeSpan.recordException(errors.InvalidToken); extractParamsSpan.end();
authCheckSpan.end(); return { err: errors.InvalidToken };
return { err: errors.InvalidToken }; }
}
const signedHeaders = authParams.signedHeaders!;
const signedHeaders = authParams.signedHeaders!; const signatureFromRequest = authParams.signatureFromRequest!;
const signatureFromRequest = authParams.signatureFromRequest!; const timestamp = authParams.timestamp!;
const timestamp = authParams.timestamp!; const expiry = authParams.expiry!;
const expiry = authParams.expiry!; const credential = authParams.credential!;
const credential = authParams.credential!;
if (!areSignedHeadersComplete(signedHeaders, request.headers)) {
if (!areSignedHeadersComplete(signedHeaders, request.headers)) { log.debug('signedHeaders are incomplete', { signedHeaders });
log.debug('signedHeaders are incomplete', { signedHeaders }); activeSpan.recordException(errors.AccessDenied);
activeSpan.recordException(errors.AccessDenied); extractParamsSpan.end();
authCheckSpan.end(); return { err: errors.AccessDenied };
return { err: errors.AccessDenied }; }
}
const validationResult = validateCredentials(credential, timestamp,
const validationResult = validateCredentials(credential, timestamp, log);
log); if (validationResult instanceof Error) {
if (validationResult instanceof Error) { log.debug('credentials in improper format', { credential,
log.debug('credentials in improper format', { credential, timestamp, validationResult });
timestamp, validationResult }); activeSpan.recordException(validationResult);
activeSpan.recordException(validationResult); extractParamsSpan.end();
authCheckSpan.end(); return { err: validationResult };
return { err: validationResult }; }
} const accessKey = credential[0];
const accessKey = credential[0]; const scopeDate = credential[1];
const scopeDate = credential[1]; const region = credential[2];
const region = credential[2]; const service = credential[3];
const service = credential[3]; const requestType = credential[4];
const requestType = credential[4];
const isTimeSkewed = checkTimeSkew(timestamp, expiry, log);
const isTimeSkewed = checkTimeSkew(timestamp, expiry, log); if (isTimeSkewed) {
if (isTimeSkewed) { activeSpan.recordException(errors.RequestTimeTooSkewed);
activeSpan.recordException(errors.RequestTimeTooSkewed); extractParamsSpan.end();
authCheckSpan.end(); return { err: errors.RequestTimeTooSkewed };
return { err: errors.RequestTimeTooSkewed }; }
}
// In query v4 auth, the canonical request needs
// In query v4 auth, the canonical request needs // to include the query params OTHER THAN
// to include the query params OTHER THAN // the signature so create a
// the signature so create a // copy of the query object and remove
// copy of the query object and remove // the X-Amz-Signature property.
// the X-Amz-Signature property. const queryWithoutSignature = Object.assign({}, data);
const queryWithoutSignature = Object.assign({}, data); delete queryWithoutSignature['X-Amz-Signature'];
delete queryWithoutSignature['X-Amz-Signature'];
// For query auth, instead of a
// For query auth, instead of a // checksum of the contents, the
// checksum of the contents, the // string 'UNSIGNED-PAYLOAD' should be
// string 'UNSIGNED-PAYLOAD' should be // added to the canonicalRequest in
// added to the canonicalRequest in // building string to sign
// building string to sign const payloadChecksum = 'UNSIGNED-PAYLOAD';
const payloadChecksum = 'UNSIGNED-PAYLOAD';
activeSpan?.addEvent('Constructing string to sign');
const stringToSign = constructStringToSign({ const stringToSign = constructStringToSign({
log, log,
request, request,
query: queryWithoutSignature, query: queryWithoutSignature,
signedHeaders, signedHeaders,
payloadChecksum, payloadChecksum,
timestamp, timestamp,
credentialScope: credentialScope:
`${scopeDate}/${region}/${service}/${requestType}`, `${scopeDate}/${region}/${service}/${requestType}`,
awsService: service, awsService: service,
}); }, oTel);
if (stringToSign instanceof Error) { activeSpan?.addEvent('Constructed string to sign v4 query');
activeSpan.recordException(stringToSign); if (stringToSign instanceof Error) {
authCheckSpan.end(); activeSpan.recordException(stringToSign);
return { err: stringToSign }; extractParamsSpan.end();
} return { err: stringToSign };
log.trace('constructed stringToSign', { stringToSign }); }
activeSpan.addEvent('Arsenal:: exiting Arsenal.auth.v4.queryAuthCheck'); log.trace('constructed stringToSign', { stringToSign });
authCheckSpan.end(); activeSpan.addEvent('Arsenal:: exiting Arsenal.auth.v4.queryAuthCheck');
return { extractParamsSpan.end();
err: null, return {
params: { err: null,
version: 4, params: {
data: { version: 4,
accessKey, data: {
signatureFromRequest, accessKey,
region, signatureFromRequest,
scopeDate, region,
stringToSign, scopeDate,
authType: 'REST-QUERY-STRING', stringToSign,
signatureVersion: 'AWS4-HMAC-SHA256', authType: 'REST-QUERY-STRING',
signatureAge: Date.now() - convertAmzTimeToMs(timestamp), signatureVersion: 'AWS4-HMAC-SHA256',
securityToken: token, signatureAge: Date.now() - convertAmzTimeToMs(timestamp),
}, securityToken: token,
}, },
}; },
}); };
} }

View File

@ -23,11 +23,6 @@ export default function routerGET(
} = dataRetrievalParams; } = dataRetrievalParams;
return tracer.startActiveSpan('Arsenal:: Performing Get API related operations using Cloudserver, Vault and Metadata', undefined, activeTracerContext, cloudserverApiSpan => { return tracer.startActiveSpan('Arsenal:: Performing Get API related operations using Cloudserver, Vault and Metadata', undefined, activeTracerContext, cloudserverApiSpan => {
activeSpan.addEvent('Request validated, routing request using routeGET() in arsenal'); activeSpan.addEvent('Request validated, routing request using routeGET() in arsenal');
cloudserverApiSpan.setAttributes({
'code.lineno': 9,
'code.filename': 'lib/s3routes/routes/routeGET.ts',
'code.function': 'routerGET()',
})
activeSpan.addEvent('Detecting which API to route to using arsenal routeGET()') activeSpan.addEvent('Detecting which API to route to using arsenal routeGET()')
log.debug('routing request', { method: 'routerGET' }); log.debug('routing request', { method: 'routerGET' });

View File

@ -638,7 +638,7 @@ export function responseStreamData(
}, },
} = retrieveDataParams; } = retrieveDataParams;
activeSpan.addEvent('Request processed, getting Data from sproxyd'); activeSpan.addEvent('Request processed, getting Data from sproxyd');
return tracer.startActiveSpan('Getting Data From RING', undefined, activeTracerContext, sproxydSpan => { return tracer.startActiveSpan('Getting Object Data from RING', undefined, activeTracerContext, sproxydSpan => {
sproxydSpan.setAttributes({ sproxydSpan.setAttributes({
'code.function': 'Arsenal:: responseStreamData()', 'code.function': 'Arsenal:: responseStreamData()',
'code.filepath': 'lib/s3routes/routesUtils.js', 'code.filepath': 'lib/s3routes/routesUtils.js',