]> git.apps.os.sepia.ceph.com Git - ceph-ci.git/commitdiff
rgw: Vault's Transit Secrets updates and docs
authorAndrea Baglioni <andrea.baglioni@workday.com>
Tue, 5 Nov 2019 11:21:06 +0000 (11:21 +0000)
committerAndrea Baglioni <andrea.baglioni@workday.com>
Thu, 28 Nov 2019 09:19:39 +0000 (09:19 +0000)
  * Drop polymorfism for KMS class
  * Fix issue in kms-key selection
  * Update documentation for Vault section

Signed-off-by: Andrea Baglioni <andrea.baglioni@workday.com>
Signed-off-by: Sergio de Carvalho <sergio.carvalho@workday.com>
doc/radosgw/vault.rst
src/rgw/rgw_kms.cc
src/rgw/rgw_kms.h

index d61b3d3a129a8c9ca89996200ae7958311caf8a0..a384887d3aa7fac16b0734555949561c8ed03ceb 100644 (file)
@@ -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=<value>.
+
 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 -<<EOF
+    path "transit/export/encryption-key/mybucketkey/*" {
+      capabilities = ["read"]
+    }
+  EOF
+
+Once the policy is created, a token can be generated by Vault administrator::
+
+  vault token create -policy=rgw-transit-policy
+
+Sample output::
+
+  Key                  Value
+  ---                  -----
+  token                s.62KuPujbc1234dWB71poOmIZ
+  token_accessor       jv95ZYBUFv6Ss84x7SCSy6lZ
+  token_duration       768h
+  token_renewable      true
+  token_policies       ["default" "rgw-transit-policy"]
+  identity_policies    []
+  policies             ["default" "rgw-transit-policy"]
+
+
+Configure Ceph Object Gateway
+=================================
+
+Edit the Ceph configuration file to enable Vault as a KMS for server-side
+encryption.::
+
+   rgw crypt s3 kms backend = vault
+   rgw crypt vault auth = { token | agent }
+   rgw crypt vault addr = { vault address e.g. http://localhost:8200 }
+   rgw crypt vault prefix = { prefix to secret engine e.g. /v1/transit/export/encryption-key }
+   rgw crypt vault token file = { absolute path to token file e.g. /etc/ceph/vault.token }
+
+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.
+In this example, the Gateway will only fetch transit encryption keys::
+
+   rgw crypt s3 kms backend = vault
+   rgw crypt vault auth = token
+   rgw crypt vault addr = https://vaultserver
+   rgw crypt vault secret engine = transit
+   rgw crypt vault prefix = /v1/transit/export/encryption-key
+   rgw crypt vault token file = /etc/ceph/vault.token
+
 
 Upload object
 =============
@@ -132,4 +220,6 @@ above, the Gateway will fetch the secret from::
 .. _Server-Side Encryption: ../encryption
 .. _Vault: https://www.vaultproject.io/docs/
 .. _token authentication method: https://www.vaultproject.io/docs/auth/token.html
+.. _vault agent: https://www.vaultproject.io/docs/agent/index.html
 .. _KV Secrets engine: https://www.vaultproject.io/docs/secrets/kv/
+.. _Transit engine: https://www.vaultproject.io/docs/secrets/transit
index 22bda18187d12baedc96c28cf5df3f7c37c7f587..bd9a8d94f88ec3d85beda83268d0ea7371c20907 100644 (file)
@@ -174,13 +174,12 @@ class TransitSecretEngine: public VaultSecretEngine {
 private:
   int get_key_version(boost::string_view key_id, string& version)
   {
-    vector<boost::string_view> 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<std::string, 3> 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<string,string> get_str_map(const string &str) {
   map<string,string> 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,
index b75ec91f82c1e2aa40d0ef3bff3452a8eca0b8cd..bffebab8e69a75e57b9109ebda6e052c01b690f7 100644 (file)
@@ -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