Add image owner/owner_group/reader_group support (for antietcd VitastorAuthFilter)
This commit is contained in:
@@ -208,6 +208,8 @@ const etcd_tree = {
|
||||
primary_affinity_tags?: 'nvme' | [ 'nvme', ... ],
|
||||
// scrub interval
|
||||
scrub_interval?: '30d',
|
||||
// users allowed to create images in this pool
|
||||
creator_group?: '',
|
||||
},
|
||||
...
|
||||
}, */
|
||||
@@ -224,6 +226,10 @@ const etcd_tree = {
|
||||
parent_id?: <inode_t>,
|
||||
readonly?: boolean,
|
||||
deleted?: boolean,
|
||||
enc_key?: string,
|
||||
owner?: string,
|
||||
owner_group?: string,
|
||||
reader_group?: string,
|
||||
}
|
||||
}
|
||||
}, */
|
||||
|
||||
@@ -1017,6 +1017,8 @@ void etcd_state_client_t::parse_state(const etcd_kv_t & kv)
|
||||
pc.used_for_app = "fs:"+pc.used_for_app;
|
||||
else
|
||||
pc.used_for_app = pool_item.second["used_for_app"].as_string();
|
||||
// Create group permission
|
||||
pc.creator_group = pool_item.second["creator_group"].string_value();
|
||||
// Local Read Configuration
|
||||
std::string local_reads = pool_item.second["local_reads"].string_value();
|
||||
if (local_reads == "nearest")
|
||||
@@ -1441,6 +1443,18 @@ json11::Json::object etcd_state_client_t::serialize_inode_cfg(inode_config_t *cf
|
||||
{
|
||||
new_cfg["deleted"] = true;
|
||||
}
|
||||
if (!cfg->owner.empty())
|
||||
{
|
||||
new_cfg["owner"] = cfg->owner;
|
||||
}
|
||||
if (!cfg->owner_group.empty())
|
||||
{
|
||||
new_cfg["owner_group"] = cfg->owner_group;
|
||||
}
|
||||
if (!cfg->reader_group.empty())
|
||||
{
|
||||
new_cfg["reader_group"] = cfg->reader_group;
|
||||
}
|
||||
if (cfg->meta.is_object())
|
||||
{
|
||||
new_cfg["meta"] = cfg->meta;
|
||||
@@ -1487,6 +1501,9 @@ inode_config_t etcd_state_client_t::deserialize_inode_cfg(uint64_t inode_num, js
|
||||
.readonly = value["readonly"].bool_value(),
|
||||
.deleted = value["deleted"].bool_value(),
|
||||
.enc_key = std::move(enc_key),
|
||||
.owner = value["owner"].string_value(),
|
||||
.owner_group = value["owner_group"].string_value(),
|
||||
.reader_group = value["reader_group"].string_value(),
|
||||
.meta = value["meta"],
|
||||
.mod_revision = mod_revision,
|
||||
};
|
||||
|
||||
@@ -69,6 +69,7 @@ struct pool_config_t
|
||||
std::map<pg_num_t, pg_config_t> pg_config;
|
||||
uint64_t scrub_interval = 0;
|
||||
std::string used_for_app;
|
||||
std::string creator_group;
|
||||
int backfillfull = 0;
|
||||
int local_reads = 0;
|
||||
|
||||
@@ -87,6 +88,8 @@ struct inode_config_t
|
||||
bool readonly = false;
|
||||
bool deleted = false;
|
||||
std::string enc_key;
|
||||
// Permissions
|
||||
std::string owner, owner_group, reader_group;
|
||||
// Arbitrary metadata
|
||||
json11::Json meta;
|
||||
// Change revision of the metadata in etcd
|
||||
|
||||
+38
-14
@@ -48,6 +48,7 @@ struct http_context_t
|
||||
|
||||
#ifdef WITH_OPENSSL
|
||||
SSL_CTX *ssl_ctx = NULL;
|
||||
std::string ssl_cn;
|
||||
#endif
|
||||
|
||||
~http_context_t()
|
||||
@@ -186,24 +187,41 @@ bool openssl_ctx_use_ca(SSL_CTX *ssl_ctx, const std::string & file_or_pem)
|
||||
: !!SSL_CTX_load_verify_locations(ssl_ctx, file_or_pem.c_str(), NULL);
|
||||
}
|
||||
|
||||
bool openssl_ctx_use_cert(SSL_CTX *ssl_ctx, const std::string & file_or_pem)
|
||||
bool openssl_ctx_use_cert(SSL_CTX *ssl_ctx, const std::string & file_or_pem, std::string & common_name)
|
||||
{
|
||||
BIO *bio = NULL;
|
||||
std::string contents;
|
||||
if (file_or_pem.substr(0, 5) == "-----")
|
||||
bio = BIO_new_mem_buf(file_or_pem.data(), file_or_pem.size());
|
||||
else
|
||||
{
|
||||
BIO *bio = BIO_new_mem_buf(file_or_pem.data(), file_or_pem.size());
|
||||
if (!bio)
|
||||
contents = read_file(file_or_pem);
|
||||
if (!contents.size())
|
||||
return false;
|
||||
X509 *x509 = PEM_read_bio_X509(bio, NULL, 0, NULL);
|
||||
bool ok = !!x509;
|
||||
if (x509)
|
||||
{
|
||||
ok = SSL_CTX_use_certificate(ssl_ctx, x509);
|
||||
X509_free(x509);
|
||||
}
|
||||
BIO_free(bio);
|
||||
return ok;
|
||||
bio = BIO_new_mem_buf(contents.data(), contents.size());
|
||||
}
|
||||
return !!SSL_CTX_use_certificate_file(ssl_ctx, file_or_pem.c_str(), SSL_FILETYPE_PEM);
|
||||
if (!bio)
|
||||
return false;
|
||||
X509 *x509 = PEM_read_bio_X509(bio, NULL, 0, NULL);
|
||||
bool ok = !!x509;
|
||||
if (x509)
|
||||
{
|
||||
ok = SSL_CTX_use_certificate(ssl_ctx, x509);
|
||||
if (ok)
|
||||
{
|
||||
X509_NAME* subj = X509_get_subject_name(x509);
|
||||
int pos = X509_NAME_get_index_by_NID(subj, NID_commonName, -1);
|
||||
if (pos != -1)
|
||||
{
|
||||
X509_NAME_ENTRY* cn = X509_NAME_get_entry(subj, pos);
|
||||
ASN1_STRING* str = X509_NAME_ENTRY_get_data(cn);
|
||||
common_name = std::string((const char*)ASN1_STRING_get0_data(str), ASN1_STRING_length(str));
|
||||
}
|
||||
}
|
||||
X509_free(x509);
|
||||
}
|
||||
BIO_free(bio);
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool openssl_ctx_use_key(SSL_CTX *ssl_ctx, const std::string & file_or_pem)
|
||||
@@ -243,6 +261,7 @@ http_context_t* http_context_init(timerfd_manager_t *tfd, const std::string & ss
|
||||
ctx->ssl_key = ssl_key;
|
||||
ctx->ssl_ca = ssl_ca;
|
||||
ctx->ssl_ctx = ssl_ctx;
|
||||
ctx->ssl_cn = "";
|
||||
if (!ssl_ctx)
|
||||
goto init_err;
|
||||
SSL_CTX_set_verify(ssl_ctx, verify_peer ? SSL_VERIFY_PEER : SSL_VERIFY_NONE, NULL);
|
||||
@@ -251,7 +270,7 @@ http_context_t* http_context_init(timerfd_manager_t *tfd, const std::string & ss
|
||||
if (!openssl_ctx_use_ca(ssl_ctx, ssl_ca))
|
||||
goto init_err;
|
||||
if (ssl_cert != "" && ssl_key != "" &&
|
||||
(!openssl_ctx_use_cert(ssl_ctx, ssl_cert) ||
|
||||
(!openssl_ctx_use_cert(ssl_ctx, ssl_cert, ctx->ssl_cn) ||
|
||||
!openssl_ctx_use_key(ssl_ctx, ssl_key)))
|
||||
goto init_err;
|
||||
#endif
|
||||
@@ -262,6 +281,11 @@ init_err:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
std::string http_context_get_ssl_cn(http_context_t *ctx)
|
||||
{
|
||||
return ctx->ssl_cn;
|
||||
}
|
||||
|
||||
struct http_ctx_resolve_t
|
||||
{
|
||||
std::function<void(const std::string & error, const std::vector<std::string> & addrs)> cb;
|
||||
|
||||
@@ -48,6 +48,7 @@ struct http_co_t;
|
||||
|
||||
http_context_t* http_context_init(timerfd_manager_t *tfd, const std::string & ssl_cert, const std::string & ssl_key,
|
||||
const std::string & ssl_ca, bool verify_peer, std::string & error);
|
||||
std::string http_context_get_ssl_cn(http_context_t *ctx);
|
||||
void http_resolve(http_context_t *ctx, bool ssl, std::string host,
|
||||
std::function<void(const std::string & error, const std::vector<std::string> & addrs)> cb);
|
||||
void http_context_destroy(http_context_t *ctx);
|
||||
|
||||
@@ -254,7 +254,7 @@ void osd_messenger_t::handle_send(int result, bool prev, bool more, osd_client_t
|
||||
{
|
||||
fprintf(stderr, "Client %ju socket write error: expected to send "
|
||||
"%zu bytes with MSG_WAITALL but sent %u. Disconnecting client\n", cl->client_id, cl->send_list_size, result);
|
||||
stop_client(cl->peer_fd);
|
||||
stop_client(cl->client_id);
|
||||
return;
|
||||
}
|
||||
for (auto op: cl->send_free_ops)
|
||||
|
||||
+12
-5
@@ -49,6 +49,9 @@ static const char* help_text =
|
||||
" --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"
|
||||
" --enc-key vault:ID Use an encryption key from an external Vault secret with specified ID.\n"
|
||||
" --owner username Set owner (default is the current user from TLS certificate).\n"
|
||||
" --owner_group name Set owner group name.\n"
|
||||
" --reader_group rdr Set reader group name.\n"
|
||||
"\n"
|
||||
"vitastor-cli create --snapshot <snapshot> [OPTIONS] <image>\n"
|
||||
"vitastor-cli snap-create [OPTIONS] <image>@<snapshot>\n"
|
||||
@@ -63,10 +66,13 @@ static const char* help_text =
|
||||
" Rename, resize image or change its readonly status. Images with children can't be made read-write.\n"
|
||||
" If the new size is smaller than the old size, extra data will be purged.\n"
|
||||
" You should resize file system in the image, if present, before shrinking it.\n"
|
||||
" --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"
|
||||
" --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"
|
||||
" --owner username Change image owner.\n"
|
||||
" --owner_group name Change image owner group name.\n"
|
||||
" --reader_group rdr Change image reader group name.\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"
|
||||
@@ -205,6 +211,7 @@ static const char* help_text =
|
||||
" --used_for_app s3:<name> Mark pool as used for S3 location with name <name>\n"
|
||||
" --pg_stripe_size <number> Increase object grouping stripe\n"
|
||||
" --max_osd_combinations 10000 Maximum number of random combinations for LP solver input\n"
|
||||
" --creator_group <group> User group allowed to create images in this pool.\n"
|
||||
" --wait Wait for the new pool to come online\n"
|
||||
" -f|--force Do not check that cluster has enough OSDs to create the pool\n"
|
||||
" Examples:\n"
|
||||
@@ -216,7 +223,7 @@ static const char* help_text =
|
||||
" [-s|--pg_size <number>] [--pg_minsize <number>] [-n|--pg_count <count>]\n"
|
||||
" [--failure_domain <level>] [--root_node <node>] [--osd_tags <tags>] [--used_for_app <type>:<name>]\n"
|
||||
" [--max_osd_combinations <number>] [--primary_affinity_tags <tags>] [--scrub_interval <time>]\n"
|
||||
" [--level_placement <rules>] [--raw_placement <rules>]\n"
|
||||
" [--level_placement <rules>] [--raw_placement <rules>] [--creator_group <group>]\n"
|
||||
" Non-modifiable parameters (changing them WILL lead to data loss):\n"
|
||||
" [--block_size <size>] [--bitmap_granularity <size>]\n"
|
||||
" [--immediate_commit <all|small|none>] [--pg_stripe_size <size>]\n"
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include <ctype.h>
|
||||
#include "cli.h"
|
||||
#include "http_client.h"
|
||||
#include "cluster_client.h"
|
||||
#include "str_util.h"
|
||||
|
||||
@@ -35,6 +36,7 @@ struct image_creator_t
|
||||
bool force_size = false;
|
||||
std::string enc_key;
|
||||
bool set_key = false;
|
||||
std::string new_owner, new_owner_group, new_reader_group;
|
||||
|
||||
pool_id_t old_pool_id = 0;
|
||||
inode_t new_parent_id = 0;
|
||||
@@ -442,6 +444,19 @@ resume_3:
|
||||
{
|
||||
new_cfg.enc_key = cur_cfg.enc_key;
|
||||
}
|
||||
new_cfg.owner = http_context_get_ssl_cn(parent->cli->st_cli.get_http_ctx());
|
||||
if (!new_owner.empty())
|
||||
{
|
||||
new_cfg.owner = new_owner;
|
||||
}
|
||||
if (!new_owner_group.empty())
|
||||
{
|
||||
new_cfg.owner_group = new_owner_group;
|
||||
}
|
||||
if (!new_reader_group.empty())
|
||||
{
|
||||
new_cfg.reader_group = new_reader_group;
|
||||
}
|
||||
json11::Json::array checks = json11::Json::array {
|
||||
json11::Json::object {
|
||||
{ "target", "VERSION" },
|
||||
@@ -604,6 +619,9 @@ std::function<bool(cli_result_t &)> cli_tool_t::start_create(json11::Json cfg)
|
||||
}
|
||||
}
|
||||
}
|
||||
image_creator->new_owner = cfg["owner"].string_value();
|
||||
image_creator->new_owner_group = cfg["owner_group"].string_value();
|
||||
image_creator->new_reader_group = cfg["reader_group"].string_value();
|
||||
image_creator->new_parent = cfg["parent"].string_value();
|
||||
if (!cfg["size"].is_null())
|
||||
{
|
||||
|
||||
@@ -19,6 +19,7 @@ struct image_changer_t
|
||||
bool set_deleted = false, new_deleted = false;
|
||||
bool set_key = false;
|
||||
std::string enc_key;
|
||||
json11::Json new_owner, new_owner_group, new_reader_group;
|
||||
bool down_ok = false;
|
||||
// interval between fsyncs
|
||||
int fsync_interval = 128;
|
||||
@@ -151,6 +152,18 @@ resume_1:
|
||||
{
|
||||
cfg.name = new_name;
|
||||
}
|
||||
if (new_owner.is_string())
|
||||
{
|
||||
cfg.owner = new_owner.string_value();
|
||||
}
|
||||
if (new_owner_group.is_string())
|
||||
{
|
||||
cfg.owner_group = new_owner_group.string_value();
|
||||
}
|
||||
if (new_reader_group.is_string())
|
||||
{
|
||||
cfg.reader_group = new_reader_group.string_value();
|
||||
}
|
||||
if (set_key)
|
||||
{
|
||||
if (!force)
|
||||
@@ -278,6 +291,9 @@ std::function<bool(cli_result_t &)> cli_tool_t::start_modify(json11::Json cfg)
|
||||
if (!changer->fsync_interval)
|
||||
changer->fsync_interval = 128;
|
||||
changer->down_ok = cfg["down_ok"].bool_value();
|
||||
changer->new_owner = cfg["owner"];
|
||||
changer->new_owner_group = cfg["owner_group"];
|
||||
changer->new_reader_group = cfg["reader_group"];
|
||||
// FIXME Check that the image doesn't have children when shrinking
|
||||
return [changer](cli_result_t & result)
|
||||
{
|
||||
|
||||
@@ -91,7 +91,7 @@ std::string validate_pool_config(json11::Json::object & new_cfg, json11::Json ol
|
||||
}
|
||||
else if (key == "name" || key == "scheme" || key == "immediate_commit" ||
|
||||
key == "failure_domain" || key == "root_node" || key == "scrub_interval" || key == "used_for_app" ||
|
||||
key == "used_for_fs" || key == "raw_placement" || key == "local_reads")
|
||||
key == "used_for_fs" || key == "raw_placement" || key == "local_reads" || key == "creator_group")
|
||||
{
|
||||
if (!value.is_string())
|
||||
{
|
||||
|
||||
@@ -108,7 +108,10 @@
|
||||
{ "type": "string", "enum": [ "", "random" ] },
|
||||
{ "type": "string", "pattern": "^[0-9a-fA-F]{128}$|^vault:" }
|
||||
]
|
||||
}
|
||||
},
|
||||
"owner": { "type": "string", "description": "Set image owner" },
|
||||
"owner_group": { "type": "string", "description": "Set image owner group" },
|
||||
"reader_group": { "type": "string", "description": "Set image reader group" }
|
||||
}
|
||||
} } } },
|
||||
"responses": {
|
||||
@@ -146,6 +149,9 @@
|
||||
{ "type": "string", "pattern": "^[0-9a-fA-F]{128}$|^vault:" }
|
||||
]
|
||||
},
|
||||
"owner": { "type": "string", "description": "Set image owner" },
|
||||
"owner_group": { "type": "string", "description": "Set image owner group" },
|
||||
"reader_group": { "type": "string", "description": "Set image reader group" },
|
||||
"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" }
|
||||
}
|
||||
@@ -812,7 +818,8 @@
|
||||
"scrub_interval": { "type": "string", "pattern": "^\\d+[smhdMy]$", "description": "Automatic scrub interval" },
|
||||
"level_placement": { "type": "string", "description": "Additional failure domain rules" },
|
||||
"raw_placement": { "type": "string", "description": "Raw PG generation rules" },
|
||||
"max_osd_combinations": { "type": "integer", "format": "uint64", "description": "Maximum number of random combinations during PG generation" }
|
||||
"max_osd_combinations": { "type": "integer", "format": "uint64", "description": "Maximum number of random combinations during PG generation" },
|
||||
"creator_group": { "type": "string", "description": "User group allowed to create images in this pool" }
|
||||
}
|
||||
},
|
||||
"PoolList": {
|
||||
|
||||
Reference in New Issue
Block a user