Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/docker/local.sh
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export S3METADATA=file
export S3VAULT=scality
export MPU_TESTING="yes"

export CLOUDSERVER_IMAGE_BEFORE_SSE_MIGRATION=ghcr.io/scality/cloudserver:7.70.66
export CLOUDSERVER_IMAGE_BEFORE_SSE_MIGRATION=ghcr.io/scality/cloudserver:7.70.63
export CLOUDSERVER_IMAGE_ORIGINAL=ghcr.io/scality/cloudserver:7.70.70

export VAULT_IMAGE_BEFORE_SSE_MIGRATION=ghcr.io/scality/vault:7.70.31
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -407,10 +407,10 @@ jobs:
S3BACKEND: file
S3VAULT: scality
# Versions before using kms scality arn prefix & sse migration used to seed buckets & objects
CLOUDSERVER_VERSION_BEFORE: 7.70.66
CLOUDSERVER_VERSION_BEFORE: 7.70.63
VAULT_VERSION_BEFORE: 7.70.31
VAULT_VERSION_CURRENT: 7.70.32
CLOUDSERVER_IMAGE_BEFORE_SSE_MIGRATION: ghcr.io/${{ github.repository }}:7.70.66
CLOUDSERVER_IMAGE_BEFORE_SSE_MIGRATION: ghcr.io/${{ github.repository }}:7.70.63
VAULT_IMAGE_BEFORE_SSE_MIGRATION: ghcr.io/scality/vault:7.70.31
CLOUDSERVER_IMAGE: ghcr.io/${{ github.repository }}:${{ github.sha }}
VAULT_IMAGE: ghcr.io/scality/vault:7.70.32
Expand Down
9 changes: 9 additions & 0 deletions lib/api/initiateMultipartUpload.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ const { setExpirationHeaders } = require('./apiUtils/object/expirationHeaders');
const { data } = require('../data/wrapper');
const { setSSEHeaders } = require('./apiUtils/object/sseHeaders');
const { updateEncryption } = require('./apiUtils/bucket/updateEncryption');
const kms = require('../kms/wrapper');

/*
Sample xml response:
Expand Down Expand Up @@ -335,6 +336,14 @@ function initiateMultipartUpload(authInfo, request, log, callback) {
return next(null, corsHeaders, destinationBucket, objectSSEConfig);
}
),
// If SSE configured, test kms key encryption access, but ignore cipher bundle
(corsHeaders, destinationBucket, objectSSEConfig, next) => {
if (objectSSEConfig) {
return kms.createCipherBundle(objectSSEConfig, log,
err => next(err, corsHeaders, destinationBucket, objectSSEConfig));
}
return next(null, corsHeaders, destinationBucket, objectSSEConfig);
},
],
(error, corsHeaders, destinationBucket, objectSSEConfig) => {
if (error) {
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
"homepage": "https://github.com/scality/S3#readme",
"dependencies": {
"@hapi/joi": "^17.1.0",
"arsenal": "git+https://github.com/scality/Arsenal#7.70.47",
"arsenal": "git+https://github.com/scality/Arsenal#7.70.48",
"async": "~2.5.0",
"aws-sdk": "2.905.0",
"azure-storage": "^2.1.0",
Expand Down
160 changes: 160 additions & 0 deletions tests/functional/sse-kms-migration/arnPrefix.js
Original file line number Diff line number Diff line change
Expand Up @@ -505,3 +505,163 @@ describe('ensure MPU use good SSE', () => {
);
});
});
describe('KMS error', () => {
const sseConfig = { algo: 'aws:kms', masterKeyId: true };
const Bucket = 'bkt-kms-err';
const Key = 'obj';
const body = 'content';

let mpuEncrypted;
let mpuPlaintext;

let masterKeyId;
let masterKeyArn;

let expected;

const expectedKMIP = {
code: 'KMS.NotFoundException',
msg: (action, keyId) => new RegExp(`^KMS \\(KMIP\\) error for ${action} on ${keyId}\\..*`),
};
const expectedAWS = {
code: 'KMS.KMSInvalidStateException',
msg: (_, keyId) => new RegExp(`${keyId} is pending deletion\\.`),
};
/**
* localkms container returns a different error message when the key is pending deletion
* as we decrypt without passing the keyId, so we need to handle it separately
*/
const expectedLocalKms = {
code: 'KMS.AccessDeniedException',
msg: () => new RegExp(
'The ciphertext refers to a customer master key that does not exist, ' +
'does not exist in this region, or you are not allowed to access\\.'
),
};
if (helpers.config.backends.kms === 'kmip') {
expected = expectedKMIP;
} else if (helpers.config.backends.kms === 'aws') {
expected = expectedAWS;
} else {
throw new Error(`Unsupported KMS backend: ${helpers.config.backends.kms}`);
}

function assertKmsError(action, keyId) {
return err => {
if (helpers.config.backends.kms === 'aws' && action === 'Decrypt') {
assert.strictEqual(err.name, expectedLocalKms.code);
assert.match(err.message, expectedLocalKms.msg(action, keyId));
return true;
}
assert.strictEqual(err.name, expected.code);
assert.match(err.message, expected.msg(action, keyId));
return true;
};
}

before(async () => {
void await helpers.s3.createBucket({ Bucket }).promise();

await helpers.s3.putObject({
...helpers.putObjParams(Bucket, 'plaintext', {}, null),
Body: body,
}).promise();

mpuPlaintext = await helpers.s3.createMultipartUpload(
helpers.putObjParams(Bucket, 'mpuPlaintext', {}, null)).promise();

({ masterKeyId, masterKeyArn } = await helpers.createKmsKey(log));

await helpers.putEncryptedObject(Bucket, Key, sseConfig, masterKeyArn, body);
// ensure we can decrypt and read the object
const obj = await helpers.s3.getObject({ Bucket, Key }).promise();
assert.strictEqual(obj.Body.toString(), body);

mpuEncrypted = await helpers.s3.createMultipartUpload(
helpers.putObjParams(Bucket, 'mpuEncrypted', sseConfig, masterKeyArn)).promise();

// make key unavailable
void await helpers.destroyKmsKey(masterKeyArn, log);
});

after(async () => {
void await helpers.cleanup(Bucket);
Comment thread
BourgoisMickael marked this conversation as resolved.
if (masterKeyArn) {
try {
void await helpers.destroyKmsKey(masterKeyArn, log);
} catch (e) { void e; }
[masterKeyArn, masterKeyId] = [null, null];
}
});

const testCases = [
{
action: 'putObject', kmsAction: 'Encrypt',
fct: async ({ masterKeyArn }) =>
helpers.putEncryptedObject(Bucket, 'fail', sseConfig, masterKeyArn, body),
},
{
action: 'getObject', kmsAction: 'Decrypt',
fct: async () => helpers.s3.getObject({ Bucket, Key }).promise(),
},
{
action: 'copyObject', detail: ' when getting from source', kmsAction: 'Decrypt',
fct: async () =>
helpers.s3.copyObject({ Bucket, Key: 'copy', CopySource: `${Bucket}/${Key}` }).promise(),
},
{
action: 'copyObject', detail: ' when putting to destination', kmsAction: 'Encrypt',
fct: async ({ masterKeyArn }) => helpers.s3.copyObject({
Bucket,
Key: 'copyencrypted',
CopySource: `${Bucket}/plaintext`,
ServerSideEncryption: 'aws:kms',
SSEKMSKeyId: masterKeyArn,
}).promise(),
},
{
action: 'createMPU', kmsAction: 'Encrypt',
fct: async ({ masterKeyArn }) => helpers.s3.createMultipartUpload(
helpers.putObjParams(Bucket, 'mpuKeyEncryptedFail', sseConfig, masterKeyArn)).promise(),
},
{
action: 'mpu uploadPartCopy', detail: ' when getting from source', kmsAction: 'Decrypt',
fct: async ({ mpuPlaintext }) => helpers.s3.uploadPartCopy({
UploadId: mpuPlaintext.UploadId,
Bucket,
Key: 'mpuPlaintext',
PartNumber: 1,
CopySource: `${Bucket}/${Key}`,
}).promise(),
},
{
action: 'mpu uploadPart', detail: ' when putting to destination', kmsAction: 'Encrypt',
fct: async ({ mpuEncrypted }) => helpers.s3.uploadPart({
UploadId: mpuEncrypted.UploadId,
Bucket,
Key: 'mpuEncrypted',
PartNumber: 1,
Body: body,
}).promise(),
},
{
action: 'mpu uploadPartCopy', detail: ' when putting to destination', kmsAction: 'Encrypt',
fct: async ({ mpuEncrypted }) => helpers.s3.uploadPartCopy({
UploadId: mpuEncrypted.UploadId,
Bucket,
Key: 'mpuEncrypted',
PartNumber: 1,
CopySource: `${Bucket}/plaintext`,
}).promise(),
},
];

testCases.forEach(({ action, kmsAction, fct, detail }) => {
it(`${action} should fail with kms error${detail || ''}`, async () => {
await assert.rejects(
fct({ masterKeyArn, mpuEncrypted, mpuPlaintext }),
assertKmsError(kmsAction, masterKeyId),
);
});
});
});
3 changes: 3 additions & 0 deletions tests/functional/sse-kms-migration/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@ async function createKmsKey(log) {
});
}

const destroyKmsKey = promisify(kms.destroyBucketKey);

async function cleanup(Bucket) {
await bucketUtil.empty(Bucket);
await s3.deleteBucket({ Bucket }).promise();
Expand All @@ -112,5 +114,6 @@ module.exports = {
putEncryptedObject,
getObjectMDSSE,
createKmsKey,
destroyKmsKey,
cleanup,
};
6 changes: 3 additions & 3 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -501,9 +501,9 @@ arraybuffer.slice@~0.0.7:
optionalDependencies:
ioctl "^2.0.2"

"arsenal@git+https://github.com/scality/Arsenal#7.70.47":
version "7.70.47"
resolved "git+https://github.com/scality/Arsenal#6ff3267c33ceee0e4af3aa7757f0a17a5d53db54"
"arsenal@git+https://github.com/scality/Arsenal#7.70.48":
version "7.70.48"
resolved "git+https://github.com/scality/Arsenal#bf27689b29293a1f24ff3090720067525f282baa"
dependencies:
"@js-sdsl/ordered-set" "^4.4.2"
"@types/async" "^3.2.12"
Expand Down
Loading