Commit ac4a032b authored by Julius Goryavsky's avatar Julius Goryavsky

MDEV-30406: Allow usage of namespaces in Hashicorp vault encryption plugin

This commit adds namespaces support for the Hashicorp Key Management plugin.
parent eaafb7eb
...@@ -30,6 +30,12 @@ ...@@ -30,6 +30,12 @@
# #
#hashicorp-key-management-vault-url="<url>" #hashicorp-key-management-vault-url="<url>"
#
# An optional parameter that specifies the namespace
# to use for all API calls:
#
#hashicorp-key-management-namespace="<namespace>"
# #
# Authentication token that passed to the Hashicorp Vault # Authentication token that passed to the Hashicorp Vault
# in the request header: # in the request header:
......
...@@ -93,6 +93,28 @@ operation: ...@@ -93,6 +93,28 @@ operation:
By default, the path is not set, therefore you must By default, the path is not set, therefore you must
replace with the correct path to your secrets. replace with the correct path to your secrets.
--[loose-]hashicorp-key-management-namespace="<namespace>"
An optional parameter that specifies the namespace
to use for all API calls. Alternatively, the namespace
can be part of the secrets path. That is:
--hashicorp-key-management-vault-url="https://127.0.0.1:8200/v1/ns1/ns2/my_secrets"
is equivalent to:
--hashicorp-key-management-vault-url="https://127.0.0.1:8200/v1/my_secrets"
--hashicorp-key-management-namespace="ns1/ns2"
and is equivalent to:
--hashicorp-key-management-vault-url="https://127.0.0.1:8200/v1/ns2/my_secrets"
--hashicorp-key-management-namespace="ns1"
The plugin also supports the standard Hashicorp environment
variable VAULT_NAMESPACE, and issues a warning if its value
does not match the configuration parameter.
--[loose-]hashicorp-key-management-token="<token>" --[loose-]hashicorp-key-management-token="<token>"
Authentication token that passed to the Hashicorp Vault Authentication token that passed to the Hashicorp Vault
...@@ -102,6 +124,11 @@ operation: ...@@ -102,6 +124,11 @@ operation:
so you must specify the correct value for it, otherwise so you must specify the correct value for it, otherwise
the Hashicorp Vault server will refuse authorization. the Hashicorp Vault server will refuse authorization.
Alternatively, the plugin also supports the standard
Hashicorp environment variable VAULT_TOKEN (if set),
and issues a warning if its value does not match the
configuration parameter.
--[loose-]hashicorp-key-management-vault-ca="<path>" --[loose-]hashicorp-key-management-vault-ca="<path>"
Path to the Certificate Authority (CA) bundle (is a file Path to the Certificate Authority (CA) bundle (is a file
......
...@@ -75,7 +75,9 @@ class HCData ...@@ -75,7 +75,9 @@ class HCData
char *vault_url_data; char *vault_url_data;
size_t vault_url_len; size_t vault_url_len;
char *local_token; char *local_token;
char *local_namespace;
char *token_header; char *token_header;
char *namespace_header;
bool curl_inited; bool curl_inited;
public: public:
HCData() HCData()
...@@ -83,7 +85,9 @@ class HCData ...@@ -83,7 +85,9 @@ class HCData
vault_url_data(NULL), vault_url_data(NULL),
vault_url_len(0), vault_url_len(0),
local_token(NULL), local_token(NULL),
local_namespace(NULL),
token_header(NULL), token_header(NULL),
namespace_header(NULL),
curl_inited(false) curl_inited(false)
{} {}
unsigned int get_latest_version (unsigned int key_id); unsigned int get_latest_version (unsigned int key_id);
...@@ -110,11 +114,21 @@ class HCData ...@@ -110,11 +114,21 @@ class HCData
free(vault_url_data); free(vault_url_data);
vault_url_data = NULL; vault_url_data = NULL;
} }
if (namespace_header)
{
free(namespace_header);
namespace_header = NULL;
}
if (token_header) if (token_header)
{ {
free(token_header); free(token_header);
token_header = NULL; token_header = NULL;
} }
if (local_namespace)
{
free(local_namespace);
local_namespace = NULL;
}
if (local_token) if (local_token)
{ {
free(local_token); free(local_token);
...@@ -337,6 +351,7 @@ unsigned int HCData::cache_check_version (unsigned int key_id) ...@@ -337,6 +351,7 @@ unsigned int HCData::cache_check_version (unsigned int key_id)
} }
static char* vault_url; static char* vault_url;
static char* vault_namespace;
static char* token; static char* token;
static char* vault_ca; static char* vault_ca;
static int timeout; static int timeout;
...@@ -358,6 +373,11 @@ static MYSQL_SYSVAR_STR(vault_url, vault_url, ...@@ -358,6 +373,11 @@ static MYSQL_SYSVAR_STR(vault_url, vault_url,
"HTTP[s] URL that is used to connect to the Hashicorp Vault server", "HTTP[s] URL that is used to connect to the Hashicorp Vault server",
NULL, NULL, ""); NULL, NULL, "");
static MYSQL_SYSVAR_STR(namespace, vault_namespace,
PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY,
"Namespace that is used in interactions with the Hashicorp Vault server",
NULL, NULL, "");
static MYSQL_SYSVAR_STR(token, token, static MYSQL_SYSVAR_STR(token, token,
PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY | PLUGIN_VAR_NOSYSVAR, PLUGIN_VAR_RQCMDARG | PLUGIN_VAR_READONLY | PLUGIN_VAR_NOSYSVAR,
"Authentication token that passed to the Hashicorp Vault " "Authentication token that passed to the Hashicorp Vault "
...@@ -425,6 +445,7 @@ static MYSQL_SYSVAR_BOOL(use_cache_on_timeout, use_cache_on_timeout, ...@@ -425,6 +445,7 @@ static MYSQL_SYSVAR_BOOL(use_cache_on_timeout, use_cache_on_timeout,
static struct st_mysql_sys_var *settings[] = { static struct st_mysql_sys_var *settings[] = {
MYSQL_SYSVAR(vault_url), MYSQL_SYSVAR(vault_url),
MYSQL_SYSVAR(namespace),
MYSQL_SYSVAR(token), MYSQL_SYSVAR(token),
MYSQL_SYSVAR(vault_ca), MYSQL_SYSVAR(vault_ca),
MYSQL_SYSVAR(timeout), MYSQL_SYSVAR(timeout),
...@@ -966,7 +987,8 @@ int HCData::init () ...@@ -966,7 +987,8 @@ int HCData::init ()
local_token = token; local_token = token;
} }
} }
if (token_len == 0) { if (token_len == 0)
{
my_printf_error(ER_UNKNOWN_ERROR, PLUGIN_ERROR_HEADER my_printf_error(ER_UNKNOWN_ERROR, PLUGIN_ERROR_HEADER
"The --hashicorp-key-management-token option value " "The --hashicorp-key-management-token option value "
"or the value of the corresponding parameter in the " "or the value of the corresponding parameter in the "
...@@ -996,9 +1018,9 @@ int HCData::init () ...@@ -996,9 +1018,9 @@ int HCData::init ()
{ {
my_printf_error(ER_UNKNOWN_ERROR, PLUGIN_ERROR_HEADER my_printf_error(ER_UNKNOWN_ERROR, PLUGIN_ERROR_HEADER
"The --hashicorp-key-management-token option value " "The --hashicorp-key-management-token option value "
"or the value of the corresponding parameter is not " "or the value of the corresponding configuration "
"equal to the value of the VAULT_TOKEN environment " "parameter is not equal to the value of the "
"variable", "VAULT_TOKEN environment variable",
ME_ERROR_LOG_ONLY | ME_WARNING); ME_ERROR_LOG_ONLY | ME_WARNING);
} }
} }
...@@ -1014,6 +1036,62 @@ int HCData::init () ...@@ -1014,6 +1036,62 @@ int HCData::init ()
return 1; return 1;
} }
snprintf(token_header, buf_len, "%s%s", x_vault_token, token); snprintf(token_header, buf_len, "%s%s", x_vault_token, token);
/*
Let's construct a header for the namespace (if the corresponding
parameter or environment variable is present):
*/
char *namespace_env= getenv("VAULT_NAMESPACE");
size_t namespace_len= 0;
if (vault_namespace && *vault_namespace)
{
namespace_len = strlen(vault_namespace);
/*
Issue a warning if the VAULT_NAMESPACE environment variable
is not equal to the namespace parameter:
*/
if (namespace_env != NULL && strcmp(namespace_env, vault_namespace) != 0)
{
my_printf_error(ER_UNKNOWN_ERROR, PLUGIN_ERROR_HEADER
"The --hashicorp-key-management-namespace option "
"value or the value of the corresponding configuration "
"parameter is not equal to the value of the "
"VAULT_NAMESPACE environment variable",
ME_ERROR_LOG_ONLY | ME_WARNING);
}
}
else if (namespace_env)
{
namespace_len = strlen(namespace_env);
if (namespace_len != 0)
{
/*
The value of the namespace parameter obtained using the getenv()
system call, which does not guarantee that the memory pointed
to by the returned pointer can be read in the long term (for
example, after changing the values of the environment variables
of the current process). Therefore, we need to copy the namespace
value to the working buffer:
*/
if (!(vault_namespace = (char *) alloc(namespace_len + 1)))
{
return 1;
}
memcpy(vault_namespace, namespace_env, namespace_len + 1);
local_namespace = vault_namespace;
}
}
if (namespace_len)
{
const static char *x_vault_namespace = "X-Vault-Namespace:";
const static size_t x_vault_namespace_len = strlen(x_vault_namespace);
size_t ns_buf_len = x_vault_namespace_len + namespace_len + 1;
if (!(namespace_header = (char *) alloc(ns_buf_len)))
{
return 1;
}
snprintf(namespace_header, ns_buf_len, "%s%s",
x_vault_namespace, vault_namespace);
}
/* We need to check that the path inside the URL starts with "/v1/": */ /* We need to check that the path inside the URL starts with "/v1/": */
const char *suffix = strchr(vault_url, '/'); const char *suffix = strchr(vault_url, '/');
if (suffix == NULL) if (suffix == NULL)
...@@ -1171,6 +1249,16 @@ int HCData::init () ...@@ -1171,6 +1249,16 @@ int HCData::init ()
"curl: unable to construct slist", 0); "curl: unable to construct slist", 0);
return 1; return 1;
} }
if (namespace_header)
{
slist = curl_slist_append(slist, namespace_header);
if (slist == NULL)
{
my_printf_error(ER_UNKNOWN_ERROR, PLUGIN_ERROR_HEADER
"curl: unable to append to slist", 0);
return 1;
}
}
/* /*
If we do not need to check the key-value storage version, If we do not need to check the key-value storage version,
then we immediately return from this function: then we immediately return from this function:
...@@ -1184,8 +1272,6 @@ int HCData::init () ...@@ -1184,8 +1272,6 @@ int HCData::init ()
char *mount_url = (char *) alloc(vault_url_len + 11 + 6); char *mount_url = (char *) alloc(vault_url_len + 11 + 6);
if (mount_url == NULL) if (mount_url == NULL)
{ {
my_printf_error(ER_UNKNOWN_ERROR, PLUGIN_ERROR_HEADER
"Memory allocation error", 0);
return 1; return 1;
} }
/* /*
......
...@@ -5,6 +5,7 @@ hashicorp_key_management_cache_version_timeout 0 ...@@ -5,6 +5,7 @@ hashicorp_key_management_cache_version_timeout 0
hashicorp_key_management_caching_enabled ON hashicorp_key_management_caching_enabled ON
hashicorp_key_management_check_kv_version OFF hashicorp_key_management_check_kv_version OFF
hashicorp_key_management_max_retries 3 hashicorp_key_management_max_retries 3
hashicorp_key_management_namespace
hashicorp_key_management_timeout 60 hashicorp_key_management_timeout 60
hashicorp_key_management_use_cache_on_timeout OFF hashicorp_key_management_use_cache_on_timeout OFF
hashicorp_key_management_vault_ca hashicorp_key_management_vault_ca
......
...@@ -5,6 +5,7 @@ hashicorp_key_management_cache_version_timeout 0 ...@@ -5,6 +5,7 @@ hashicorp_key_management_cache_version_timeout 0
hashicorp_key_management_caching_enabled ON hashicorp_key_management_caching_enabled ON
hashicorp_key_management_check_kv_version OFF hashicorp_key_management_check_kv_version OFF
hashicorp_key_management_max_retries 3 hashicorp_key_management_max_retries 3
hashicorp_key_management_namespace
hashicorp_key_management_timeout 60 hashicorp_key_management_timeout 60
hashicorp_key_management_use_cache_on_timeout OFF hashicorp_key_management_use_cache_on_timeout OFF
hashicorp_key_management_vault_ca hashicorp_key_management_vault_ca
......
# restart: with restart_parameters
select @@hashicorp_key_management_namespace;
@@hashicorp_key_management_namespace
"ns1"
CREATE TABLE t1 (a VARCHAR(8)) ENGINE=InnoDB ENCRYPTED=YES ENCRYPTION_KEY_ID=11;
INSERT INTO t1 VALUES ('foo'),('bar');
# restart: with restart_parameters
select @@hashicorp_key_management_namespace;
@@hashicorp_key_management_namespace
"ns1"
CREATE TABLE t2 (a VARCHAR(8)) ENGINE=InnoDB ENCRYPTED=YES ENCRYPTION_KEY_ID=12;
INSERT INTO t2 VALUES ('foo'),('bar');
SELECT * FROM t1;
a
foo
bar
SELECT * FROM t2;
a
foo
bar
DROP TABLE t1, t2;
# restart
# MDEV-28330: Key caching doesn't appear to be working
# The test presumes that the local vault is running at $VAULT_ADDR,
# and the token is configured in $VAULT_TOKEN.
--source include/have_innodb.inc
--source hashicorp_plugin.inc
--exec vault namespace create ns1 > /dev/null
--exec vault secrets disable -namespace ns1 test1 > /dev/null
--exec vault secrets enable -namespace ns1 -path /test1 -version=2 kv > /dev/null
--exec vault kv put -namespace ns1 /test1/11 data=01234567890123456789012345678901 > /dev/null
--exec vault kv put -namespace ns1 /test1/12 data=01234567890123456789012345678904 > /dev/null
--let $restart_parameters=--plugin-load-add=hashicorp_key_management --hashicorp-key-management-vault-url=$VAULT_ADDR/v1/test1/ --hashicorp-key-management-token=$VAULT_TOKEN --hashicorp-key-management-namespace="ns1"
--let $restart_noprint=1
--source include/restart_mysqld.inc
select @@hashicorp_key_management_namespace;
CREATE TABLE t1 (a VARCHAR(8)) ENGINE=InnoDB ENCRYPTED=YES ENCRYPTION_KEY_ID=11;
INSERT INTO t1 VALUES ('foo'),('bar');
--source include/restart_mysqld.inc
select @@hashicorp_key_management_namespace;
CREATE TABLE t2 (a VARCHAR(8)) ENGINE=InnoDB ENCRYPTED=YES ENCRYPTION_KEY_ID=12;
INSERT INTO t2 VALUES ('foo'),('bar');
SELECT * FROM t1;
SELECT * FROM t2;
# Cleanup
DROP TABLE t1, t2;
--let $restart_parameters=
--source include/restart_mysqld.inc
--exec vault secrets disable -namespace ns1 test1 > /dev/null
--exec vault namespace delete ns1 > /dev/null
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment