-
Notifications
You must be signed in to change notification settings - Fork 0
/
CertificateManager.js
564 lines (527 loc) · 27 KB
/
CertificateManager.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
//Background: This tool can be used to call IBM Cloud Certificate Manager APIs to manager your IBM Cloud Certificates
//i.e., import bulk of certificates to Certificate Manager, delete certificates, update certificates, etc
//prerequisites
//(1) setup nodejs environment, i.e. "brew install node"; then npm install shelljs, fs, https, commander
//(2) store your certificates into your local machine under some specfic directory
//(3) get the "api_key" of your account, you can follow this doc to get it: https://console.bluemix.net/docs/iam/apikey_iamtoken.html#iamtoken_from_apikey
//(4) fetch service instanceCRN raw string, which you can get from your cert manager service instance dashboard: Settings -> Instance info -> Copy the CRN.
//(5) fetch region name that your cert manager service instance resides, i.e. us-south, eu-gb.
//(6) Get Certificate CRN from certificate manager instance dashboard
//Note
// (1) The certificates you plan to import must be in PEM format, so it's suggested you leverage openssl command like "openssl x509 -in <path_of_cert_with_NON_PEM_format> -out <path_of_cert_with_PEM_format> -outform PEM" to do the conversion before you run this script to import them
// If you have certificates that have formats (i.e., .crt, .cer, .der, etc) other than PEM, this tool can find them and list all the "openssl" commands that you can use to convert them to PEM before importing the certificates.
// Once you convert all certs to PEM format, you can re-run this script again, that can help you import all those PEM certs to certificate manager instance
// (2)Please Notice that if you re-run this script to import the same local certificates , the existing PEM certs will be imported to cert manager again even if they already exist in cert manager, since cert manager supports several copies of certs co-existing, and cert_name is not a unique key
// if you want to update some certificate, you should use "reimport" function this script provided
// Usage:
//(1) To import a specific certificate:
// node CertificateManager.js -a import -l <cert_file_absolute_path> -k <api_key> -r <region_name_hosting_your_certmgr_instance> -i <CRN_of_your_certmgr_instance>
//(2) To import bunch of certificates under a specific directory:
// node CertificateManager.js -a import -l <certs_directory_absolute_path> -k <api_key> -r <region_name_hosting_your_certmgr_instance> -i <CRN_of_your_certmgr_instance>
//(3) To reimport (update) existing specific certificate:
// node CertificateManager.js -a reimport -l <cert_file_absolute_path> -k <api_key> -r <region_name_hosting_your_certmgr_instance> -i <CRN_of_your_certmgr_instance> -c <certificate_CRN>
//(4) To export (download) a specific certificate to local directory: (Note: must have certificate CRN as the parameter)
// node CertificateManager.js -a export -l <local_directory_absolute_path> -k <api_key> -r <region_name_hosting_your_certmgr_instance> -i <CRN_of_your_certmgr_instance> -c <certificate_CRN>
//(5) To export (download) all certificates to local directory: (Note: if no certificate CRN assigned as parameter, means all certs will be exported)
// node CertificateManager.js -a export -l <local_directory_absolute_path> -k <api_key> -r <region_name_hosting_your_certmgr_instance> -i <CRN_of_your_certmgr_instance>
//(6) To delete a specific certificate:
// node CertificateManager.js -a delete -k <api_key> -r <region_name_hosting_your_certmgr_instance> -i <CRN_of_your_certmgr_instance> -c <certificate_CRN>
//(7) To delete all certificates managed by the certificate manager instance
// node CertificateManager.js -a purgeAll -k <api_key> -r <region_name_hosting_your_certmgr_instance> -i <CRN_of_your_certmgr_instance>
//(8) To list all certificates managed by the certificate manager instance
// node CertificateManager.js -a list -k <api_key> -r <region_name_hosting_your_certmgr_instance> -i <CRN_of_your_certmgr_instance>
//(9) Get metada of certificate repository:
// node CertificateManager.js -a meta -k <api_key> -r <region_name_hosting_your_certmgr_instance> -i <CRN_of_your_certmgr_instance>
//library definition
var shell = require('shelljs');
var fs = require('fs');
const https = require('https');
var program = require('commander');
//Once bluemix.net migrated to cloud.ibm.com, need change it
const CM_domain= 'certificate-manager.bluemix.net';
//option parser
program
.option('-a, --action [value]', 'Madatory option. must be one of import, reimport, export, delete, purgeAll, list, meta. The action name for managing certificates, such as importing certs, reimporting certs, list all certs, exporting certs to local directory, deleting specific certs, deleting all certificates, get metadata of certs, etc.', /import|reimport|list|export|delete|purgeAll|list|meta/)
.option('-l, --loc_cert_dir_file [value]', 'Madatory option only when action is import, reimport, export. local directory under which need importing certs from, or need exporting certs to; OR: local cert file that needs to be imported or reimported')
.option('-c --certCRN [value]', 'Madatory option only when action is reimport, delete, export. The id or CRN of the specific certificate that you want to reimport, delete or export')
.option('-k, --api_key [value]', 'Madatory option. ibm cloud api key, which you can get from this doc: https://console.bluemix.net/docs/iam/apikey_iamtoken.html#iamtoken_from_apikey')
.option('-r, --region [value]', 'Madatory option. Region hosting your cert manager service instance, i.e. eu-gb, us-south', /us-south|eu-gb|eu-de|au-syd|us-east/)
.option('-i, --instance_CRN [value]', 'Madatory option. CRN of your cert manager service instance, where you can get from your cert manager service instance dashboard: Settings -> Instance info -> Copy the CRN ')
program.parse(process.argv);
//check the action name of certs management. the action name must be one of import, reimport, list, export, delete, meta. Do some initial validation work on the combination of command options
if (typeof program.action === 'undefined') {
console.error('no action name given!');
program.outputHelp();
process.exit(1);
}
if (program.action !== 'import' && program.action !== 'reimport' && program.action !== 'export' && program.action !== 'delete' && program.action !== 'purgeAll' && program.action !== 'list' && program.action !== 'meta') {
console.error('action name must be one of import, reimport, list, export, delete, purgeAll, meta');
program.outputHelp();
process.exit(1);
} else {
//when import, reimport or export certificates, must provide an existing directory or file name
if (program.action === 'import' || program.action === 'reimport' || program.action === 'export') {
if (typeof program.loc_cert_dir_file === 'undefined' || !fs.existsSync(program.loc_cert_dir_file)) {
console.error('for certs import, reimport or export, you must provide an existing directory or an existing cert file');
program.outputHelp();
process.exit(1);
}
}
//when reimport, delete or export a certificate, must provide certificate CRN
if (program.action === 'reimport' || program.action === 'delete') {
if (typeof program.certCRN === 'undefined') {
console.error('for cert reimport, delete, you must provide the certificate CRN');
program.outputHelp();
process.exit(1);
}
}
// when reimport a certificate, the input parameter must be a certificate file
if (program.action === 'reimport') {
if(!fs.statSync(program.loc_cert_dir_file).isFile()){
console.error('for certs reimport, you must provide an existing cert file');
program.outputHelp();
process.exit(1);
}
}
// when export a certificate, the input parameter must be a directory
if (program.action === 'export') {
if(!fs.statSync(program.loc_cert_dir_file).isDirectory()){
console.error('for certs export, you must provide an existing directory');
program.outputHelp();
process.exit(1);
}
}
}
//check if ibm cloud api key is given
if (typeof program.api_key === 'undefined') {
console.error('no ibm cloud api key given!');
program.outputHelp();
process.exit(1);
}
//check if region name is given and if region name is one of us-south, eu-gb, eu-de, au-syd, us-east
if (typeof program.region === 'undefined') {
console.error('no region name given!');
program.outputHelp();
process.exit(1);
}
if (program.region !== 'us-south' && program.region !== 'eu-gb' && program.region !== 'eu-de' && program.region !== 'au-syd' && program.region !== 'us-east') {
console.error('region name must be one of us-south, eu-gb, eu-de, au-syd, us-east');
program.outputHelp();
process.exit(1);
}
//check if service instanc CRN exists or not
if (typeof program.instance_CRN === 'undefined') {
console.error('no instance_CRN given!');
program.outputHelp();
process.exit(1);
}
//variable definition
var region = program.region;
var instanceCRN = program.instance_CRN;
var apiKey = program.api_key;
var local_cert_dir_file = program.loc_cert_dir_file;
var certCRN = program.certCRN;
//function for importing (or reimporting) single certificate: Currently the cert must be in PEM format
function importSingleCertificate(options, local_cert_file, action_type) {
var certNamePath = local_cert_file.split(/\.pem/)[0];
const certName = certNamePath.substring(certNamePath.lastIndexOf("/") + 1, certNamePath.length);
const cert_content = fs.readFileSync(local_cert_file,'utf8');
var body;
//check if private key exist, if it exists, include key as payload as well; if it does not exist, will ignore
if (fs.existsSync(certNamePath + ".key")) {
const private_key_content = fs.readFileSync(certNamePath + ".key",'utf8');
//if action is import, need to include "name: certName" in the body; otherwise for action as reimport, no need to include "name: certName"
if (action_type === 'import') {
body = {
name: certName,
data: {
content: cert_content,
priv_key: private_key_content
}
};
} else {
body = {
content: cert_content,
priv_key: private_key_content
};
}
} else {
if (action_type === 'import') {
body = {
name: certName,
data: {
content: cert_content
}
};
} else {
body = {
content: cert_content
};
}
}
//import single certificate by calling Certificate Manager API
const req = https.request(options, (res) => {
if(res.statusCode === 200){
console.log(`${certName} imported successfully`);
} else {
console.log(`${certName} failed to be imported with status: ${res.statusCode}`);
res.setEncoding('utf8');
res.on('data', (chunk) => {
console.log(`BODY: ${chunk}`);
});
}
});
req.on('error', (e) => {
console.error(`problem with request: ${e.message}`);
});
// write data to request body
req.end(JSON.stringify(body));
}
//function for importing lots of certificates from specific directory, or importing a single certificate by given certificate file
function importCertificates(options, local_cert_dir_file, action_type){
if(fs.statSync(local_cert_dir_file).isDirectory()) {
//Check if there are certs are not in PEM format, i.e. crt, cer, der; if there are, notify the user to convert them to PEM before importing the certs from the directory
shell.ls('-d', local_cert_dir_file).forEach(function (folder) {
shell.cd(folder);
var non_pem_flag = 0;
var files = shell.find('.').filter(function(file) { return file.match(/\.crt$|\.cer$|\.der$/); });
if (files.length !== 0) {
files.forEach(function (file) {
var certAbsolPath = folder + '/' + file;
var certNamePath = file.split(/\.crt|\.cer|\.der/)[0];
//const certName = certNamePath.substring(certNamePath.lastIndexOf("/") + 1, certNamePath.length);
if (!(fs.existsSync(certNamePath + ".pem"))) {
non_pem_flag = 1;
var pemcertAbsolPath = folder + '/' + certNamePath + '.pem'
console.log(`openssl x509 -in ${certAbsolPath} -out ${pemcertAbsolPath} -outform PEM`);
}
})
}
if (non_pem_flag === 1) {
console.log('NON-PEM certs exist, need to run above openssl commands to convert them to PEM format before importing');
process.exit(1);
}
});
//Check if there are certs are in PEM format, if no, will exit
shell.ls('-d', local_cert_dir_file).forEach(function (folder) {
shell.cd(folder);
var files = shell.find('.').filter(function(file) { return file.match(/\.pem$/); });
if (files.length === 0) {
console.log(`NO pem certs detected under directory ${local_cert_dir_file} !!`);
process.exit(1);
}
});
//import PEM certificates by calling function importSingleCertificate in a loop
shell.ls('-d', local_cert_dir_file).forEach(function (folder) {
shell.cd(folder);
var files = shell.find('.').filter(function(file) { return file.match(/\.pem$/); });
if (files.length !== 0) {
files.forEach(function (file) {
importSingleCertificate(options, file, action_type);
})
}
});
} else {
//if the cert is not a PEM file, then need warn the user to convert the cert to PEM format before reimport it
if(local_cert_dir_file.indexOf('.pem') <= -1) {
var certNamePath = local_cert_dir_file.split(/\.crt|\.cer|\.der/)[0];
var certPemPath = certNamePath + ".pem";
console.log('You need to run following openssl command to convert the cert to PEM format before importing it');
console.log(`openssl x509 -in ${local_cert_dir_file} -out ${certPemPath} -outform PEM`);
process.exit(1);
} // if the cert is in PEM format, just import it
else {
importSingleCertificate(options, local_cert_dir_file, action_type);
}
}
}
//function for reimporting (update) specific certificate. Input parameters: options payload, target certificate's CRN, and the source local certificate that you want to reimport
function reimportCertificate(options, local_cert_file, action_type) {
if(local_cert_file.indexOf('.pem') <= -1) {
var certNamePath = local_cert_file.split(/\.crt|\.cer|\.der/)[0];
var certPemPath = certNamePath + ".pem";
console.log('You need to run following openssl command to convert the cert to PEM format before reimporting it');
console.log(`openssl x509 -in ${local_cert_file} -out ${certPemPath} -outform PEM`);
process.exit(1);
} // if the cert is in PEM format, just import it
else {
importSingleCertificate(options, local_cert_file, action_type);
}
}
//function for deleting specific certificate. Input parameters: options payload
function deleteCertificate(options, certCRN) {
const req = https.request(options, (res) => {
if(res.statusCode === 200){
console.log(`cert (CRN: ${certCRN}) deleted successfully`);
} else {
console.log(`cert (CRN: ${certCRN})failed to be deleted with status: ${res.statusCode}`);
res.setEncoding('utf8');
res.on('data', (chunk) => {
console.log(`BODY: ${chunk}`);
});
}
});
req.on('error', (e) => {
console.error(`problem with request: ${e.message}`);
});
req.end();
}
//function for exporting(Get) specific certificate to local directory. Input parameters: options payload, local directory to store the cert
function exportCertificate(options, local_cert_dir_file) {
var str = '';
var json_arr = {};
shell.ls('-d', local_cert_dir_file).forEach(function (folder) {
shell.cd(folder);
const req = https.request(options, (res) => {
if(res.statusCode === 200){
res.setEncoding('utf8');
res.on('data', function(chunk) {
str += chunk;
});
res.on('end', function() {
json_arr = JSON.parse(str);
var cert_name = json_arr.name;
var cert_content = json_arr.data.content;
var key_content = json_arr.data.priv_key;
fs.writeFile(local_cert_dir_file + '/' + `${cert_name}.pem`, cert_content, 'utf8', function (err) {
if (err) {
return console.log(err);
}
console.log(`cert ${cert_name} exported to ${local_cert_dir_file}`);
});
if ( typeof key_content !== 'undefined' ) {
if(key_content.trim()) {
fs.writeFile(local_cert_dir_file + '/' + `${cert_name}.key`, key_content, 'utf8', function (err) {
if (err) {
return console.log(err);
}
console.log(`key for cert ${cert_name} exported to ${local_cert_dir_file}`);
});
}
}
});
} else {
console.log(`cert(s) failed to be exported to ${local_cert_dir_file} with status: ${res.statusCode}`);
res.setEncoding('utf8');
res.on('data', (chunk) => {
console.log(`BODY: ${chunk}`);
});
}
});
req.on('error', (e) => {
console.error(`problem with request: ${e.message}`)
});
req.end();
});
}
//function for retrieving a list of all certificates and their associated metadata.
function listCertificates(options) {
var str = '';
var json_arr = [];
const req = https.request(options, (res) => {
if(res.statusCode === 200){
res.setEncoding('utf8');
res.on('data', function(chunk) {
str += chunk;
});
res.on('end', function() {
json_arr = JSON.parse(str);
console.log(str);
});
} else {
console.log(`failed to list certificates with status: ${res.statusCode}`);
res.setEncoding('utf8');
res.on('data', (chunk) => {
console.log(`BODY: ${chunk}`);
});
}
});
req.on('error', (e) => {
console.error(`problem with request: ${e.message}`);
});
req.end();
}
//function for retrieving the CRNs for all the certificates, which can be used as a callback when deleting all certificates, or export all certificates to local machine
function getAllCertificatesCRNs(options, callback) {
var str = '';
var json_arr = [];
var certs_CRNs_arr = [];
const req = https.request(options, (res) => {
if(res.statusCode === 200){
res.setEncoding('utf8');
res.on('data', function(chunk) {
str += chunk;
});
res.on('end', function() {
json_arr = JSON.parse(str);
var total_num_certs = json_arr.totalScannedDocs;
if (total_num_certs > 0) {
for (var i = 0; i < total_num_certs; i++) {
certs_CRNs_arr[i] = json_arr.certificates[i]._id;
}
callback(certs_CRNs_arr);
}
});
} else {
console.log(`failed to list certificates with status: ${res.statusCode}`);
res.setEncoding('utf8');
res.on('data', (chunk) => {
console.log(`BODY: ${chunk}`);
});
}
});
req.on('error', (e) => {
console.error(`problem with request: ${e.message}`);
});
req.end();
}
//function for retrieving metadata of the certificates repository. The total number of certificates, the number of expired certificates, and the number of certificates expiring in the next 30 days.
function getCertsMetadata(options) {
var str = '';
const req = https.request(options, (res) => {
if(res.statusCode === 200){
res.setEncoding('utf8');
res.on('data', function(chunk) {
str += chunk;
});
res.on('end', function() {
console.log(`successfully get certificates repository metadata as following: ${str}`);
});
} else {
console.log(`failed to get certificates repository metadata with status: ${res.statusCode}`);
res.setEncoding('utf8');
res.on('data', (chunk) => {
console.log(`BODY: ${chunk}`);
});
}
});
req.on('error', (e) => {
console.error(`problem with request: ${e.message}`);
});
req.end();
}
//Main block
//get iam access token by given apikey
const iam_options = {
protocol: 'https:',
hostname: 'iam.bluemix.net',
port: 443,
path: '/identity/token',
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Accept': 'application/json'
}
};
var iam_token_body = `grant_type=urn%3Aibm%3Aparams%3Aoauth%3Agrant-type%3Aapikey&apikey=${apiKey}`;
var str = '';
var iam_access_token = '';
var json_arr = {};
var options = {};
//function to get iam_access_token and provide the callback to get the returned value of iam token
function get_iam_access_token(options, callback){
const iam_token_req = https.request(options, (res) => {
if(res.statusCode === 200){
res.setEncoding('utf8');
res.on('data', function(chunk) {
str += chunk;
});
res.on('end', function() {
json_arr = JSON.parse(str);
callback(json_arr.access_token);
});
} else {
console.log(`failed to get iam token with status: ${res.statusCode}`);
res.setEncoding('utf8');
res.on('data', (data) => {
console.log(`BODY: ${data}`);
});
}
});
iam_token_req.on('error', (e) => {
console.error(`problem with request: ${e.message}`);
});
// write data to request body
iam_token_req.end(iam_token_body);
}
function generateOptions(CM_cluster_url, request_path, iam_access_token, request_method) {
options = {protocol: 'https:',
hostname: CM_cluster_url,
port: 443,
path: request_path,
method: request_method,
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${iam_access_token}`
}
};
return options;
}
//call the callback to generate the iam access token, and do the corresponding actions i.e. importing certs, list certs, delete certs, etc
get_iam_access_token(iam_options, function(iam_access_token){
request_path = '';
CM_cluster_url = `${region}.${CM_domain}`;
switch (program.action) {
case "import":
request_path = `/api/v3/${encodeURIComponent(instanceCRN)}/certificates/import`;
options = generateOptions(CM_cluster_url, request_path, iam_access_token, 'POST');
importCertificates(options, local_cert_dir_file, 'import');
break;
case "reimport":
request_path = `/api/v1/certificate/${encodeURIComponent(certCRN)}`;
options = generateOptions(CM_cluster_url, request_path, iam_access_token, 'PUT');
reimportCertificate(options, local_cert_dir_file, 'reimport');
break;
case "export":
if ( certCRN !== "" && typeof certCRN !== 'undefined' ) {
request_path = `/api/v2/certificate/${encodeURIComponent(certCRN)}`;
options = generateOptions(CM_cluster_url, request_path, iam_access_token, 'GET');
exportCertificate(options, local_cert_dir_file);
} else {
var certs_CRNs_arr = [];
var list_request_path = `/api/v2/${encodeURIComponent(instanceCRN)}/certificates`;
list_options = generateOptions(CM_cluster_url, list_request_path, iam_access_token, 'GET');
getAllCertificatesCRNs(list_options, function (certs_CRNs_arr){
if (certs_CRNs_arr.length > 0) {
certs_CRNs_arr.forEach(function (cert_crn) {
var exportAll_request_path = `/api/v2/certificate/${encodeURIComponent(cert_crn)}`;
export_options = generateOptions(CM_cluster_url, exportAll_request_path, iam_access_token, 'GET');
exportCertificate(export_options, local_cert_dir_file);
})
}
});
}
break;
case "delete":
request_path = `/api/v2/certificate/${encodeURIComponent(certCRN)}`;
options = generateOptions(CM_cluster_url, request_path, iam_access_token, 'DELETE');
deleteCertificate(options, `${encodeURIComponent(certCRN)}`);
break;
case "purgeAll":
var certs_CRNs_arr = [];
var list_request_path = `/api/v2/${encodeURIComponent(instanceCRN)}/certificates`;
list_options = generateOptions(CM_cluster_url, list_request_path, iam_access_token, 'GET');
getAllCertificatesCRNs(list_options, function (certs_CRNs_arr){
if (certs_CRNs_arr.length > 0) {
certs_CRNs_arr.forEach(function (cert_crn) {
var delete_request_path = `/api/v2/certificate/${encodeURIComponent(cert_crn)}`;
delete_options = generateOptions(CM_cluster_url, delete_request_path, iam_access_token, 'DELETE');
deleteCertificate(delete_options, `${encodeURIComponent(cert_crn)}`);
})
}
});
break;
case "list":
request_path = `/api/v2/${encodeURIComponent(instanceCRN)}/certificates`;
options = generateOptions(CM_cluster_url, request_path, iam_access_token, 'GET');
listCertificates(options);
break;
case "meta":
request_path = `/api/v2/${encodeURIComponent(instanceCRN)}/certificates/metadata`;
options = generateOptions(CM_cluster_url, request_path, iam_access_token, 'GET');
getCertsMetadata(options);
break;
default:
console.log('unsupported cert management action');
}
});