Skip to content

Commit f8889a4

Browse files
Merge remote-tracking branch 'origin/improvement/ARSN-479-kms-ip' into w/8.1/improvement/ARSN-479-kms-ip
2 parents 10a5734 + 8b0b191 commit f8889a4

File tree

9 files changed

+211
-20
lines changed

9 files changed

+211
-20
lines changed

lib/network/KMSInterface.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
2+
import type * as werelogs from 'werelogs';
3+
// Logger should probably by RequestLogger instead
4+
5+
export interface KMSInterface {
6+
createBucketKey(
7+
bucketName: string,
8+
logger: werelogs.Logger,
9+
cb: (err?: Error | null, keyId?: string) => void,
10+
): void
11+
12+
destroyBucketKey(
13+
bucketKeyId: string,
14+
logger: werelogs.Logger,
15+
cb: (err?: Error | null) => void,
16+
): void
17+
18+
generateDataKey?(
19+
cryptoScheme: number,
20+
masterKeyId: string,
21+
logger: werelogs.Logger,
22+
cb: (err: Error | null, plainTextDataKey?: Buffer, cipheredDataKey?: Buffer) => void,
23+
): void
24+
25+
cipherDataKey(
26+
cryptoScheme: number,
27+
masterKeyId: string,
28+
plainTextDataKey: Buffer,
29+
logger: werelogs.Logger,
30+
cb: (err: Error | null, cipheredDataKey?: Buffer) => void,
31+
): void
32+
33+
decipherDataKey(
34+
cryptoScheme: number,
35+
masterKeyId: string,
36+
cipheredDataKey: Buffer,
37+
logger: werelogs.Logger,
38+
cb: (err: Error | null, plainTextDataKey?: Buffer) => void,
39+
): void
40+
41+
healthcheck?(
42+
logger: werelogs.Logger,
43+
cb: (err: Error | null) => void
44+
): void
45+
}

lib/network/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ export const probe = { ProbeServer, HealthProbeServer, Utils };
1313
export { default as RoundRobin } from './RoundRobin';
1414
export { default as kmip } from './kmip';
1515
export { default as kmipClient } from './kmip/Client';
16+
export { default as kmipClusterClient } from './kmip/ClusterClient';
1617
export { default as KmsAWSClient } from './kmsAWS/Client';
1718
export * as rpc from './rpc/rpc';
1819
export * as level from './rpc/level-net';

lib/network/kmip/Client.ts

Lines changed: 42 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import TlsTransport from './transport/tls';
77
import KMIP from '.';
88
import * as werelogs from 'werelogs';
99
import { arsenalErrorKMIP } from '../utils'
10+
import { KMSInterface } from '../KMSInterface';
1011

1112
const CRYPTOGRAPHIC_OBJECT_TYPE = 'Symmetric Key';
1213
const CRYPTOGRAPHIC_ALGORITHM = 'AES';
@@ -53,6 +54,7 @@ const searchFilter = {
5354
* @param cb - The callback triggered after the negotiation.
5455
*/
5556
function _negotiateProtocolVersion(client: any, logger: werelogs.Logger, cb: any) {
57+
const startDate = Date.now();
5658
return client.kmip.request(logger, 'Discover Versions', [
5759
KMIP.Structure('Protocol Version', [
5860
KMIP.Integer('Protocol Version Major', 1),
@@ -67,10 +69,14 @@ function _negotiateProtocolVersion(client: any, logger: werelogs.Logger, cb: any
6769
KMIP.Integer('Protocol Version Minor', 2),
6870
]),
6971
], (err, response) => {
72+
const kmipLog = {
73+
host: client.host,
74+
latencyMs: Date.now() - startDate
75+
};
7076
if (err) {
7177
const error = arsenalErrorKMIP(err);
7278
logger.error('KMIP::negotiateProtocolVersion',
73-
{ error,
79+
{ error, kmip: kmipLog,
7480
vendorIdentification: client.vendorIdentification });
7581
return cb(error);
7682
}
@@ -82,7 +88,7 @@ function _negotiateProtocolVersion(client: any, logger: werelogs.Logger, cb: any
8288
majorVersions.length !== minorVersions.length) {
8389
const error = arsenalErrorKMIP('No suitable protocol version');
8490
logger.error('KMIP::negotiateProtocolVersion',
85-
{ error,
91+
{ error, kmip: kmipLog,
8692
vendorIdentification: client.vendorIdentification });
8793
return cb(error);
8894
}
@@ -99,13 +105,18 @@ function _negotiateProtocolVersion(client: any, logger: werelogs.Logger, cb: any
99105
* @param cb - The callback triggered after the extension mapping
100106
*/
101107
function _mapExtensions(client: any, logger: werelogs.Logger, cb: any) {
108+
const startDate = Date.now();
102109
return client.kmip.request(logger, 'Query', [
103110
KMIP.Enumeration('Query Function', 'Query Extension Map'),
104111
], (err, response) => {
112+
const kmipLog = {
113+
host: client.host,
114+
latencyMs: Date.now() - startDate
115+
};
105116
if (err) {
106117
const error = arsenalErrorKMIP(err);
107118
logger.error('KMIP::mapExtensions',
108-
{ error,
119+
{ error, kmip: kmipLog,
109120
vendorIdentification: client.vendorIdentification });
110121
return cb(error);
111122
}
@@ -114,7 +125,7 @@ function _mapExtensions(client: any, logger: werelogs.Logger, cb: any) {
114125
if (extensionNames.length !== extensionTags.length) {
115126
const error = arsenalErrorKMIP('Inconsistent extension list');
116127
logger.error('KMIP::mapExtensions',
117-
{ error,
128+
{ error, kmip: kmipLog,
118129
vendorIdentification: client.vendorIdentification });
119130
return cb(error);
120131
}
@@ -132,25 +143,31 @@ function _mapExtensions(client: any, logger: werelogs.Logger, cb: any) {
132143
* @param cb - The callback triggered after the information discovery
133144
*/
134145
function _queryServerInformation(client: any, logger: werelogs.Logger, cb: any) {
146+
const startDate = Date.now();
135147
client.kmip.request(logger, 'Query', [
136148
KMIP.Enumeration('Query Function', 'Query Server Information'),
137149
], (err, response) => {
150+
const kmipLog = {
151+
host: client.host,
152+
latencyMs: Date.now() - startDate
153+
};
138154
if (err) {
139155
const error = arsenalErrorKMIP(err);
140156
logger.warn('KMIP::queryServerInformation',
141-
{ error });
157+
{ error, kmip: kmipLog });
142158
/* no error returned, caller can keep going */
143159
return cb();
144160
}
145161
client._setVendorIdentification(
146162
response.lookup(searchFilter.vendorIdentification)[0]);
147163
client._setServerInformation(
148-
JSON.stringify(response.lookup(searchFilter.serverInformation)[0]));
164+
response.lookup(searchFilter.serverInformation)[0]);
149165

150166
logger.info('KMIP Server identified',
151167
{ vendorIdentification: client.vendorIdentification,
152168
serverInformation: client.serverInformation,
153-
negotiatedProtocolVersion: client.kmip.protocolVersion });
169+
negotiatedProtocolVersion: client.kmip.protocolVersion,
170+
kmip: kmipLog });
154171
return cb();
155172
});
156173
}
@@ -166,14 +183,19 @@ function _queryServerInformation(client: any, logger: werelogs.Logger, cb: any)
166183
* @param cb - The callback triggered after the information discovery
167184
*/
168185
function _queryOperationsAndObjects(client: any, logger: werelogs.Logger, cb: any) {
186+
const startDate = Date.now();
169187
return client.kmip.request(logger, 'Query', [
170188
KMIP.Enumeration('Query Function', 'Query Operations'),
171189
KMIP.Enumeration('Query Function', 'Query Objects'),
172190
], (err, response) => {
191+
const kmipLog = {
192+
host: client.host,
193+
latencyMs: Date.now() - startDate
194+
};
173195
if (err) {
174196
const error = arsenalErrorKMIP(err);
175197
logger.error('KMIP::queryOperationsAndObjects',
176-
{ error,
198+
{ error, kmip: kmipLog,
177199
vendorIdentification: client.vendorIdentification });
178200
return cb(error);
179201
}
@@ -204,21 +226,23 @@ function _queryOperationsAndObjects(client: any, logger: werelogs.Logger, cb: an
204226
supportsEncrypt, supportsDecrypt,
205227
supportsActivate, supportsRevoke,
206228
supportsCreate, supportsDestroy,
207-
supportsQuery, supportsSymmetricKeys });
229+
supportsQuery, supportsSymmetricKeys,
230+
kmip: kmipLog });
208231
} else {
209232
logger.info('KMIP Server provides the necessary feature set',
210-
{ vendorIdentification: client.vendorIdentification });
233+
{ vendorIdentification: client.vendorIdentification,
234+
kmip: kmipLog });
211235
}
212236
return cb();
213237
});
214238
}
215239

216-
217-
export default class Client {
240+
export default class Client implements KMSInterface {
218241
options: any;
219242
vendorIdentification: string;
220243
serverInformation: any[];
221244
kmip: KMIP;
245+
host: string;
222246

223247
/**
224248
* Construct a high level KMIP driver suitable for cloudserver
@@ -255,6 +279,7 @@ export default class Client {
255279
CodecClass: any,
256280
TransportClass: any,
257281
) {
282+
this.host = options.kmip.transport.tls.host;
258283
this.options = options.kmip.client || {};
259284
this.vendorIdentification = '';
260285
this.serverInformation = [];
@@ -567,20 +592,23 @@ export default class Client {
567592
}
568593

569594
healthcheck(logger, cb) {
595+
const kmipLog = { host: this.host };
570596
// the bucket does not have to exist, just passing a common bucket name here
571597
this.createBucketKey('kmip-healthcheck-test-bucket', logger, (err, bucketKeyId) => {
572598
if (err) {
573599
logger.error('KMIP::healthcheck: failure to create a test bucket key', {
574-
error: err,
600+
error: err, kmip: kmipLog,
575601
});
576602
return cb(err);
577603
}
578-
logger.debug('KMIP::healthcheck: success creating a test bucket key');
604+
logger.debug('KMIP::healthcheck: success creating a test bucket key',
605+
{ kmip: kmipLog });
579606
this.destroyBucketKey(bucketKeyId, logger, err => {
580607
if (err) {
581608
logger.error('KMIP::healthcheck: failure to remove the test bucket key', {
582609
bucketKeyId,
583610
error: err,
611+
kmip: kmipLog,
584612
});
585613
}
586614
});

lib/network/kmip/ClusterClient.ts

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
'use strict'; // eslint-disable-line
2+
/* eslint new-cap: "off" */
3+
4+
import TTLVCodec from './codec/ttlv';
5+
import TlsTransport from './transport/tls';
6+
import KMIPClient from './Client';
7+
import { KMSInterface } from '../KMSInterface';
8+
import type { Logger } from 'werelogs';
9+
import async from 'async';
10+
11+
export default class ClusterClient implements KMSInterface {
12+
private readonly clients: KMIPClient[];
13+
private roundRobinIndex = 0;
14+
15+
/**
16+
* Construct a high level cluster of KMIP drivers suitable for cloudserver
17+
* @param options - Instance options
18+
* @param options.kmip - Low level driver options
19+
* @param options.kmip.client - This high level driver options
20+
* @param options.kmip.client.compoundCreateActivate -
21+
* Depends on the server's ability. False offers the best
22+
* compatibility. True does not offer a significant
23+
* performance gain, but can be useful in case of unreliable
24+
* time synchronization between the client and the server.
25+
* @param options.kmip.client.bucketNameAttributeName -
26+
* Depends on the server's ability. Not specifying this
27+
* offers the best compatibility and disable the attachement
28+
* of the bucket name as a key attribute.
29+
* @param options.kmip.codec - KMIP Codec options
30+
* @param options.kmip.transport - KMIP Transport options
31+
* @param CodecClass - diversion for the Codec class,
32+
* defaults to TTLVCodec
33+
* @param TransportClass - diversion for the Transport class,
34+
* defaults to TlsTransport
35+
*/
36+
constructor(
37+
options: {
38+
kmip: {
39+
codec: any;
40+
transport: any[];
41+
client: {
42+
compoundCreateActivate: any;
43+
bucketNameAttributeName: any;
44+
};
45+
}
46+
},
47+
CodecClass: any,
48+
TransportClass: any,
49+
) {
50+
const { codec, client } = options.kmip;
51+
this.clients = options.kmip.transport.map(transport => new KMIPClient(
52+
{ kmip: { codec, transport, client } },
53+
CodecClass || TTLVCodec,
54+
TransportClass || TlsTransport,
55+
));
56+
}
57+
58+
next() {
59+
if (this.roundRobinIndex >= this.clients.length) {
60+
this.roundRobinIndex = 0;
61+
}
62+
return this.clients[this.roundRobinIndex++];
63+
}
64+
65+
66+
createBucketKey(...args: Parameters<KMSInterface['createBucketKey']>) {
67+
const client = this.next();
68+
client.createBucketKey.apply(client, args);
69+
}
70+
71+
destroyBucketKey(...args: Parameters<KMSInterface['destroyBucketKey']>) {
72+
const client = this.next();
73+
client.destroyBucketKey.apply(client, args);
74+
}
75+
76+
cipherDataKey(...args: Parameters<KMSInterface['cipherDataKey']>) {
77+
const client = this.next();
78+
client.cipherDataKey.apply(client, args);
79+
}
80+
81+
decipherDataKey(...args: Parameters<KMSInterface['decipherDataKey']>) {
82+
const client = this.next();
83+
client.decipherDataKey.apply(client, args);
84+
}
85+
86+
clusterHealthcheck(logger: Logger, cb: (err: Error | null) => void) {
87+
async.parallel<any, Error>(
88+
this.clients.map(c => (next) => c.healthcheck(logger, next)),
89+
(err, results) => {
90+
cb(err ?? null);
91+
}
92+
)
93+
}
94+
95+
healthcheck(...args: Parameters<Required<KMSInterface>['healthcheck']>) {
96+
// for now check health of every member
97+
this.clusterHealthcheck.apply(this, args);
98+
}
99+
}

lib/network/kmip/codec/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -354,7 +354,7 @@ class MyCodec {
354354

355355
## Encoding specification links
356356

357-
### TTLV Encoding Baseline Profile
357+
### TTLV (Tag, Type, Length, Value) Encoding Baseline Profile
358358

359359
[TTLV Encoding Specification](http://docs.oasis-open.org/kmip/spec/v1.4/os/kmip-spec-v1.4-os.html#_Toc490660911)
360360

lib/network/kmip/codec/ttlv.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ function _throwError(logger: werelogs.Logger, msg: string, data?: LogDictionary)
2525
throw Error(msg);
2626
}
2727

28+
/** TTLV = Tag, Type, Length, Value */
2829
export default function TTLVCodec() {
2930
if (!new.target) {
3031
// @ts-ignore

0 commit comments

Comments
 (0)