Compare commits
2 Commits
developmen
...
improvemen
Author | SHA1 | Date |
---|---|---|
naren-scality | 18cc1ae62f | |
Rahul Padigela | f114c1be59 |
|
@ -97,7 +97,6 @@ const constants = {
|
|||
],
|
||||
// Headers supported by AWS that we do not currently support.
|
||||
unsupportedHeaders: [
|
||||
'x-amz-server-side-encryption',
|
||||
'x-amz-server-side-encryption-customer-algorithm',
|
||||
'x-amz-server-side-encryption-aws-kms-key-id',
|
||||
'x-amz-server-side-encryption-context',
|
||||
|
|
|
@ -45,6 +45,11 @@ function initiateMultipartUpload(authInfo, request, log, callback) {
|
|||
log.debug('processing request', { method: 'initiateMultipartUpload' });
|
||||
const bucketName = request.bucketName;
|
||||
const objectKey = request.objectKey;
|
||||
const sseHeader =
|
||||
request.headers['x-amz-server-side-encryption'];
|
||||
const invalidSSEError
|
||||
= errors.InvalidArgument.customizeDescription('The encryption method '
|
||||
+ 'specified is not supported');
|
||||
// Note that we are using the string set forth in constants.js
|
||||
// to split components in the storage
|
||||
// of each MPU. AWS does not restrict characters in object keys so
|
||||
|
@ -160,6 +165,13 @@ function initiateMultipartUpload(authInfo, request, log, callback) {
|
|||
const serverSideEncryption =
|
||||
destinationBucket.getServerSideEncryption();
|
||||
let cipherBundle = null;
|
||||
if ((!serverSideEncryption && sseHeader) ||
|
||||
(sseHeader === 'AES256' &&
|
||||
serverSideEncryption.algorithm !== sseHeader)) {
|
||||
// x-amz-server-side-encryption is allowed only if bucket
|
||||
// encryption is enabled and if the value is AES256
|
||||
return callback(invalidSSEError);
|
||||
}
|
||||
if (serverSideEncryption) {
|
||||
cipherBundle = {
|
||||
algorithm: serverSideEncryption.algorithm,
|
||||
|
|
|
@ -213,6 +213,11 @@ function objectCopy(authInfo, request, sourceBucket,
|
|||
};
|
||||
const websiteRedirectHeader =
|
||||
request.headers['x-amz-website-redirect-location'];
|
||||
const sseHeader =
|
||||
request.headers['x-amz-server-side-encryption'];
|
||||
const invalidSSEError
|
||||
= errors.InvalidArgument.customizeDescription('The encryption method '
|
||||
+ 'specified is not supported');
|
||||
|
||||
if (!validateWebsiteHeader(websiteRedirectHeader)) {
|
||||
const err = errors.InvalidRedirectLocation;
|
||||
|
@ -326,6 +331,14 @@ function objectCopy(authInfo, request, sourceBucket,
|
|||
const destLocationConstraintName =
|
||||
storeMetadataParams.dataStoreName;
|
||||
|
||||
if ((!serverSideEncryption && sseHeader) ||
|
||||
(sseHeader === 'AES256' &&
|
||||
serverSideEncryption.algorithm !== sseHeader)) {
|
||||
// x-amz-server-side-encryption is allowed only if bucket
|
||||
// encryption is enabled and if the value is AES256
|
||||
return next(invalidSSEError);
|
||||
}
|
||||
|
||||
// skip if source and dest and location constraint the same and
|
||||
// versioning is not enabled
|
||||
// still send along serverSideEncryption info so algo
|
||||
|
|
|
@ -33,6 +33,7 @@ const versionIdUtils = versioning.VersionID;
|
|||
*/
|
||||
function objectPut(authInfo, request, streamingV4Params, log, callback) {
|
||||
log.debug('processing request', { method: 'objectPut' });
|
||||
const { headers } = request;
|
||||
if (!aclUtils.checkGrantHeaderValidity(request.headers)) {
|
||||
log.trace('invalid acl header');
|
||||
return callback(errors.InvalidArgument);
|
||||
|
@ -41,6 +42,9 @@ function objectPut(authInfo, request, streamingV4Params, log, callback) {
|
|||
if (queryContainsVersionId instanceof Error) {
|
||||
return callback(queryContainsVersionId);
|
||||
}
|
||||
const sseHeader = headers['x-amz-server-side-encryption'];
|
||||
const invalidSSEError = errors.InvalidArgument.customizeDescription(
|
||||
'The encryption method specified is not supported');
|
||||
const bucketName = request.bucketName;
|
||||
const objectKey = request.objectKey;
|
||||
const requestType = 'objectPut';
|
||||
|
@ -73,6 +77,13 @@ function objectPut(authInfo, request, streamingV4Params, log, callback) {
|
|||
},
|
||||
function createCipherBundle(next) {
|
||||
const serverSideEncryption = bucket.getServerSideEncryption();
|
||||
if ((!serverSideEncryption && sseHeader) ||
|
||||
(sseHeader === 'AES256' &&
|
||||
serverSideEncryption.algorithm !== sseHeader)) {
|
||||
// x-amz-server-side-encryption is allowed only if bucket
|
||||
// encryption is enabled and if the value is AES256
|
||||
return next(invalidSSEError);
|
||||
}
|
||||
if (serverSideEncryption) {
|
||||
return kms.createCipherBundle(
|
||||
serverSideEncryption, log, next);
|
||||
|
|
|
@ -0,0 +1,259 @@
|
|||
const AWS = require('aws-sdk');
|
||||
const uuid4 = require('uuid/v4');
|
||||
const config = require('../config.json');
|
||||
const { auth } = require('arsenal');
|
||||
const http = require('http');
|
||||
const https = require('https');
|
||||
const assert = require('assert');
|
||||
const logger = { info: msg => process.stdout.write(`${msg}\n`) };
|
||||
const async = require('async');
|
||||
|
||||
|
||||
function _createBucket(name, encrypt, done) {
|
||||
const { transport, ipAddress, accessKey, secretKey } = config;
|
||||
const verbose = false;
|
||||
const options = {
|
||||
host: ipAddress,
|
||||
port: 8000,
|
||||
method: 'PUT',
|
||||
path: `/${name}/`,
|
||||
rejectUnauthorized: false,
|
||||
};
|
||||
|
||||
if (encrypt) {
|
||||
Object.assign(options, {
|
||||
headers: {
|
||||
'x-amz-scal-server-side-encryption': 'AES256',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
logger.info(`Creating encrypted bucket ${name}`);
|
||||
const client = transport === 'https' ? https : http;
|
||||
const request = client.request(options, response => {
|
||||
if (verbose) {
|
||||
logger.info('response status code', {
|
||||
statusCode: response.statusCode,
|
||||
});
|
||||
logger.info('response headers', { headers: response.headers });
|
||||
}
|
||||
const body = [];
|
||||
response.setEncoding('utf8');
|
||||
response.on('data', chunk => body.push(chunk));
|
||||
response.on('end', () => {
|
||||
if (response.statusCode >= 200 && response.statusCode < 300) {
|
||||
logger.info('Success', {
|
||||
statusCode: response.statusCode,
|
||||
body: verbose ? body.join('') : undefined,
|
||||
});
|
||||
done(null);
|
||||
} else {
|
||||
done({
|
||||
statusCode: response.statusCode,
|
||||
body: body.join(''),
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
auth.client.generateV4Headers(request, '', accessKey, secretKey, 's3');
|
||||
if (verbose) {
|
||||
logger.info('request headers', { headers: request._headers });
|
||||
}
|
||||
request.end();
|
||||
}
|
||||
|
||||
function _buildS3() {
|
||||
const { transport, ipAddress, accessKey, secretKey } = config;
|
||||
AWS.config.update({
|
||||
endpoint: `${transport}://${ipAddress}:8000`,
|
||||
accessKeyId: accessKey,
|
||||
secretAccessKey: secretKey,
|
||||
sslEnabled: transport === 'https',
|
||||
s3ForcePathStyle: true,
|
||||
});
|
||||
return new AWS.S3();
|
||||
}
|
||||
const s3 = _buildS3();
|
||||
|
||||
function _putObject(bucketName, objectName, encrypt, cb) {
|
||||
const params = {
|
||||
Bucket: bucketName,
|
||||
Key: objectName,
|
||||
Body: 'I am the best content ever',
|
||||
};
|
||||
|
||||
if (encrypt) {
|
||||
Object.assign(params, {
|
||||
ServerSideEncryption: 'AES256',
|
||||
});
|
||||
}
|
||||
|
||||
s3.putObject(params, err => {
|
||||
cb(err);
|
||||
});
|
||||
}
|
||||
|
||||
function _copyObject(sourceBucket, sourceObject, targetBucket, targetObject,
|
||||
encrypt, cb) {
|
||||
const params = {
|
||||
Bucket: targetBucket,
|
||||
CopySource: `/${sourceBucket}/${sourceObject}`,
|
||||
Key: targetObject,
|
||||
};
|
||||
|
||||
if (encrypt) {
|
||||
Object.assign(params, {
|
||||
ServerSideEncryption: 'AES256',
|
||||
});
|
||||
}
|
||||
|
||||
s3.copyObject(params, err => {
|
||||
cb(err);
|
||||
});
|
||||
}
|
||||
|
||||
function _initiateMultipartUpload(bucketName, objectName, encrypt, cb) {
|
||||
const params = {
|
||||
Bucket: bucketName,
|
||||
Key: objectName,
|
||||
};
|
||||
|
||||
if (encrypt) {
|
||||
Object.assign(params, {
|
||||
ServerSideEncryption: 'AES256',
|
||||
});
|
||||
}
|
||||
|
||||
s3.createMultipartUpload(params, err => {
|
||||
cb(err);
|
||||
});
|
||||
}
|
||||
|
||||
describe('KMIP backed server-side encryption', () => {
|
||||
let bucketName;
|
||||
let objectName;
|
||||
const encryptionErrorMessage
|
||||
= 'The encryption method specified is not supported';
|
||||
|
||||
beforeEach(() => {
|
||||
bucketName = uuid4();
|
||||
objectName = uuid4();
|
||||
});
|
||||
|
||||
it('should create an encrypted bucket', done => {
|
||||
_createBucket(bucketName, true, err => {
|
||||
assert.equal(err, null, 'Expected success, ' +
|
||||
`got error ${JSON.stringify(err)}`);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should create an encrypted bucket and upload an object', done => {
|
||||
async.waterfall([
|
||||
next => _createBucket(bucketName, true, err => next(err)),
|
||||
next => _putObject(bucketName, objectName, false, err => next(err)),
|
||||
], err => {
|
||||
assert.equal(err, null, 'Expected success, ' +
|
||||
`got error ${JSON.stringify(err)}`);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should allow object PUT with SSE header in encrypted bucket', done => {
|
||||
async.waterfall([
|
||||
next => _createBucket(bucketName, true, err => next(err)),
|
||||
next => _putObject(bucketName, objectName, true, err => next(err)),
|
||||
], err => {
|
||||
assert.equal(err, null, 'Expected success, ' +
|
||||
`got error ${JSON.stringify(err)}`);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not allow object PUT with SSE header ' +
|
||||
'in bucket with no SSE', done => {
|
||||
async.waterfall([
|
||||
next => _createBucket(bucketName, false, err => {
|
||||
assert.equal(err, null, 'Expected success, ' +
|
||||
`got error ${JSON.stringify(err)}`);
|
||||
return next();
|
||||
}),
|
||||
next => _putObject(bucketName, objectName, true, err => next(err)),
|
||||
], err => {
|
||||
assert.strictEqual(err.statusCode, 400);
|
||||
assert.strictEqual(err.message, encryptionErrorMessage);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should allow object copy with SSE header in encrypted bucket', done => {
|
||||
async.waterfall([
|
||||
next => _createBucket(bucketName, false, err => next(err)),
|
||||
next => _putObject(bucketName, objectName, false, err => next(err)),
|
||||
next => _createBucket(`${bucketName}2`, true, err => next(err)),
|
||||
next => _copyObject(bucketName, objectName, `${bucketName}2`,
|
||||
`${objectName}2`, true, err => next(err)),
|
||||
], err => {
|
||||
assert.equal(err, null, 'Expected success, ' +
|
||||
`got error ${JSON.stringify(err)}`);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not allow object copy with SSE header ' +
|
||||
'in bucket with no SSE', done => {
|
||||
async.waterfall([
|
||||
next => _createBucket(bucketName, false, err => {
|
||||
assert.equal(err, null, 'Expected success, ' +
|
||||
`got error ${JSON.stringify(err)}`);
|
||||
return next();
|
||||
}),
|
||||
next => _putObject(bucketName, objectName, false, err => {
|
||||
assert.equal(err, null, 'Expected success, ' +
|
||||
`got error ${JSON.stringify(err)}`);
|
||||
return next();
|
||||
}),
|
||||
next => _createBucket(`${bucketName}2`, false, err => {
|
||||
assert.equal(err, null, 'Expected success, ' +
|
||||
`got error ${JSON.stringify(err)}`);
|
||||
return next();
|
||||
}),
|
||||
next => _copyObject(bucketName, objectName, `${bucketName}2`,
|
||||
`${objectName}2`, true, err => next(err)),
|
||||
], err => {
|
||||
assert.strictEqual(err.statusCode, 400);
|
||||
assert.strictEqual(err.message, encryptionErrorMessage);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should allow creating mpu with SSE header ' +
|
||||
'in encrypted bucket', done => {
|
||||
async.waterfall([
|
||||
next => _createBucket(bucketName, true, err => next(err)),
|
||||
next => _initiateMultipartUpload(bucketName, objectName,
|
||||
true, err => next(err)),
|
||||
], err => {
|
||||
assert.equal(err, null, 'Expected success, ' +
|
||||
`got error ${JSON.stringify(err)}`);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not allow mpu creation with SSE header ' +
|
||||
'in bucket with no SSE', done => {
|
||||
async.waterfall([
|
||||
next => _createBucket(bucketName, false, err => {
|
||||
assert.equal(err, null, 'Expected success, ' +
|
||||
`got error ${JSON.stringify(err)}`);
|
||||
return next();
|
||||
}),
|
||||
next => _initiateMultipartUpload(bucketName, objectName,
|
||||
true, err => next(err)),
|
||||
], err => {
|
||||
assert.strictEqual(err.statusCode, 400);
|
||||
assert.strictEqual(err.message, encryptionErrorMessage);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
Loading…
Reference in New Issue