Skip to content

Commit e7931fe

Browse files
committed
nsfs nc secret keys encryption
Signed-off-by: Romy <[email protected]> (cherry picked from commit 1ed5310)
1 parent 3588a15 commit e7931fe

15 files changed

+640
-39
lines changed

config.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -822,6 +822,15 @@ config.NSFS_WHITELIST = [];
822822
config.NSFS_HEALTH_ENDPOINT_RETRY_COUNT = 3;
823823
config.NSFS_HEALTH_ENDPOINT_RETRY_DELAY = 10;
824824

825+
826+
/** @type {'file' | 'executable'} */
827+
config.NC_MASTER_KEYS_STORE_TYPE = 'file';
828+
// unless override in config.json, the default will be the config_dir/master_keys.json
829+
config.NC_MASTER_KEYS_FILE_LOCATION = '';
830+
config.NC_MASTER_KEYS_GET_EXECUTABLE = '';
831+
config.NC_MASTER_KEYS_PUT_EXECUTABLE = '';
832+
config.NC_MASTER_KEYS_MANAGER_REFRESH_THRESHOLD = 5 * 60 * 1000; // 5 minutes
833+
825834
//Quota
826835
config.QUOTA_LOW_THRESHOLD = 80;
827836
config.QUOTA_MAX_OBJECTS = Number.MAX_SAFE_INTEGER;

docs/dev_guide/NonContainerizedDeveloperCustomizations.md

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,74 @@ Example:
337337
3. systemctl restart noobaa_nsfs
338338
```
339339

340+
## 19. Set Master Keys Store type-
341+
**Description -** This flag will set the type of the master keys store used by NooBaa.
342+
343+
**Configuration Key -** NC_MASTER_KEYS_STORE_TYPE
344+
345+
**Type -** string
346+
347+
**Default -** 'file'
348+
**Steps -**
349+
```
350+
1. Open /path/to/config_dir/config.json file.
351+
2. Set the config key -
352+
Example:
353+
"NC_MASTER_KEYS_STORE_TYPE": 'executable'
354+
3. systemctl restart noobaa_nsfs
355+
```
356+
357+
## 20. Set Master Keys File Location -
358+
**Description -** This flag will set the location of the master keys file used by NooBaa.
359+
360+
**Configuration Key -** NC_MASTER_KEYS_FILE_LOCATION
361+
362+
**Type -** string
363+
364+
**Default -** '/etc/noobaa.conf.d/master_keys.json'
365+
**Steps -**
366+
```
367+
1. Open /path/to/config_dir/config.json file.
368+
2. Set the config key -
369+
Example:
370+
"NC_MASTER_KEYS_FILE_LOCATION": '/private/tmp/master_keys.json'
371+
3. systemctl restart noobaa_nsfs
372+
```
373+
374+
## 21. Set Master Keys GET executable script -
375+
**Description -** This flag will set the location of the executable script for reading the master keys file used by NooBa.
376+
377+
**Configuration Key -** NC_MASTER_KEYS_GET_EXECUTABLE
378+
379+
**Type -** string
380+
381+
**Default -** undefined
382+
**Steps -**
383+
```
384+
1. Open /path/to/config_dir/config.json file.
385+
2. Set the config key -
386+
Example:
387+
"NC_MASTER_KEYS_GET_EXECUTABLE": '/private/tmp/get_master_keys.sh'
388+
3. systemctl restart noobaa_nsfs
389+
```
390+
391+
## 22. Set Master Keys PUT executable script -
392+
**Description -** This flag will set the location of the executable script for updating the master keys file used by NooBa.
393+
394+
**Configuration Key -** NC_MASTER_KEYS_PUT_EXECUTABLE
395+
396+
**Type -** string
397+
398+
**Default -** undefined
399+
**Steps -**
400+
```
401+
1. Open /path/to/config_dir/config.json file.
402+
2. Set the config key -
403+
Example:
404+
"NC_MASTER_KEYS_PUT_EXECUTABLE": '/private/tmp/put_master_keys.sh'
405+
3. systemctl restart noobaa_nsfs
406+
```
407+
340408
## Config.json example
341409
```
342410
> cat /path/to/config_dir/config.json

src/cmd/manage_nsfs.js

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ const { print_usage } = require('../manage_nsfs/manage_nsfs_help_utils');
2525
const { TYPES, ACTIONS, VALID_OPTIONS, OPTION_TYPE, FROM_FILE, BOOLEAN_STRING_VALUES,
2626
LIST_ACCOUNT_FILTERS, LIST_BUCKET_FILTERS, GLACIER_ACTIONS } = require('../manage_nsfs/manage_nsfs_constants');
2727
const NoobaaEvent = require('../manage_nsfs/manage_nsfs_events_utils').NoobaaEvent;
28+
const nc_mkm = require('../manage_nsfs/nc_master_key_manager').get_instance();
2829

2930
function throw_cli_error(error_code, detail, event_arg) {
3031
const error_event = NSFS_CLI_ERROR_EVENT_MAP[error_code.code];
@@ -200,7 +201,7 @@ async function add_bucket(data) {
200201
const fs_context = native_fs_utils.get_process_fs_context(config_root_backend);
201202
const bucket_conf_path = get_config_file_path(buckets_dir_path, data.name);
202203
const exists = await native_fs_utils.is_path_exists(fs_context, bucket_conf_path);
203-
if (exists) throw_cli_error(ManageCLIError.BucketAlreadyExists, data.name, {bucket: data.name});
204+
if (exists) throw_cli_error(ManageCLIError.BucketAlreadyExists, data.name, { bucket: data.name });
204205
data._id = mongo_utils.mongoObjectId();
205206
data.owner_account = account_id;
206207
const data_json = JSON.stringify(data);
@@ -209,7 +210,7 @@ async function add_bucket(data) {
209210
// for validating against the schema we need an object, hence we parse it back to object
210211
nsfs_schema_utils.validate_bucket_schema(JSON.parse(data_json));
211212
await native_fs_utils.create_config_file(fs_context, buckets_dir_path, bucket_conf_path, data_json);
212-
write_stdout_response(ManageCLIResponse.BucketCreated, data_json, {bucket: data.name});
213+
write_stdout_response(ManageCLIResponse.BucketCreated, data_json, { bucket: data.name });
213214
}
214215

215216
/** verify_bucket_owner will check if the bucket_owner has an account
@@ -419,6 +420,7 @@ async function fetch_existing_account_data(target) {
419420
get_config_file_path(accounts_dir_path, target.name) :
420421
get_symlink_config_file_path(access_keys_dir_path, target.access_keys[0].access_key);
421422
source = await get_config_data(account_path, true);
423+
source.access_keys = await nc_mkm.decrypt_access_keys(source);
422424
} catch (err) {
423425
dbg.log1('NSFS Manage command: Could not find account', target, err);
424426
if (err.code === 'ENOENT') {
@@ -452,17 +454,20 @@ async function add_account(data) {
452454
throw_cli_error(err_code, event_arg, {account: event_arg});
453455
}
454456
data._id = mongo_utils.mongoObjectId();
455-
data = JSON.stringify(data);
457+
const encrypted_account = await nc_mkm.encrypt_access_keys(data);
458+
data.master_key_id = encrypted_account.master_key_id;
459+
const encrypted_data = JSON.stringify(encrypted_account);
456460
// We take an object that was stringify
457461
// (it unwraps ths sensitive strings, creation_date to string and removes undefined parameters)
458462
// for validating against the schema we need an object, hence we parse it back to object
459-
nsfs_schema_utils.validate_account_schema(JSON.parse(data));
460-
await native_fs_utils.create_config_file(fs_context, accounts_dir_path, account_config_path, data);
463+
nsfs_schema_utils.validate_account_schema(JSON.parse(encrypted_data));
464+
await native_fs_utils.create_config_file(fs_context, accounts_dir_path, account_config_path, encrypted_data);
461465
await native_fs_utils._create_path(access_keys_dir_path, fs_context, config.BASE_MODE_CONFIG_DIR);
462466
await nb_native().fs.symlink(fs_context, account_config_relative_path, account_config_access_key_path);
463-
write_stdout_response(ManageCLIResponse.AccountCreated, data, {account: event_arg});
467+
write_stdout_response(ManageCLIResponse.AccountCreated, data, { account: event_arg });
464468
}
465469

470+
466471
async function update_account(data) {
467472
await validate_account_args(data, ACTIONS.UPDATE);
468473

@@ -475,12 +480,14 @@ async function update_account(data) {
475480

476481
if (!update_name && !update_access_key) {
477482
const account_config_path = get_config_file_path(accounts_dir_path, data.name);
478-
data = JSON.stringify(data);
483+
const encrypted_account = await nc_mkm.encrypt_access_keys(data);
484+
data.master_key_id = encrypted_account.master_key_id;
485+
const encrypted_data = JSON.stringify(encrypted_account);
479486
// We take an object that was stringify
480487
// (it unwraps ths sensitive strings, creation_date to string and removes undefined parameters)
481488
// for validating against the schema we need an object, hence we parse it back to object
482-
nsfs_schema_utils.validate_account_schema(JSON.parse(data));
483-
await native_fs_utils.update_config_file(fs_context, accounts_dir_path, account_config_path, data);
489+
nsfs_schema_utils.validate_account_schema(JSON.parse(encrypted_data));
490+
await native_fs_utils.update_config_file(fs_context, accounts_dir_path, account_config_path, encrypted_data);
484491
write_stdout_response(ManageCLIResponse.AccountUpdated, data);
485492
return;
486493
}
@@ -499,16 +506,21 @@ async function update_account(data) {
499506
const err_code = name_exists ? ManageCLIError.AccountNameAlreadyExists : ManageCLIError.AccountAccessKeyAlreadyExists;
500507
throw_cli_error(err_code);
501508
}
502-
data = JSON.stringify(_.omit(data, ['new_name', 'new_access_key']));
509+
data = _.omit(data, ['new_name', 'new_access_key']);
510+
const encrypted_account = await nc_mkm.encrypt_access_keys(data);
511+
data.master_key_id = encrypted_account.master_key_id;
512+
const encrypted_data = JSON.stringify(encrypted_account);
513+
data = JSON.stringify(data);
514+
503515
// We take an object that was stringify
504516
// (it unwraps ths sensitive strings, creation_date to string and removes undefined parameters)
505517
// for validating against the schema we need an object, hence we parse it back to object
506-
nsfs_schema_utils.validate_account_schema(JSON.parse(data));
518+
nsfs_schema_utils.validate_account_schema(JSON.parse(encrypted_data));
507519
if (update_name) {
508-
await native_fs_utils.create_config_file(fs_context, accounts_dir_path, new_account_config_path, data);
520+
await native_fs_utils.create_config_file(fs_context, accounts_dir_path, new_account_config_path, encrypted_data);
509521
await native_fs_utils.delete_config_file(fs_context, accounts_dir_path, cur_account_config_path);
510522
} else if (update_access_key) {
511-
await native_fs_utils.update_config_file(fs_context, accounts_dir_path, cur_account_config_path, data);
523+
await native_fs_utils.update_config_file(fs_context, accounts_dir_path, cur_account_config_path, encrypted_data);
512524
}
513525
// TODO: safe_unlink can be better but the current impl causing ELOOP - Too many levels of symbolic links
514526
// need to find a better way for atomic unlinking of symbolic links
@@ -560,6 +572,7 @@ async function get_account_status(data, show_secrets) {
560572
get_symlink_config_file_path(access_keys_dir_path, data.access_keys[0].access_key) :
561573
get_config_file_path(accounts_dir_path, data.name);
562574
const config_data = await get_config_data(account_path, show_secrets);
575+
if (config_data.access_keys) config_data.access_keys = await nc_mkm.decrypt_access_keys(config_data);
563576
write_stdout_response(ManageCLIResponse.AccountStatus, config_data);
564577
} catch (err) {
565578
if (_.isUndefined(data.name)) {
@@ -656,6 +669,7 @@ async function list_config_files(type, config_path, wide, show_secrets, filters)
656669
if (wide || should_filter) {
657670
const full_path = path.join(config_path, entry.name);
658671
const data = await get_config_data(full_path, show_secrets || should_filter);
672+
if (data.access_keys) data.access_keys = await nc_mkm.decrypt_access_keys(data);
659673
if (should_filter && !filter_list_item(type, data, filters)) return undefined;
660674
// remove secrets on !show_secrets && should filter
661675
return wide ? _.omit(data, show_secrets ? [] : ['access_keys']) : { name: entry.name.slice(0, entry.name.indexOf('.json')) };

0 commit comments

Comments
 (0)