Add vitastor-cli create & modify --enc-key parameter

This commit is contained in:
Vitaliy Filippov
2026-04-27 15:24:44 +03:00
parent 66a016f8a1
commit 29640d1aac
8 changed files with 147 additions and 23 deletions
+20 -7
View File
@@ -125,18 +125,30 @@ bench-kaveri kaveri 10 G 10 G 0 B/s 0 0 0 us 0 B/s 0
## create
`vitastor-cli create -s|--size <size> [-p|--pool <id|name>] [--parent <parent_name>[@<snapshot>]] <name>`
`vitastor-cli create -s|--size SIZE [OPTIONS] <name>`
Create an image. You may use K/M/G/T suffixes for `<size>`. If `--parent` is specified,
a copy-on-write image clone is created. Parent must be a snapshot (readonly image).
Pool must be specified if there is more than one pool.
Create an image. Options:
* `-s|--size SIZE` - New image size in bytes or with a K/M/G/T unit suffix.
* `-p|--pool POOL` - Specify pool for the new image (may be omitted if there is only 1 pool).
* `--parent PARENT` - Create a copy-on-write image clone based on PARENT (or PARENT@SNAPSHOT).
If parent is not a snapshot, it must be a read-only image.
* `--enc-key random` - Generate a new random AES-256-XTS encryption key for the new image.
* `--enc-key HEX` - Set a specified AES-256-XTS key (64 bytes in hex) for the new image.
```
vitastor-cli create --snapshot <snapshot> [-p|--pool <id|name>] <image>
vitastor-cli snap-create [-p|--pool <id|name>] <image>@<snapshot>
vitastor-cli create --snapshot <snapshot> [OPTIONS] <image>
vitastor-cli snap-create [OPTIONS] <image>@<snapshot>
```
Create a snapshot of image `<name>` (either form can be used). May be used live if only a single writer is active.
Create a snapshot of image `<image>`. May be used live if only a single writer is active.
Options:
* `-p|--pool POOL` - Move image to pool POOL, leaving the snapshot in the old pool.
* `--enc-key random` - Change image encryption key to a new random AES-256-XTS key.
* `--enc-key HEX` - Change image encryption key to a specified key or to an empty key.
By default, the image retains its old encryption key when taking a snapshot.
See also about [how to export snapshots](qemu.en.md#exporting-snapshots).
@@ -151,6 +163,7 @@ You should resize file system in the image, if present, before shrinking it.
* `--deleted 1|0` - Set/clear 'deleted image' flag (set automatically during unfinished deletes).
* `-f|--force` - Proceed with shrinking or setting readwrite flag even if the image has children.
* `--down-ok` - Proceed with shrinking even if some data will be left on unavailable OSDs.
* `--enc-key HEX` - Change image encryption key (allowed only with `--force`).
## dd
+21 -8
View File
@@ -127,19 +127,31 @@ bench-kaveri kaveri 10 G 10 G 0 B/s 0 0 0 us 0 B/s 0
## create
`vitastor-cli create -s|--size <size> [-p|--pool <id|name>] [--parent <parent_name>[@<snapshot>]] <name>`
`vitastor-cli create -s|--size SIZE [ОПЦИИ] <name>`
Создать образ. Для размера `<size>` можно использовать суффиксы K/M/G/T (килобайт-мегабайт-гигабайт-терабайт).
Если указана опция `--parent`, создаётся клон образа. Родитель `<parent_name>[@<snapshot>]` должен быть
снимком (или просто немодифицируемым образом). Пул обязательно указывать, если в кластере больше одного пула.
Создать образ. Опции:
* `-s|--size SIZE` - Размер нового образа в байтах или с суффиксом K/M/G/T (кило/мега/гига/терабайт).
* `-p|--pool POOL` - Создать образ в заданном пуле (можно не указывать, если пул всего один).
* `--parent PARENT` - Создать легковесный клон на основе образа `PARENT` или снимка `PARENT@SNAP`.
Если `PARENT` - не снимок, он должен быть помечен как образ только для чтения.
* `--enc-key random` - Сгенерировать случайный ключ шифрования AES-256-XTS для нового образа.
* `--enc-key HEX` - Установить заданный ключ AES-256-XTS (64 байта в hex) для нового образа.
```
vitastor-cli create --snapshot <snapshot> [-p|--pool <id|name>] <image>
vitastor-cli snap-create [-p|--pool <id|name>] <image>@<snapshot>
vitastor-cli create --snapshot <snapshot> [ОПЦИИ] <image>
vitastor-cli snap-create [ОПЦИИ] <image>@<snapshot>
```
Создать снимок образа `<name>` (можно использовать любую форму команды). Снимок можно создавать без остановки
клиентов, если пишущий клиент максимум 1.
Создать снимок образа `<image>` (можно использовать любую форму команды).
Снимок можно создавать без остановки клиентов, если пишущих клиентов не больше одного.
Опции:
* `-p|--pool POOL` - Переместить образ в пул POOL, оставив снимок в старом пуле.
* `--enc-key random` - Изменить ключ шифрования образа на новый случайный ключ AES-256-XTS.
* `--enc-key HEX` - Изменить ключ шифрования образа на заданный или пустой ключ.
По умолчанию шифрованные образы сохраняют старый ключ при снятии снимка.
Смотрите также информацию о том, [как экспортировать снимки](qemu.ru.md#экспорт-снимков).
@@ -156,6 +168,7 @@ vitastor-cli snap-create [-p|--pool <id|name>] <image>@<snapshot>
* `--deleted 1|0` - Установить/снять флаг "образ удалён" (устанавливается при незавершённом удалении).
* `-f|--force` - Разрешить уменьшение или перевод в чтение-запись образа, у которого есть клоны.
* `--down-ok` - Разрешить уменьшение, даже если часть данных останется неудалённой на недоступных OSD.
* `--enc-key HEX` - Изменить ключ шифрования образа (разрешено только с `--force`).
## dd
+17 -7
View File
@@ -40,14 +40,23 @@ static const char* help_text =
" --ids ID1,ID2 Only list images with specified full IDs\n"
" --tree Show image snapshot/clone tree\n"
"\n"
"vitastor-cli create -s|--size <size> [-p|--pool <id|name>] [--parent <parent_name>[@<snapshot>]] <name>\n"
" Create an image. You may use K/M/G/T suffixes for <size>. If --parent is specified,\n"
" a copy-on-write image clone is created. Parent must be a snapshot (readonly image).\n"
" Pool must be specified if there is more than one pool.\n"
"vitastor-cli create -s|--size SIZE [OPTIONS] <name>\n"
" Create an image. Options:\n"
" -s|--size SIZE New image size in bytes or with a K/M/G/T unit suffix.\n"
" -p|--pool POOL Specify pool for the new image (may be omitted if there is only 1 pool).\n"
" --parent PARENT Create a copy-on-write image clone based on PARENT (or PARENT@SNAPSHOT).\n"
" If parent is not a snapshot, it must be a read-only image.\n"
" --enc-key random Generate a new random AES-256-XTS encryption key for the new image.\n"
" --enc-key HEX Set a specified AES-256-XTS key (64 bytes in hex) for the new image.\n"
"\n"
"vitastor-cli create --snapshot <snapshot> [-p|--pool <id|name>] <image>\n"
"vitastor-cli snap-create [-p|--pool <id|name>] <image>@<snapshot>\n"
" Create a snapshot of image <name>. May be used live if only a single writer is active.\n"
"vitastor-cli create --snapshot <snapshot> [OPTIONS] <image>\n"
"vitastor-cli snap-create [OPTIONS] <image>@<snapshot>\n"
" Create a snapshot of image <image>. May be used live if only a single writer is active.\n"
" Options:\n"
" -p|--pool POOL Move image to pool POOL, leaving the snapshot in the old pool.\n"
" --enc-key random Change image encryption key to a new random AES-256-XTS key.\n"
" --enc-key HEX Change image encryption key to a specified key or to an empty key.\n"
" By default, the image retains its old key when taking a snapshot.\n"
"\n"
"vitastor-cli modify <name> [--rename <new-name>] [--resize <size>] [--readonly | --readwrite] [-f|--force] [--down-ok]\n"
" Rename, resize image or change its readonly status. Images with children can't be made read-write.\n"
@@ -56,6 +65,7 @@ static const char* help_text =
" --deleted 1|0 Set/clear 'deleted image' flag (set automatically during unfinished deletes).\n"
" -f|--force Proceed with shrinking or setting readwrite flag even if the image has children.\n"
" --down-ok Proceed with shrinking even if some data will be left on unavailable OSDs.\n"
" --enc-key HEX Change image encryption key (allowed only with --force).\n"
"\n"
"vitastor-cli dd [iimg=<image> | if=<file>] [oimg=<image> | of=<file>] [bs=1M]\n"
" [count=N] [seek/oseek=N] [skip/iseek=M] [iodepth=N] [status=progress]\n"
+38
View File
@@ -1,6 +1,10 @@
// Copyright (c) Vitaliy Filippov, 2019+
// License: VNPL-1.1 (see README.md for details)
#ifdef WITH_OPENSSL
#include <openssl/rand.h>
#endif
#include <ctype.h>
#include "cli.h"
#include "cluster_client.h"
@@ -29,6 +33,8 @@ struct image_creator_t
uint64_t size = 0;
bool force = false;
bool force_size = false;
std::string enc_key;
bool set_key = false;
pool_id_t old_pool_id = 0;
inode_t new_parent_id = 0;
@@ -448,6 +454,13 @@ resume_3:
.readonly = false,
.meta = new_meta,
};
if (set_key)
{
new_cfg.enc_key.resize(enc_key.size()/2);
fromhexstr(enc_key, new_cfg.enc_key.size(), new_cfg.enc_key.data());
}
else if (new_snap != "")
new_cfg.enc_key = cur_cfg.enc_key;
json11::Json::array checks = json11::Json::array {
json11::Json::object {
{ "target", "VERSION" },
@@ -584,6 +597,31 @@ std::function<bool(cli_result_t &)> cli_tool_t::start_create(json11::Json cfg)
{
image_creator->new_snap = cfg["snapshot"].string_value();
}
if (!cfg["enc_key"].is_null())
{
image_creator->set_key = true;
image_creator->enc_key = cfg["enc_key"].string_value();
if (image_creator->enc_key != "" &&
#ifdef WITH_OPENSSL
image_creator->enc_key != "random" &&
#endif
(!ishexstr(image_creator->enc_key) || image_creator->enc_key.size() != 128))
{
return [](cli_result_t & result)
{
result = (cli_result_t){ .err = EINVAL, .text = "Encryption key is not a 512-bit hex string, not \"\" and not \"random\"" };
return true;
};
}
#ifdef WITH_OPENSSL
if (image_creator->enc_key == "random")
{
uint8_t newkey[64];
RAND_bytes(newkey, 64);
image_creator->enc_key = tohexstr(newkey, 64);
}
#endif
}
image_creator->new_parent = cfg["parent"].string_value();
if (!cfg["size"].is_null())
{
+21
View File
@@ -17,6 +17,8 @@ struct image_changer_t
bool force_size = false, inc_size = false;
bool set_readonly = false, set_readwrite = false, force = false;
bool set_deleted = false, new_deleted = false;
bool set_key = false;
std::string enc_key;
bool down_ok = false;
// interval between fsyncs
int fsync_interval = 128;
@@ -149,6 +151,23 @@ resume_1:
{
cfg.name = new_name;
}
if (set_key)
{
if (!force)
{
result = (cli_result_t){ .err = EINVAL, .text = "Changing image encryption key is only allowed with --force" };
state = 100;
return;
}
if (enc_key != "" && (!ishexstr(enc_key) || enc_key.size() != 128))
{
result = (cli_result_t){ .err = EINVAL, .text = "Encryption key is not a 512-bit hex string and not \"\"" };
state = 100;
return;
}
cfg.enc_key.resize(enc_key.size()/2);
fromhexstr(enc_key, cfg.enc_key.size(), cfg.enc_key.data());
}
{
std::string cur_cfg_key = base64_encode(parent->cli->st_cli.etcd_prefix+
"/config/inode/"+std::to_string(INODE_POOL(inode_num))+
@@ -285,6 +304,8 @@ std::function<bool(cli_result_t &)> cli_tool_t::start_modify(json11::Json cfg)
changer->set_deleted = !cfg["deleted"].is_null();
changer->new_deleted = json_is_true(cfg["deleted"]);
changer->fsync_interval = cfg["fsync_interval"].uint64_value();
changer->enc_key = cfg["enc_key"].string_value();
changer->set_key = cfg["enc_key"].is_string();
if (!changer->fsync_interval)
changer->fsync_interval = 128;
changer->down_ok = cfg["down_ok"].bool_value();
+15 -1
View File
@@ -101,7 +101,14 @@
},
"pool_id": { "type": "integer", "format": "uint64", "description": "Pool ID for the new image/snapshot" },
"pool_name": { "type": "string", "description": "Pool name for the new image/snapshot" },
"parent": { "type": "string", "description": "Create a clone with this parent image name" }
"parent": { "type": "string", "description": "Create a clone with this parent image name" },
"enc_key": {
"description": "Encryption key for the new/cloned image",
"oneOf": [
{ "type": "string", "enum": [ "", "random" ] },
{ "type": "string", "pattern": "^[0-9a-fA-F]{128}$" }
]
}
}
} } } },
"responses": {
@@ -132,6 +139,13 @@
"readonly": { "type": "boolean", "description": "Make the image read-only" },
"readwrite": { "type": "boolean", "description": "Make the image read-write" },
"deleted": { "type": "boolean", "description": "Set or clear the 'deleted' flag" },
"enc_key": {
"description": "Change encryption key for the image",
"oneOf": [
{ "type": "string", "enum": [ "" ] },
{ "type": "string", "pattern": "^[0-9a-fA-F]{128}$" }
]
},
"force": { "type": "boolean", "description": "Proceed with shrinking or setting readwrite flag even if the image has children" },
"down_ok": { "type": "boolean", "description": "Proceed with shrinking even if some data will be left on unavailable OSDs" }
}
+14
View File
@@ -565,3 +565,17 @@ std::string tohexstr(const uint8_t *from, size_t bytes)
}
return res;
}
bool ishexstr(const std::string & str)
{
if (str.size() % 2)
return false;
for (auto & c: str)
{
if ((c < '0' || c > '9') &&
(c < 'a' || c > 'f') &&
(c < 'A' || c > 'F'))
return false;
}
return true;
}
+1
View File
@@ -37,5 +37,6 @@ bool is_zero(void *buf, size_t size);
std::string urldecode(const std::string & orig);
size_t fromhexstr(const std::string & from, size_t bytes, uint8_t *to);
std::string tohexstr(const uint8_t *from, size_t bytes);
bool ishexstr(const std::string & str);
#pragma GCC visibility pop