Skip to content

Commit 75188ef

Browse files
committed
Merge branch 'w/9.0/improvement/CLDSRV-670-kms-copy' into tmp/octopus/w/9.1/improvement/CLDSRV-670-kms-copy
2 parents 4e4a06e + 0c09de5 commit 75188ef

5 files changed

Lines changed: 184 additions & 12 deletions

File tree

lib/api/initiateMultipartUpload.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ const { getObjectSSEConfiguration } = require('./apiUtils/bucket/bucketEncryptio
2323
const { setExpirationHeaders } = require('./apiUtils/object/expirationHeaders');
2424
const { setSSEHeaders } = require('./apiUtils/object/sseHeaders');
2525
const { updateEncryption } = require('./apiUtils/bucket/updateEncryption');
26+
const kms = require('../kms/wrapper');
2627

2728
/*
2829
Sample xml response:
@@ -359,6 +360,14 @@ function initiateMultipartUpload(authInfo, request, log, callback) {
359360
return next(null, corsHeaders, destinationBucket, objectSSEConfig);
360361
}
361362
),
363+
// If SSE configured, test kms key encryption access, but ignore cipher bundle
364+
(corsHeaders, destinationBucket, objectSSEConfig, next) => {
365+
if (objectSSEConfig) {
366+
return kms.createCipherBundle(objectSSEConfig, log,
367+
err => next(err, corsHeaders, destinationBucket, objectSSEConfig));
368+
}
369+
return next(null, corsHeaders, destinationBucket, objectSSEConfig);
370+
},
362371
],
363372
(error, corsHeaders, destinationBucket, objectSSEConfig) => {
364373
if (error) {

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
"dependencies": {
2222
"@azure/storage-blob": "^12.25.0",
2323
"@hapi/joi": "^17.1.1",
24-
"arsenal": "git+https://github.com/scality/Arsenal#8.2.23",
24+
"arsenal": "git+https://github.com/scality/Arsenal#8.2.24",
2525
"async": "2.6.4",
2626
"aws-sdk": "^2.1692.0",
2727
"bucketclient": "scality/bucketclient#8.2.4",

tests/functional/sse-kms-migration/arnPrefix.js

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -504,3 +504,163 @@ describe('ensure MPU use good SSE', () => {
504504
);
505505
});
506506
});
507+
describe('KMS error', () => {
508+
const sseConfig = { algo: 'aws:kms', masterKeyId: true };
509+
const Bucket = 'bkt-kms-err';
510+
const Key = 'obj';
511+
const body = 'content';
512+
513+
let mpuEncrypted;
514+
let mpuPlaintext;
515+
516+
let masterKeyId;
517+
let masterKeyArn;
518+
519+
let expected;
520+
521+
const expectedKMIP = {
522+
code: 'KMS.NotFoundException',
523+
msg: (action, keyId) => new RegExp(`^KMS \\(KMIP\\) error for ${action} on ${keyId}\\..*`),
524+
};
525+
const expectedAWS = {
526+
code: 'KMS.KMSInvalidStateException',
527+
msg: (_, keyId) => new RegExp(`${keyId} is pending deletion\\.`),
528+
};
529+
/**
530+
* localkms container returns a different error message when the key is pending deletion
531+
* as we decrypt without passing the keyId, so we need to handle it separately
532+
*/
533+
const expectedLocalKms = {
534+
code: 'KMS.AccessDeniedException',
535+
msg: () => new RegExp(
536+
'The ciphertext refers to a customer master key that does not exist, ' +
537+
'does not exist in this region, or you are not allowed to access\\.'
538+
),
539+
};
540+
if (helpers.config.backends.kms === 'kmip') {
541+
expected = expectedKMIP;
542+
} else if (helpers.config.backends.kms === 'aws') {
543+
expected = expectedAWS;
544+
} else {
545+
throw new Error(`Unsupported KMS backend: ${helpers.config.backends.kms}`);
546+
}
547+
548+
function assertKmsError(action, keyId) {
549+
return err => {
550+
if (helpers.config.backends.kms === 'aws' && action === 'Decrypt') {
551+
assert.strictEqual(err.name, expectedLocalKms.code);
552+
assert.match(err.message, expectedLocalKms.msg(action, keyId));
553+
return true;
554+
}
555+
assert.strictEqual(err.name, expected.code);
556+
assert.match(err.message, expected.msg(action, keyId));
557+
return true;
558+
};
559+
}
560+
561+
before(async () => {
562+
void await helpers.s3.createBucket({ Bucket }).promise();
563+
564+
await helpers.s3.putObject({
565+
...helpers.putObjParams(Bucket, 'plaintext', {}, null),
566+
Body: body,
567+
}).promise();
568+
569+
mpuPlaintext = await helpers.s3.createMultipartUpload(
570+
helpers.putObjParams(Bucket, 'mpuPlaintext', {}, null)).promise();
571+
572+
({ masterKeyId, masterKeyArn } = await helpers.createKmsKey(log));
573+
574+
await helpers.putEncryptedObject(Bucket, Key, sseConfig, masterKeyArn, body);
575+
// ensure we can decrypt and read the object
576+
const obj = await helpers.s3.getObject({ Bucket, Key }).promise();
577+
assert.strictEqual(obj.Body.toString(), body);
578+
579+
mpuEncrypted = await helpers.s3.createMultipartUpload(
580+
helpers.putObjParams(Bucket, 'mpuEncrypted', sseConfig, masterKeyArn)).promise();
581+
582+
// make key unavailable
583+
void await helpers.destroyKmsKey(masterKeyArn, log);
584+
});
585+
586+
after(async () => {
587+
void await helpers.cleanup(Bucket);
588+
if (masterKeyArn) {
589+
try {
590+
void await helpers.destroyKmsKey(masterKeyArn, log);
591+
} catch (e) { void e; }
592+
[masterKeyArn, masterKeyId] = [null, null];
593+
}
594+
});
595+
596+
const testCases = [
597+
{
598+
action: 'putObject', kmsAction: 'Encrypt',
599+
fct: async ({ masterKeyArn }) =>
600+
helpers.putEncryptedObject(Bucket, 'fail', sseConfig, masterKeyArn, body),
601+
},
602+
{
603+
action: 'getObject', kmsAction: 'Decrypt',
604+
fct: async () => helpers.s3.getObject({ Bucket, Key }).promise(),
605+
},
606+
{
607+
action: 'copyObject', detail: ' when getting from source', kmsAction: 'Decrypt',
608+
fct: async () =>
609+
helpers.s3.copyObject({ Bucket, Key: 'copy', CopySource: `${Bucket}/${Key}` }).promise(),
610+
},
611+
{
612+
action: 'copyObject', detail: ' when putting to destination', kmsAction: 'Encrypt',
613+
fct: async ({ masterKeyArn }) => helpers.s3.copyObject({
614+
Bucket,
615+
Key: 'copyencrypted',
616+
CopySource: `${Bucket}/plaintext`,
617+
ServerSideEncryption: 'aws:kms',
618+
SSEKMSKeyId: masterKeyArn,
619+
}).promise(),
620+
},
621+
{
622+
action: 'createMPU', kmsAction: 'Encrypt',
623+
fct: async ({ masterKeyArn }) => helpers.s3.createMultipartUpload(
624+
helpers.putObjParams(Bucket, 'mpuKeyEncryptedFail', sseConfig, masterKeyArn)).promise(),
625+
},
626+
{
627+
action: 'mpu uploadPartCopy', detail: ' when getting from source', kmsAction: 'Decrypt',
628+
fct: async ({ mpuPlaintext }) => helpers.s3.uploadPartCopy({
629+
UploadId: mpuPlaintext.UploadId,
630+
Bucket,
631+
Key: 'mpuPlaintext',
632+
PartNumber: 1,
633+
CopySource: `${Bucket}/${Key}`,
634+
}).promise(),
635+
},
636+
{
637+
action: 'mpu uploadPart', detail: ' when putting to destination', kmsAction: 'Encrypt',
638+
fct: async ({ mpuEncrypted }) => helpers.s3.uploadPart({
639+
UploadId: mpuEncrypted.UploadId,
640+
Bucket,
641+
Key: 'mpuEncrypted',
642+
PartNumber: 1,
643+
Body: body,
644+
}).promise(),
645+
},
646+
{
647+
action: 'mpu uploadPartCopy', detail: ' when putting to destination', kmsAction: 'Encrypt',
648+
fct: async ({ mpuEncrypted }) => helpers.s3.uploadPartCopy({
649+
UploadId: mpuEncrypted.UploadId,
650+
Bucket,
651+
Key: 'mpuEncrypted',
652+
PartNumber: 1,
653+
CopySource: `${Bucket}/plaintext`,
654+
}).promise(),
655+
},
656+
];
657+
658+
testCases.forEach(({ action, kmsAction, fct, detail }) => {
659+
it(`${action} should fail with kms error${detail || ''}`, async () => {
660+
await assert.rejects(
661+
fct({ masterKeyArn, mpuEncrypted, mpuPlaintext }),
662+
assertKmsError(kmsAction, masterKeyId),
663+
);
664+
});
665+
});
666+
});

tests/functional/sse-kms-migration/helpers.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,8 @@ async function createKmsKey(log) {
9393
});
9494
}
9595

96+
const destroyKmsKey = promisify(kms.destroyBucketKey);
97+
9698
async function cleanup(Bucket) {
9799
await bucketUtil.empty(Bucket);
98100
await s3.deleteBucket({ Bucket }).promise();
@@ -111,5 +113,6 @@ module.exports = {
111113
putEncryptedObject,
112114
getObjectMDSSE,
113115
createKmsKey,
116+
destroyKmsKey,
114117
cleanup,
115118
};

yarn.lock

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -229,10 +229,10 @@
229229
resolved "https://registry.yarnpkg.com/@azure/msal-common/-/msal-common-15.3.0.tgz#cc75760f7929588b261b970a1dd1d292d0efdbc8"
230230
integrity sha512-lh+eZfibGwtQxFnx+mj6cYWn0pwA8tDnn8CBs9P21nC7Uw5YWRwfXaXdVQSMENZ5ojRqR+NzRaucEo4qUvs3pA==
231231

232-
"@azure/msal-common@15.6.0":
233-
version "15.6.0"
234-
resolved "https://registry.yarnpkg.com/@azure/msal-common/-/msal-common-15.6.0.tgz#0764d6446eeff3970221995e25f265fdb218da66"
235-
integrity sha512-EotmBz42apYGjqiIV9rDUdptaMptpTn4TdGf3JfjLvFvinSe9BJ6ywU92K9ky+t/b0ghbeTSe9RfqlgLh8f2jA==
232+
"@azure/msal-common@15.7.1":
233+
version "15.7.1"
234+
resolved "https://registry.yarnpkg.com/@azure/msal-common/-/msal-common-15.7.1.tgz#6f8df5ace9c94d570b5eec1abc6ebdd4e12831f1"
235+
integrity sha512-a0eowoYfRfKZEjbiCoA5bPT3IlWRAdGSvi63OU23Hv+X6EI8gbvXCoeqokUceFMoT9NfRUWTJSx5FiuzruqT8g==
236236

237237
"@azure/msal-node@^3.2.3":
238238
version "3.4.0"
@@ -244,11 +244,11 @@
244244
uuid "^8.3.0"
245245

246246
"@azure/msal-node@^3.5.0":
247-
version "3.5.3"
248-
resolved "https://registry.yarnpkg.com/@azure/msal-node/-/msal-node-3.5.3.tgz#02f7a2344a2c2994354a0cec125b9ef9a8e7109b"
249-
integrity sha512-c5mifzHX5mwm5JqMIlURUyp6LEEdKF1a8lmcNRLBo0lD7zpSYPHupa4jHyhJyg9ccLwszLguZJdk2h3ngnXwNw==
247+
version "3.6.1"
248+
resolved "https://registry.yarnpkg.com/@azure/msal-node/-/msal-node-3.6.1.tgz#1f288c3de15bdd734cc41a39bbd50f5a83fd64a1"
249+
integrity sha512-ctcVz4xS+st5KxOlQqgpvA+uDFAa59CvkmumnuhlD2XmNczloKBdCiMQG7/TigSlaeHe01qoOlDjz3TyUAmKUg==
250250
dependencies:
251-
"@azure/msal-common" "15.6.0"
251+
"@azure/msal-common" "15.7.1"
252252
jsonwebtoken "^9.0.0"
253253
uuid "^8.3.0"
254254

@@ -1296,9 +1296,9 @@ arraybuffer.prototype.slice@^1.0.4:
12961296
get-intrinsic "^1.2.6"
12971297
is-array-buffer "^3.0.4"
12981298

1299-
"arsenal@git+https://github.com/scality/Arsenal#8.2.23":
1300-
version "8.2.23"
1301-
resolved "git+https://github.com/scality/Arsenal#77d5781ba013708e44251c4e0142428c5f05a643"
1299+
"arsenal@git+https://github.com/scality/Arsenal#8.2.24":
1300+
version "8.2.24"
1301+
resolved "git+https://github.com/scality/Arsenal#ebf56826f4201d3bc88b5e5c7e9653c788184c29"
13021302
dependencies:
13031303
"@azure/identity" "^4.10.1"
13041304
"@azure/storage-blob" "^12.27.0"

0 commit comments

Comments
 (0)