Support storing image encryption keys in Vault

This commit is contained in:
Vitaliy Filippov
2026-07-05 14:10:11 +03:00
parent c0d2dabe66
commit 8f208c53df
19 changed files with 626 additions and 229 deletions
+17
View File
@@ -0,0 +1,17 @@
// Copyright (c) Vitaliy Filippov, 2019+
// License: VNPL-1.1 or GNU GPL-2.0+ (see README.md for details)
#include "cluster_client.h"
void cluster_client_t::vault_destroy()
{
}
void cluster_client_t::vault_load_keys()
{
if (vault_loading || !vault_key_load_queue.size())
{
return;
}
vault_loading = true;
}
+88 -25
View File
@@ -19,6 +19,8 @@ public:
{
cli->continue_ops(cli->client_retry_interval);
}
static void test_vault();
};
void configure_single_pg_pool(etcd_state_client_mock_t *mock)
@@ -55,7 +57,7 @@ int *test_write(cluster_client_t *cli, uint64_t offset, uint64_t len, uint8_t c,
{
printf("Post write %jx+%jx\n", offset, len);
int *r = new int;
*r = instant ? -2 : -1;
*r = instant ? -1001 : -1000;
cluster_op_t *op = new cluster_op_t();
op->opcode = OSD_OP_WRITE;
op->inode = 0x1000000000001;
@@ -65,10 +67,11 @@ int *test_write(cluster_client_t *cli, uint64_t offset, uint64_t len, uint8_t c,
memset(op->iov.buf[0].iov_base, c, len);
op->callback = [r, cb](cluster_op_t *op)
{
if (*r == -1)
printf("Error: Not allowed to complete yet (retval %d)\n", op->retval);
assert(*r != -1);
*r = op->retval == op->len ? 1 : 0;
if (*r == -1000)
printf("Error: Not allowed to complete yet\n");
assert(*r != -1000);
assert(op->retval == op->len || op->retval < 0);
*r = op->retval == op->len ? 1 : op->retval;
free(op->iov.buf[0].iov_base);
printf("Done write %jx+%jx r=%d\n", op->offset, op->len, op->retval);
delete op;
@@ -90,14 +93,14 @@ int *test_sync(cluster_client_t *cli)
{
printf("Post sync\n");
int *r = new int;
*r = -1;
*r = -1000;
cluster_op_t *op = new cluster_op_t();
op->opcode = OSD_OP_SYNC;
op->callback = [r](cluster_op_t *op)
{
if (*r == -1)
if (*r == -1000)
printf("Error: Not allowed to complete yet\n");
assert(*r != -1);
assert(*r != -1000);
*r = op->retval == 0 ? 1 : 0;
printf("Done sync r=%d\n", op->retval);
delete op;
@@ -110,7 +113,7 @@ void can_complete(int *r)
{
// Allow the operation to proceed so the test verifies
// that it doesn't complete earlier than expected
*r = -2;
*r = -1001;
}
#define check_completed(r) { assert(*(r) == 1); delete (r); }
@@ -145,24 +148,22 @@ void pretend_disconnected(cluster_client_t *cli, osd_num_t osd_num)
pretend_disconnected(&cli->msgr, osd_num);
}
void check_disconnected(cluster_client_t *cli, osd_num_t osd_num)
{
if (cli->msgr.osd_peers.find(osd_num) != cli->msgr.osd_peers.end())
{
printf("OSD %ju not disconnected as it ought to be\n", osd_num);
assert(0);
}
#define check_disconnected(cli, osd_num) {\
if (cli->msgr.osd_peers.find(osd_num) != cli->msgr.osd_peers.end())\
{\
printf("OSD %ju not disconnected as it ought to be\n", (uint64_t)osd_num);\
assert(0);\
}\
}
void check_op_count(cluster_client_t *cli, osd_num_t osd_num, int ops)
{
osd_client_t *cl = cli->msgr.osd_peers.at(osd_num);
int real_ops = cl->sent_ops.size();
if (real_ops != ops)
{
printf("error: %d ops expected, but %d queued\n", ops, real_ops);
assert(0);
}
#define check_op_count(cli, osd_num, ops) {\
osd_client_t *cl = cli->msgr.osd_peers.at(osd_num);\
int real_ops = cl->sent_ops.size();\
if (real_ops != ops)\
{\
printf("error: %d ops expected, but %d queued\n", ops, real_ops);\
assert(0);\
}\
}
osd_op_t *find_op(cluster_client_t *cli, osd_num_t osd_num, uint64_t opcode, uint64_t offset, uint64_t len)
@@ -824,6 +825,67 @@ void test_msgr_decrypt_chain()
}
#endif
void cluster_client_test_t::test_vault()
{
json11::Json::object config;
config["vault_url"] = "http://vault";
timerfd_manager_t *tfd = new timerfd_manager_t([](int fd, bool wr, std::function<void(int, int)> callback){});
etcd_state_client_mock_t *mock = new etcd_state_client_mock_t();
mock->pause();
cluster_client_t *cli = new cluster_client_t(NULL, tfd, config, std::unique_ptr<etcd_state_client_t>(mock));
mock->set("/vitastor/config/inode/1/1", json11::Json::object {
{ "name", "testimg" },
{ "size", (uint64_t)10*1024*1024*1024 },
{ "enc_key", "vault:key1" },
});
configure_single_pg_pool(mock);
mock->resume();
pretend_connected(cli, 1);
// No key -> fetch successfully -> complete op
int *r1 = test_write(cli, 0, 4096, 0x55);
check_op_count(cli, 1, 0);
assert(cli->vault_key_load_queue == std::vector<std::string>{"vault:key1"});
cli->vault_key_load_queue.clear();
cli->vault_parse_secret("vault:key1", "", json11::Json::object{
{"data", json11::Json::object {
{"key", "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"}
}}
});
can_complete(r1);
check_op_count(cli, 1, 1);
pretend_op_completed(cli, find_op(cli, 1, OSD_OP_WRITE, 0, 4096), 0);
check_completed(r1);
// No key -> error -> EPERM
cli->vault_keys.clear();
cli->inode_cache.clear();
cli->inode_cache_children.clear();
r1 = test_write(cli, 0, 4096, 0x55);
check_op_count(cli, 1, 0);
assert(cli->vault_key_load_queue == std::vector<std::string>{"vault:key1"});
cli->vault_key_load_queue.clear();
can_complete(r1);
cli->vault_parse_secret("vault:key1", "HTTP 403 Forbidden", json11::Json());
check_op_count(cli, 1, 0);
assert(*r1 == -EPERM);
delete r1;
// Free client
delete cli;
delete tfd;
printf("[ok] basic vault key fetch test\n");
}
int main(int narg, char *args[])
{
test1();
@@ -835,5 +897,6 @@ int main(int narg, char *args[])
test_msgr_encrypt();
test_msgr_decrypt_chain();
#endif
cluster_client_test_t::test_vault();
return 0;
}