From cebb48bffe7f2906c0348ade8dba60ce80e0edcb Mon Sep 17 00:00:00 2001 From: Andrea Baglioni Date: Tue, 5 Nov 2019 11:21:06 +0000 Subject: [PATCH] rgw: Vault's Transit Secrets updates and docs * Drop polymorfism for KMS class * Fix issue in kms-key selection * Update documentation for Vault section Signed-off-by: Andrea Baglioni Signed-off-by: Sergio de Carvalho --- doc/radosgw/vault.rst | 172 ++++++++++++++++++++++++++++++++---------- src/rgw/rgw_kms.cc | 71 ++++++----------- src/rgw/rgw_kms.h | 9 --- 3 files changed, 153 insertions(+), 99 deletions(-) diff --git a/doc/radosgw/vault.rst b/doc/radosgw/vault.rst index d61b3d3a129..a384887d3aa 100644 --- a/doc/radosgw/vault.rst +++ b/doc/radosgw/vault.rst @@ -5,32 +5,58 @@ HashiCorp Vault Integration HashiCorp `Vault`_ can be used as a secure key management service for `Server-Side Encryption`_ (SSE-KMS). -#. `Configure Vault`_ -#. `Configure the Ceph Object Gateway`_ -#. `Create a key in Vault`_ +#. `Vault Access`_ +#. `Secrets Engines`_ +#. `Configure Ceph Object Gateway`_ #. `Upload object`_ -Configure Vault +Vault Access +============ + +Access to Vault can be done through several authentication mechanisms. +Object Gateway supports `token authentication method`_ and `vault agent`_. + +Token Authentication +-------------------- +The Token authentication method expects a Vault token to be present in a plaintext file. You can configure +the token option in Ceph's configuration file as follows:: + + rgw crypt vault auth = token + rgw crypt vault token file = /etc/ceph/vault.token + +For security reasons, ensure the file is readable by the Object Gateway only. + +.. note:: Token authentication is not recommended configuration for production environments. + +Vault Agent +----------- +Vault Agent is a client daemon that provides authentication to Vault, manages the token renewal and caching. +With a Vault agent, it is possible to use other Vault authentication mechanism such as AppRole, AWS, Certs, JWT, Azure... + +You can enable agent authentication in Ceph's configuration file with:: + + rgw crypt vault auth = agent + rgw crypt vault addr = http://localhost:8200 + +.. note:: ``rgw_crypt_vault_token_file`` is ignored if ``rgw_crypt_vault_auth`` is set to ``agent``. + +Secrets Engines =============== +Vault provides several Secret Engines, which can store, generate, and encrypt data. Currently, the Object Gateway supports: + +- `KV Secrets engine`_ version 2 +- `Transit engine`_ + +.. important:: Examples in this section are for demonstration purposes only and assume token authentication. -Vault provides several Secret Engines, which can store, generate, and encrypt -data. Currently, the Object Gateway supports `KV Secrets engine`_ version 2 -an `KV Transit engine`_. -Basic Vault Configuration -------------------------- +KV Secrets engine +----------------- To enable the KV engine version 2 in Vault, use the Vault command line tool:: vault secrets enable kv-v2 -Analogously for the Transit Engine:: - vault secrets enable transit - -Vault also provides several authentication mechanisms. -To simplify user's interaction with Vault, the Object Gateway supports -two modes: `token authentication method`_ and `agent authentication method`_. - When authenticating using the token method, a token must be obtained for the Gateway and saved in a file as plain-text. @@ -61,31 +87,9 @@ Sample output:: identity_policies [] policies ["default" "rgw-policy"] -The actual token, displayed in the output of the above command, must be saved -in a file as plain-text. The path to this file must then be provided in the -Gateway configuration file (see section below). For security reasons, ensure -the file is readable by the Object Gateway only. - - -Configure the Ceph Object Gateway -================================= - -Edit the Ceph configuration file to enable Vault as a KMS for server-side -encryption. The following example uses the ``token`` authentication method (with -a Vault token stored in a file), sets the Vault server address, and restricts -the URLs where encryption keys can be retrieved from Vault using a path prefix:: - rgw crypt s3 kms backend = vault - rgw crypt vault auth = token - rgw crypt vault addr = http://vaultserver:8200 - rgw crypt vault prefix = /v1/secret/data - rgw crypt vault token file = /etc/ceph/vault.token - -In this example, the Gateway will only fetch encryption keys under -``http://vaultserver:8200/v1/secret/data``. - -Create a key in Vault -===================== +Create a key using KV Engine +---------------------------- .. note:: Server-side encryption keys must be 256-bit long and base64 encoded. @@ -95,6 +99,8 @@ use the commands below:: export VAULT_ADDR='http://vaultserver:8200' vault kv put secret/myproject/mybucketkey key=$(openssl rand -base64 32) +.. important:: In the KV secrets engine, secrets are stored as key-value pairs, and the Gateway expects the key name to be key, i.e. the secret must be in the form key=. + Output:: ====== Metadata ====== @@ -108,7 +114,89 @@ Output:: === Data === Key Value --- ----- - key Ak5dRyLQjwX/wb7vo6Fq1qjsfk1dh2CiSicX+gLAhwk= + key Ak5dRyLQjwX/wb7vo6Fq1qjsfk1dh2CiSicX-gLAhwk= + + +Transit Secrets Engine +---------------------- +To enable the Transit engine in Vault, use the Vault command line tool:: + + vault secrets enable transit + +Create a key using Transit secrets engine +----------------------------------------- +Object Gateway supports Transit engine exportable keys only. +To create an exportable key, use the command line tool:: + + export VAULT_ADDR='http://vaultserver:8200' + vault write -f transit/keys/mybucketkey exportable=true + +The command line above creates a keyring, which contains an aes256-gcm96 key type. +To verify the key is created properly, use the command line tool:: + + vault read transit/export/encryption-key/mybucketkey/1 + +Output:: + + Key Value + --- ----- + keys map[1:-gbTI9lNpqv/V/2lDcmH2Nq1xKn6FPDWarCmFM2aNsQ=] + name mybucketkey + type aes256-gcm96 + +.. note:: To use Transit Secrets engine in Object Gateway, you shall specify the full key, including its version. + +For security reasons, the Object Gateway should be given a Vault token with a +restricted policy that allows it to fetch the dedicated keyrings only. Such a policy can be +created in Vault using the Vault command line utility:: + + vault policy write rgw-transit-policy -< tokens; size_t pos = 0; - pos = key_id.rfind("/", key_id.length()); + pos = key_id.rfind("/"); if (pos != std::string::npos){ - boost::string_view token = key_id.substr(pos, key_id.length()); - if (tokens.size() >= 1 && token.find_first_not_of("0123456789") == std::string::npos){ + boost::string_view token = key_id.substr(pos+1, key_id.length()-pos); + if (!token.empty() && token.find_first_not_of("0123456789") == std::string::npos){ version.assign(std::string(token)); return 0; } @@ -193,7 +192,7 @@ public: int get_key(boost::string_view key_id, std::string& actual_key) { - JSONParser* parser = new JSONParser(); + JSONParser parser; string version; if (get_key_version(key_id, version) < 0){ @@ -201,12 +200,12 @@ public: return -EINVAL; } - int res = send_request(key_id, parser); + int res = send_request(key_id, &parser); if (res < 0) { return res; } - JSONObj* json_obj = &(*parser); + JSONObj* json_obj = &parser; std::array elements = {"data", "keys", version}; for(const auto& elem : elements) { json_obj = json_obj->find_obj(elem); @@ -251,43 +250,6 @@ public: }; -class VaultKMS : KMS { - -protected: - CephContext *cct; - VaultSecretEngine *engine; - -public: - int get_key(boost::string_view key_id, std::string& actual_key) { - if (!engine){ - ldout(cct, 5) << "ERROR: No supported Vault Secret Engine found" << dendl; - return -EINVAL; - } - return engine->get_key(key_id, actual_key); - } - - VaultKMS(CephContext *cct) { - std::string secret_engine = cct->_conf->rgw_crypt_vault_secret_engine; - - ldout(cct, 10) << "Selected " << secret_engine << " secret engine" << dendl; - - if (RGW_SSE_KMS_VAULT_SE_KV == secret_engine) - engine = new KvSecretEngine(cct); - else if (RGW_SSE_KMS_VAULT_SE_TRANSIT == secret_engine) - engine = new TransitSecretEngine(cct); - else - ldout(cct, 0) << "Missing or invalid secret engine" << dendl; - - this->cct = cct; - } - - ~VaultKMS(){ - delete engine; - } - -}; - - static map get_str_map(const string &str) { map m; get_str_map(str, &m, ";, \t"); @@ -401,14 +363,25 @@ static int get_actual_key_from_vault(CephContext *cct, boost::string_view key_id, std::string& actual_key) { + std::string secret_engine = cct->_conf->rgw_crypt_vault_secret_engine; ldout(cct, 20) << "Vault authentication method: " << cct->_conf->rgw_crypt_vault_auth << dendl; - ldout(cct, 20) << "Vault Secrets Engine: " << cct->_conf->rgw_crypt_vault_secret_engine << dendl; - VaultKMS *kms = new VaultKMS(cct); - int res = kms->get_key(key_id, actual_key); - delete kms; - return res; + ldout(cct, 20) << "Vault Secrets Engine: " << secret_engine << dendl; + + if (RGW_SSE_KMS_VAULT_SE_KV == secret_engine){ + KvSecretEngine engine(cct); + return engine.get_key(key_id, actual_key); + } + else if (RGW_SSE_KMS_VAULT_SE_TRANSIT == secret_engine){ + TransitSecretEngine engine(cct); + return engine.get_key(key_id, actual_key); + } + else{ + ldout(cct, 0) << "Missing or invalid secret engine" << dendl; + return -EINVAL; + } } + int get_actual_key_from_kms(CephContext *cct, boost::string_view key_id, boost::string_view key_selector, diff --git a/src/rgw/rgw_kms.h b/src/rgw/rgw_kms.h index b75ec91f82c..bffebab8e69 100644 --- a/src/rgw/rgw_kms.h +++ b/src/rgw/rgw_kms.h @@ -47,13 +47,4 @@ protected: virtual int send_request(boost::string_view key_id, JSONParser* parser) = 0; virtual int decode_secret(JSONObj* json_obj, std::string& actual_key) = 0; }; - -class KMS { - -public: - virtual int get_key(boost::string_view key_id, std::string& actual_key) = 0; - virtual ~KMS(){}; -}; - - #endif -- 2.39.5