Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ARSN-446 Cherry-pick change for AWS KMS Backend in Vault #2271

Merged
merged 4 commits into from
Nov 12, 2024
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
41 changes: 41 additions & 0 deletions lib/auth/Vault.ts
Original file line number Diff line number Diff line change
Expand Up @@ -384,4 +384,45 @@ export default class Vault {
return callback(null, respBody);
});
}

/**
* Calls Vault to retrieve the default encryption key id of the account, or creates it if it doesn't exist.
*
* @param {string} canonicalID - The canonical id of the account for which
* the encryption key id is being retrieved or created.
* @param {Logger} log - logger
* @param {(err: Error | null, data?: {
* canonicalId: string,
* encryptionKeyId: string,
* action: 'retrieved' | 'created'
* }) => void}
* - canonicalId: The canonical id of the account.
* - encryptionKeyId: The retrieved or newly created encryption key id.
* - action: Describes if the key was 'retrieved' or 'created'.
*
* @returns {void}
*/
getOrCreateEncryptionKeyId(
canonicalID: string,
log: Logger,
callback: (err: Error | null, data?: {
canonicalId: string,
encryptionKeyId: string,
action: 'retrieved' | 'created'
}) => void
) {
log.trace('sending request context params to vault to get or create encryption key id');
this.client.getOrCreateEncryptionKeyId(canonicalID, {
// @ts-ignore
reqUid: log.getSerializedUids(),
}, (err: Error | null, info?: any) => {
if (err) {
log.debug('received error message from auth provider',
{ error: err });
return callback(err);
}
const result = info.message.body;
return callback(null, result);
});
}
}
32 changes: 32 additions & 0 deletions lib/auth/in_memory/Backend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,38 @@ class Backend {
};
return cb(null, vaultReturnObject);
}

/**
* Retrieves or creates an encryption key id for the specified canonical id.
*
* @param {string} canonicalId - The canonical id of the account for which to retrieve or create the encryption key.
* @param {any} _options - An options object, currently unused.
* @param {(err: Error | null, data?: {
* canonicalId: string,
* encryptionKeyId: string,
* action: 'retrieved' | 'created'
* }) => void}
* - canonicalId: The canonical id of the account.
* - encryptionKeyId: The retrieved or newly created encryption key id.
* - action: Describes if the key was 'retrieved' or 'created'.
*
* @returns {void}
*/
getOrCreateEncryptionKeyId(
canonicalId: string,
_options: any,
cb: (err: null, data: { message: { body: { canonicalId: string, encryptionKeyId: string, action: string } } }) => void
): void {
return cb(null, {
message: {
body: {
canonicalId,
encryptionKeyId: 'account-level-master-encryption-key',
action: 'retrieved',
}
}
});
}
}

class S3AuthBackend extends Backend {
Expand Down
23 changes: 21 additions & 2 deletions lib/models/BucketInfo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ export type SSE = {
masterKeyId: string;
configuredMasterKeyId: string;
mandatory: boolean;
isAccountEncryptionEnabled: boolean;
};

export type VersioningConfiguration = {
Expand Down Expand Up @@ -153,10 +154,13 @@ export default class BucketInfo {
configuredMasterKeyId, mandatory } = serverSideEncryption;
assert.strictEqual(typeof cryptoScheme, 'number');
assert.strictEqual(typeof algorithm, 'string');
assert.strictEqual(typeof masterKeyId, 'string');
assert.strictEqual(typeof mandatory, 'boolean');
assert.ok(masterKeyId !== undefined || configuredMasterKeyId !== undefined, 'At least one of masterKeyId or configuredMasterKeyId must be defined');
if (masterKeyId !== undefined) {
assert.strictEqual(typeof masterKeyId, 'string', 'masterKeyId must be a string');
}
if (configuredMasterKeyId !== undefined) {
assert.strictEqual(typeof configuredMasterKeyId, 'string');
assert.strictEqual(typeof configuredMasterKeyId, 'string', 'configuredMasterKeyId must be a string');
}
}
if (versioningConfiguration) {
Expand Down Expand Up @@ -546,6 +550,21 @@ export default class BucketInfo {
}
return this._serverSideEncryption.masterKeyId;
}

/**
* Checks if the default encryption is set at the account level instead of the legacy bucket level.
* This method helps to prevent deletion of the account-level master encryption key when deleting buckets.
*
* @returns {boolean} - Returns true if account-level default encryption is enabled,
* false if it uses the legacy bucket level.
*/
isAccountEncryptionEnabled() {
if (!this._serverSideEncryption) {
return false;
}

return this._serverSideEncryption.isAccountEncryptionEnabled;
}
/**
* Get bucket name.
* @return - bucket name
Expand Down
1 change: 1 addition & 0 deletions lib/network/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@ export const probe = { ProbeServer };
export { default as RoundRobin } from './RoundRobin';
export { default as kmip } from './kmip';
export { default as kmipClient } from './kmip/Client';
export { default as KmsAWSClient } from './kmsAWS/Client';
export * as rpc from './rpc/rpc';
export * as level from './rpc/level-net';
65 changes: 20 additions & 45 deletions lib/network/kmip/Client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
/* eslint new-cap: "off" */

import async from 'async';
import errors from '../../errors';
import TTLVCodec from './codec/ttlv';
import TlsTransport from './transport/tls';
import KMIP from '.';
import * as werelogs from 'werelogs';
import { arsenalErrorKMIP } from '../utils'

const CRYPTOGRAPHIC_OBJECT_TYPE = 'Symmetric Key';
const CRYPTOGRAPHIC_ALGORITHM = 'AES';
Expand Down Expand Up @@ -45,31 +45,6 @@ const searchFilter = {
'Response Message/Batch Item/Response Payload/Data',
};

/**
* Normalize errors according to arsenal definitions
* @param err - an Error instance or a message string
* @returns - arsenal error
*/
function _arsenalError(err: string | Error) {
const messagePrefix = 'KMIP:';
if (typeof err === 'string') {
return errors.InternalError
.customizeDescription(`${messagePrefix} ${err}`);
} else if (
err instanceof Error ||
// INFO: The second part is here only for Jest, to remove when we'll be
// fully migrated to TS
// @ts-expect-error
(err && typeof err.message === 'string')
) {
return errors.InternalError
.customizeDescription(`${messagePrefix} ${err.message}`);
}
return errors.InternalError
.customizeDescription(`${messagePrefix} Unspecified error`);
}


/**
* Negotiate with the server the use of a recent version of the protocol and
* update the low level driver with this new knowledge.
Expand All @@ -93,7 +68,7 @@ function _negotiateProtocolVersion(client: any, logger: werelogs.Logger, cb: any
]),
], (err, response) => {
if (err) {
const error = _arsenalError(err);
const error = arsenalErrorKMIP(err);
logger.error('KMIP::negotiateProtocolVersion',
{ error,
vendorIdentification: client.vendorIdentification });
Expand All @@ -105,7 +80,7 @@ function _negotiateProtocolVersion(client: any, logger: werelogs.Logger, cb: any
response.lookup(searchFilter.protocolVersionMinor);
if (majorVersions.length === 0 ||
majorVersions.length !== minorVersions.length) {
const error = _arsenalError('No suitable protocol version');
const error = arsenalErrorKMIP('No suitable protocol version');
logger.error('KMIP::negotiateProtocolVersion',
{ error,
vendorIdentification: client.vendorIdentification });
Expand All @@ -128,7 +103,7 @@ function _mapExtensions(client: any, logger: werelogs.Logger, cb: any) {
KMIP.Enumeration('Query Function', 'Query Extension Map'),
], (err, response) => {
if (err) {
const error = _arsenalError(err);
const error = arsenalErrorKMIP(err);
logger.error('KMIP::mapExtensions',
{ error,
vendorIdentification: client.vendorIdentification });
Expand All @@ -137,7 +112,7 @@ function _mapExtensions(client: any, logger: werelogs.Logger, cb: any) {
const extensionNames = response.lookup(searchFilter.extensionName);
const extensionTags = response.lookup(searchFilter.extensionTag);
if (extensionNames.length !== extensionTags.length) {
const error = _arsenalError('Inconsistent extension list');
const error = arsenalErrorKMIP('Inconsistent extension list');
logger.error('KMIP::mapExtensions',
{ error,
vendorIdentification: client.vendorIdentification });
Expand All @@ -161,7 +136,7 @@ function _queryServerInformation(client: any, logger: werelogs.Logger, cb: any)
KMIP.Enumeration('Query Function', 'Query Server Information'),
], (err, response) => {
if (err) {
const error = _arsenalError(err);
const error = arsenalErrorKMIP(err);
logger.warn('KMIP::queryServerInformation',
{ error });
/* no error returned, caller can keep going */
Expand Down Expand Up @@ -196,7 +171,7 @@ function _queryOperationsAndObjects(client: any, logger: werelogs.Logger, cb: an
KMIP.Enumeration('Query Function', 'Query Objects'),
], (err, response) => {
if (err) {
const error = _arsenalError(err);
const error = arsenalErrorKMIP(err);
logger.error('KMIP::queryOperationsAndObjects',
{ error,
vendorIdentification: client.vendorIdentification });
Expand Down Expand Up @@ -336,7 +311,7 @@ export default class Client {
KMIP.TextString('Unique Identifier', keyIdentifier),
], (err, response) => {
if (err) {
const error = _arsenalError(err);
const error = arsenalErrorKMIP(err);
logger.error('KMIP::_activateBucketKey',
{ error,
serverInformation: this.serverInformation });
Expand All @@ -345,7 +320,7 @@ export default class Client {
const uniqueIdentifier =
response.lookup(searchFilter.uniqueIdentifier)[0];
if (uniqueIdentifier !== keyIdentifier) {
const error = _arsenalError(
const error = arsenalErrorKMIP(
'Server did not return the expected identifier');
logger.error('KMIP::cipherDataKey',
{ error, uniqueIdentifier });
Expand Down Expand Up @@ -389,7 +364,7 @@ export default class Client {
KMIP.Structure('Template-Attribute', attributes),
], (err, response) => {
if (err) {
const error = _arsenalError(err);
const error = arsenalErrorKMIP(err);
logger.error('KMIP::createBucketKey',
{ error,
serverInformation: this.serverInformation });
Expand All @@ -400,7 +375,7 @@ export default class Client {
const uniqueIdentifier =
response.lookup(searchFilter.uniqueIdentifier)[0];
if (createdObjectType !== CRYPTOGRAPHIC_OBJECT_TYPE) {
const error = _arsenalError(
const error = arsenalErrorKMIP(
'Server created an object of wrong type');
logger.error('KMIP::createBucketKey',
{ error, createdObjectType });
Expand Down Expand Up @@ -433,7 +408,7 @@ export default class Client {
]),
], (err, response) => {
if (err) {
const error = _arsenalError(err);
const error = arsenalErrorKMIP(err);
logger.error('KMIP::_revokeBucketKey',
{ error,
serverInformation: this.serverInformation });
Expand All @@ -442,7 +417,7 @@ export default class Client {
const uniqueIdentifier =
response.lookup(searchFilter.uniqueIdentifier)[0];
if (uniqueIdentifier !== bucketKeyId) {
const error = _arsenalError(
const error = arsenalErrorKMIP(
'Server did not return the expected identifier');
logger.error('KMIP::_revokeBucketKey',
{ error, uniqueIdentifier });
Expand All @@ -461,7 +436,7 @@ export default class Client {
destroyBucketKey(bucketKeyId: string, logger: werelogs.Logger, cb: any) {
return this._revokeBucketKey(bucketKeyId, logger, err => {
if (err) {
const error = _arsenalError(err);
const error = arsenalErrorKMIP(err);
logger.error('KMIP::destroyBucketKey: revocation failed',
{ error,
serverInformation: this.serverInformation });
Expand All @@ -471,7 +446,7 @@ export default class Client {
KMIP.TextString('Unique Identifier', bucketKeyId),
], (err, response) => {
if (err) {
const error = _arsenalError(err);
const error = arsenalErrorKMIP(err);
logger.error('KMIP::destroyBucketKey',
{ error,
serverInformation: this.serverInformation });
Expand All @@ -480,7 +455,7 @@ export default class Client {
const uniqueIdentifier =
response.lookup(searchFilter.uniqueIdentifier)[0];
if (uniqueIdentifier !== bucketKeyId) {
const error = _arsenalError(
const error = arsenalErrorKMIP(
'Server did not return the expected identifier');
logger.error('KMIP::destroyBucketKey',
{ error, uniqueIdentifier });
Expand Down Expand Up @@ -521,7 +496,7 @@ export default class Client {
KMIP.ByteString('IV/Counter/Nonce', CRYPTOGRAPHIC_DEFAULT_IV),
], (err, response) => {
if (err) {
const error = _arsenalError(err);
const error = arsenalErrorKMIP(err);
logger.error('KMIP::cipherDataKey',
{ error,
serverInformation: this.serverInformation });
Expand All @@ -531,7 +506,7 @@ export default class Client {
response.lookup(searchFilter.uniqueIdentifier)[0];
const data = response.lookup(searchFilter.data)[0];
if (uniqueIdentifier !== masterKeyId) {
const error = _arsenalError(
const error = arsenalErrorKMIP(
'Server did not return the expected identifier');
logger.error('KMIP::cipherDataKey',
{ error, uniqueIdentifier });
Expand Down Expand Up @@ -571,7 +546,7 @@ export default class Client {
KMIP.ByteString('IV/Counter/Nonce', CRYPTOGRAPHIC_DEFAULT_IV),
], (err, response) => {
if (err) {
const error = _arsenalError(err);
const error = arsenalErrorKMIP(err);
logger.error('KMIP::decipherDataKey',
{ error,
serverInformation: this.serverInformation });
Expand All @@ -581,7 +556,7 @@ export default class Client {
response.lookup(searchFilter.uniqueIdentifier)[0];
const data = response.lookup(searchFilter.data)[0];
if (uniqueIdentifier !== masterKeyId) {
const error = _arsenalError(
const error = arsenalErrorKMIP(
'Server did not return the right identifier');
logger.error('KMIP::decipherDataKey',
{ error, uniqueIdentifier });
Expand Down
Loading
Loading