]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
rgw: improvements to SSE-KMS with Vault 31025/head
authorSergio de Carvalho <scarvalhojr@gmail.com>
Mon, 14 Oct 2019 10:39:45 +0000 (11:39 +0100)
committerSergio de Carvalho <scarvalhojr@gmail.com>
Tue, 12 Nov 2019 13:51:25 +0000 (13:51 +0000)
* add 'rgw crypt vault prefix' config setting to allow restricting
  secret space in Vault where RGW can retrieve keys from
* refuse Vault token file if permissions are too open
* improve concatenation of URL paths to avoid constructing an invalid
  URL (missing or double '/')
* doc: clarify SSE-KMS keys must be 256-bit long and base64 encoded,
  document Vault policies and tokens, plus other minor doc improvements
* qa: check SHA256 signature of Vault zip download
* qa: fix teuthology tests broken by previous PR which made SSE-KMS
  backend default to Barbican

Signed-off-by: Andrea Baglioni <andrea.baglioni@workday.com>
Signed-off-by: Sergio de Carvalho <sergio.carvalho@workday.com>
15 files changed:
doc/radosgw/barbican.rst
doc/radosgw/config-ref.rst
doc/radosgw/encryption.rst
doc/radosgw/vault.rst
qa/suites/rgw/crypt/2-kms/vault.yaml
qa/suites/rgw/crypt/4-tests/s3tests.yaml
qa/suites/smoke/basic/tasks/rgw_ec_s3tests.yaml
qa/suites/smoke/basic/tasks/rgw_s3tests.yaml
qa/tasks/rgw.py
qa/tasks/vault.py
src/common/legacy_config_opts.h
src/common/options.cc
src/rgw/rgw_kms.cc
src/test/rgw/test_rgw_kms.cc
src/vstart.sh

index 6205567076311c7e5fff1d93e3555d6cfec701a9..a90d063fb970554676df962c95442e5f15bc2595 100644 (file)
@@ -39,6 +39,8 @@ Create a key in Barbican
 See Barbican documentation for `How to Create a Secret`_. Requests to
 Barbican must include a valid Keystone token in the ``X-Auth-Token`` header.
 
+.. note:: Server-side encryption keys must be 256-bit long and base64 encoded.
+
 Example request::
 
    POST /v1/secrets HTTP/1.1
index 15d21b0f3b70a7cadf46207c83314f184a678d7d..c3f17d62824a94c84359f12983e7d670b5df1fa0 100644 (file)
@@ -953,7 +953,7 @@ Barbican Settings
 HashiCorp Vault Settings
 ========================
 
-``rgw crypt vault auth```
+``rgw crypt vault auth``
 
 :Description: Type of authentication method to be used. The only method
               currently supported is ``token``.
@@ -969,7 +969,14 @@ HashiCorp Vault Settings
 
 ``rgw crypt vault addr``
 
-:Description: Base URL to the Vault server.
+:Description: Vault server base address, e.g. ``http://vaultserver:8200``.
+:Type: String
+:Default: None
+
+``rgw crypt vault prefix``
+
+:Description: The Vault secret URL prefix, which can be used to restrict access
+              to a particular subset of the secret space, e.g. ``/v1/secret/data``.
 :Type: String
 :Default: None
 
index 151f3b5efe5dfaabe6aa6e6f4935842b872c94f5..124b57b216969dc585ce1c24da9ab67bb22ca4ca 100644 (file)
@@ -14,6 +14,8 @@ Object Gateway stores that data in the Ceph Storage Cluster in encrypted form.
           for SSL termination, ``rgw trust forwarded https`` must be enabled
           before forwarded requests will be trusted as secure.
 
+.. note:: Server-side encryption keys must be 256-bit long and base64 encoded.
+
 Customer-Provided Keys
 ======================
 
index d14b455c7e79c737623f142f591e2fa5a3a8bd5c..6bd1a9081f7640631f6ab2dfc110ea70bad49688 100644 (file)
@@ -5,34 +5,85 @@ HashiCorp Vault Integration
 HashiCorp `Vault`_ can be used as a secure key management service for
 `Server-Side Encryption`_ (SSE-KMS).
 
-#. `Vault authentication`_
-#. `Create a key in Vault`_
+#. `Configure Vault`_
 #. `Configure the Ceph Object Gateway`_
+#. `Create a key in Vault`_
 #. `Upload object`_
 
-Vault authentication
-====================
+Configure Vault
+===============
+
+Vault provides several Secret Engines, which can store, generate, and encrypt
+data. Currently, the Object Gateway supports the `KV Secrets engine`_ version 2
+only. To enable the KV engine version 2 in Vault, use the Vault command line
+tool::
+
+  vault secrets enable kv-v2
+
+Vault also provides several authentication mechanisms. Currently, the Object
+Gateway supports the `token authentication method`_ only. When authenticating
+using the token method, a token must be obtained for the Gateway and saved in a
+file as plain-text.
+
+For security reasons, the Object Gateway should be given a Vault token with a
+restricted policy that allows it to fetch secrets only. Such a policy can be
+created in Vault using the Vault command line utility::
+
+  vault policy write rgw-policy -<<EOF
+    path "secret/data/*" {
+      capabilities = ["read"]
+    }
+  EOF
+
+A token with the policy above can be created by a Vault administrator as
+follows::
+
+  vault token create -policy=rgw-policy
+
+Sample output::
+
+  Key                  Value
+  ---                  -----
+  token                s.72KuPujbc065OdWB71poOmIq
+  token_accessor       jv95ZYBUFv6Ss84x7SCSy6lZ
+  token_duration       768h
+  token_renewable      true
+  token_policies       ["default" "rgw-policy"]
+  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::
 
-Vault provides several authentication mechanisms. Currently, the Object Gateway
-supports the `token authentication method`_ only.
+   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
 
-When authenticating with Vault using the token method, save the token in a
-plain-text file. The path to this file must be provided in the Gateway
-configuration file (see below). For security reasons, ensure the file is
-readable by the Object Gateway only.
+In this example, the Gateway will only fetch encryption keys under
+``http://vaultserver:8200/v1/secret/data``.
 
 Create a key in Vault
 =====================
 
-Generate and save a 256-bit key in Vault. Vault provides several Secret
-Engines, which store, generate, and encrypt data. Currently, the only secret
-engine supported is the `KV Secrets engine`_ version 2.
+.. note:: Server-side encryption keys must be 256-bit long and base64 encoded.
 
-To create a key in the KV version 2 engine using Vault's command line client,
+To create a key in the KV version 2 engine using Vault's command line tool,
 use the commands below::
 
   export VAULT_ADDR='http://vaultserver:8200'
-  vault kv put secret/myproject/mybucketkey key=$(dd bs=32 count=1 if=/dev/urandom of=/dev/stdout 2>/dev/null | base64)
+  vault kv put secret/myproject/mybucketkey key=$(openssl rand -base64 32)
 
 Output::
 
@@ -49,32 +100,24 @@ Output::
   ---    -----
   key    Ak5dRyLQjwX/wb7vo6Fq1qjsfk1dh2CiSicX+gLAhwk=
 
-The URL to the secret in Vault must be provided in the Gateway configuration
-file (see below).
-
-Configure the 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
-   rgw crypt vault addr = http://vaultserver:8200
-   rgw crypt vault token file = /path/to/token.file
-
 Upload object
 =============
 
 When uploading an object, provide the SSE key ID in the request. As an example,
 using the AWS command-line client::
 
-  aws --endpoint=http://radosgw:8000 s3 cp plaintext.txt s3://mybucket/encrypted.txt --sse=aws:kms --sse-kms-key-id /v1/secret/data/myproject/mybucketkey
+  aws --endpoint=http://radosgw:8000 s3 cp plaintext.txt s3://mybucket/encrypted.txt --sse=aws:kms --sse-kms-key-id myproject/mybucketkey
+
+The Object Gateway will fetch the key from Vault, encrypt the object and store
+it in the bucket. Any request to downlod the object will require the correct key
+ID for the Gateway to successfully decrypt it.
+
+Note that the secret will be fetched from Vault using a URL constructed by
+concatenating the base address (``rgw crypt vault addr``), the (optional)
+URL prefix (``rgw crypt vault prefix``), and finally the key ID. In the example
+above, the Gateway will fetch the secret from::
 
-The object gateway will fetch the key from Vault (using the token for
-authentication), encrypt the object and store it in the bucket. Any request to
-downlod the object will require the correct key ID for the Gateway to
-successfully the decrypt it.
+  http://vaultserver:8200/v1/secret/data/myproject/mybucketkey
 
 .. _Server-Side Encryption: ../encryption
 .. _Vault: https://www.vaultproject.io/docs/
index b7168acfe19d7c34a95897169dc275b5aa2a1231..c80e31dfb837cfde5f0d4bf77847dabf9aeb54d2 100644 (file)
@@ -4,6 +4,7 @@ overrides:
       client:
         rgw crypt s3 kms backend: vault
         rgw crypt vault auth: token
+        rgw crypt vault prefix: /v1/kv/data
   rgw:
     client.0:
       use-vault-role: client.0
@@ -11,10 +12,12 @@ overrides:
 tasks:
 - vault:
     client.0:
-      version: 1.2.2
+      install_url: https://releases.hashicorp.com/vault/1.2.2/vault_1.2.2_linux_amd64.zip
+      install_sha256: 7725b35d9ca8be3668abe63481f0731ca4730509419b4eb29fa0b0baa4798458
       root_token: test_root_token
+      prefix: /v1/kv/data/
       secrets:
-        - path: /v1/kv/data/my-key-1
+        - path: my-key-1
           secret: a2V5MS5GcWVxKzhzTGNLaGtzQkg5NGVpb1FKcFpGb2c=
-        - path: /v1/kv/data/my-key-2
+        - path: my-key-2
           secret: a2V5Mi5yNUNNMGFzMVdIUVZxcCt5NGVmVGlQQ1k4YWg=
index c59ee2ec922414723f8f3500af3c624771da197a..d2a66ca35a7d93076ee201b1e583ee695109d169 100644 (file)
@@ -6,5 +6,5 @@ tasks:
         kms_key: my-key-1
         kms_key2: my-key-2
       vault:
-        key_path: /v1/kv/data/my-key-1
-        key_path2: /v1/kv/data/my-key-2
+        key_path: my-key-1
+        key_path2: my-key-2
index dda9aad0d3bb97dee372ecab45a2941ae4b4c875..1455b51b4dc020f89bae6b83de3f633199275fa7 100644 (file)
@@ -15,5 +15,6 @@ overrides:
     conf:
       client:
         rgw lc debug interval: 10
+        rgw crypt s3 kms backend: testing
         rgw crypt s3 kms encryption keys: testkey-1=YmluCmJvb3N0CmJvb3N0LWJ1aWxkCmNlcGguY29uZgo= testkey-2=aWIKTWFrZWZpbGUKbWFuCm91dApzcmMKVGVzdGluZwo=
         rgw crypt require ssl: false
index 4fca78c883f5590e61fb1c17e312376a6b9a1c79..3d30774ed96bdf54d54855e9510336c2d9542e3e 100644 (file)
@@ -11,5 +11,6 @@ overrides:
     conf:
       client:
         rgw lc debug interval: 10
+        rgw crypt s3 kms backend: testing
         rgw crypt s3 kms encryption keys: testkey-1=YmluCmJvb3N0CmJvb3N0LWJ1aWxkCmNlcGguY29uZgo= testkey-2=aWIKTWFrZWZpbGUKbWFuCm91dApzcmMKVGVzdGluZwo=
         rgw crypt require ssl: false
index f1b65877be6ea70705c924623d10fa807751e6e5..d666a5e6a01a014c8303650256cb94d8afe6c6c2 100644 (file)
@@ -136,6 +136,8 @@ def start_rgw(ctx, config, clients):
                 raise ConfigError('vault: no "root_token" specified')
             # create token on file
             ctx.cluster.only(client).run(args=['echo', '-n', ctx.vault.root_token, run.Raw('>'), token_path])
+            log.info("Restrict access to token file")
+            ctx.cluster.only(client).run(args=['chmod', '600', token_path])
             log.info("Token file content")
             ctx.cluster.only(client).run(args=['cat', token_path])
 
index 748164b9cb2271120104ad678767444dc2810e15..96f134ebde7b52d81619c434814a1c3054df5257 100644 (file)
@@ -6,9 +6,10 @@ import argparse
 import contextlib
 import logging
 import time
-
 import httplib
 import json
+from os import path
+from urlparse import urljoin
 
 from teuthology import misc as teuthology
 from teuthology import contextutil
@@ -45,24 +46,27 @@ def download(ctx, config):
     testdir = teuthology.get_testdir(ctx)
 
     for (client, cconf) in config.items():
-        vault_version = cconf.get('version', '1.2.2')
-
+        install_url = cconf.get('install_url')
+        install_sha256 = cconf.get('install_sha256')
+        if not install_url or not install_sha256:
+            raise ConfigError("Missing Vault install_url and/or install_sha256")
+        install_zip = path.join(testdir, 'vault.zip')
+        install_dir = path.join(testdir, 'vault')
+
+        log.info('Downloading Vault...')
         ctx.cluster.only(client).run(
-            args=['mkdir', '-p', '{tdir}/vault'.format(tdir=testdir)])
+            args=['curl', '-L', install_url, '-o', install_zip])
 
-        cmd = [
-            'curl', '-L',
-            'https://releases.hashicorp.com/vault/{version}/vault_{version}_linux_amd64.zip'.format(version=vault_version), '-o',
-            '{tdir}/vault_{version}.zip'.format(tdir=testdir, version=vault_version)
-        ]
-        ctx.cluster.only(client).run(args=cmd)
+        log.info('Verifying SHA256 signature...')
+        ctx.cluster.only(client).run(
+            args=['echo', ' '.join([install_sha256, install_zip]), run.Raw('|'),
+                  'sha256sum', '--check', '--status'])
 
         log.info('Extracting vault...')
+        ctx.cluster.only(client).run(args=['mkdir', '-p', install_dir])
         # Using python in case unzip is not installed on hosts
-        cmd = ['python', '-m', 'zipfile', '-e',
-               '{tdir}/vault_{version}.zip'.format(tdir=testdir, version=vault_version),
-               '{tdir}/vault'.format(tdir=testdir)]
-        ctx.cluster.only(client).run(args=cmd)
+        ctx.cluster.only(client).run(
+            args=['python', '-m', 'zipfile', '-e', install_zip, install_dir])
 
     try:
         yield
@@ -71,13 +75,7 @@ def download(ctx, config):
         testdir = teuthology.get_testdir(ctx)
         for client in config:
             ctx.cluster.only(client).run(
-                args=[
-                    'rm',
-                    '-rf',
-                    '{tdir}/vault_{version}.zip'.format(tdir=testdir, version=vault_version),
-                    '{tdir}/vault'.format(tdir=testdir),
-                    ],
-                )
+                args=['rm', '-rf', install_dir, install_zip])
 
 
 def get_vault_dir(ctx):
@@ -148,7 +146,7 @@ def send_req(ctx, cconfig, client, path, body, method='POST'):
     host, port = ctx.vault.endpoints[client]
     req = httplib.HTTPConnection(host, port, timeout=30)
     token = cconfig.get('root_token', 'atoken')
-    log.info("Send request to Vault: %s:%s with token: %s", host, port, token)
+    log.info("Send request to Vault: %s:%s at %s with token: %s", host, port, path, token)
     headers = {'X-Vault-Token': token}
     req.request(method, path, headers=headers, body=body)
     resp = req.getresponse()
@@ -161,6 +159,7 @@ def send_req(ctx, cconfig, client, path, body, method='POST'):
 @contextlib.contextmanager
 def create_secrets(ctx, config):
     (cclient, cconfig) = config.items()[0]
+    prefix = cconfig.get('prefix')
     secrets = cconfig.get('secrets')
     if secrets is None:
         raise ConfigError("No secrets specified, please specify some.")
@@ -175,7 +174,7 @@ def create_secrets(ctx, config):
         except KeyError:
             raise ConfigError('vault.secrets must have "secret" field')
         try:
-            send_req(ctx, cconfig, cclient, secret['path'], json.dumps(data))
+            send_req(ctx, cconfig, cclient, urljoin(prefix, secret['path']), json.dumps(data))
         except KeyError:
             raise ConfigError('vault.secrets must have "path" field')
 
@@ -220,6 +219,7 @@ def task(ctx, config):
     ctx.vault = argparse.Namespace()
     ctx.vault.endpoints = assign_ports(ctx, config, 8200)
     ctx.vault.root_token = None
+    ctx.vault.prefix = config[client].get('prefix')
 
     with contextutil.nested(
         lambda: download(ctx=ctx, config=config),
index 01db8d78ce4034f7e701d7ba9e586eb64ab59101..a30e206fd5c2a842dbd4de77b41415669e6da4b2 100644 (file)
@@ -1499,7 +1499,8 @@ OPTION(rgw_crypt_default_encryption_key, OPT_STR) // base64 encoded key for encr
 OPTION(rgw_crypt_s3_kms_backend, OPT_STR) // Where SSE-KMS encryption keys are stored
 OPTION(rgw_crypt_vault_auth, OPT_STR) // Type of authentication method to be used with Vault
 OPTION(rgw_crypt_vault_token_file, OPT_STR) // Path to the token file for Vault authentication
-OPTION(rgw_crypt_vault_addr, OPT_STR) // URL to Vault server endpoint
+OPTION(rgw_crypt_vault_addr, OPT_STR) // Vault server base address
+OPTION(rgw_crypt_vault_prefix, OPT_STR) // Optional URL prefix to Vault secret path
 
 OPTION(rgw_crypt_s3_kms_encryption_keys, OPT_STR) // extra keys that may be used for aws:kms
                                                       // defined as map "key1=YmluCmJvb3N0CmJvb3N0LQ== key2=b3V0CnNyYwpUZXN0aW5nCg=="
index f20568501cf2ded256a1f07a5abdfcbf6f018346..1d2e93bed148aafcd27341c4ccaac7e552a1a516 100644 (file)
@@ -6604,11 +6604,20 @@ std::vector<Option> get_rgw_options() {
 
     Option("rgw_crypt_vault_addr", Option::TYPE_STR, Option::LEVEL_ADVANCED)
     .set_default("")
-    .set_description("Base URL to the Vault server.")
+    .set_description("Vault server base address.")
     .add_see_also({
       "rgw_crypt_s3_kms_backend",
       "rgw_crypt_vault_auth",
-      "rgw_crypt_vault_token_file"}),
+      "rgw_crypt_vault_prefix"}),
+
+    Option("rgw_crypt_vault_prefix", Option::TYPE_STR, Option::LEVEL_ADVANCED)
+    .set_default("")
+    .set_description("Vault secret URL prefix, which can be used to restrict "
+                     "access to a particular subset of the Vault secret space.")
+    .add_see_also({
+      "rgw_crypt_s3_kms_backend",
+      "rgw_crypt_vault_addr",
+      "rgw_crypt_vault_auth"}),
 
     Option("rgw_crypt_suppress_logs", Option::TYPE_BOOL, Option::LEVEL_ADVANCED)
     .set_default(true)
index acd3fce62fb531fb35b486859ca49bb1e588c207..cb49193bb2ee70ff527505d0b281496531a4d0c7 100644 (file)
@@ -5,6 +5,7 @@
  * Server-side encryption integrations with Key Management Systems (SSE-KMS)
  */
 
+#include <sys/stat.h>
 #include "include/str_map.h"
 #include "common/safe_io.h"
 #include "rgw/rgw_crypt.h"
@@ -23,6 +24,22 @@ static map<string,string> get_str_map(const string &str) {
   return m;
 }
 
+/**
+ * Construct a full URL string by concatenating a "base" URL with another path,
+ * ensuring there is one and only one forward slash between them. If path is
+ * empty, the URL is not changed.
+ */
+static void concat_url(std::string &url, std::string path) {
+  if (!path.empty()) {
+    if (url.back() == '/' && path.front() == '/') {
+      url.pop_back();
+    } else if (url.back() != '/' && path.front() != '/') {
+      url.push_back('/');
+    }
+    url.append(path);
+  }
+}
+
 static int get_actual_key_from_conf(CephContext *cct,
                                     boost::string_view key_id,
                                     boost::string_view key_selector,
@@ -66,33 +83,19 @@ static int get_actual_key_from_conf(CephContext *cct,
   return res;
 }
 
-static int get_barbican_url(CephContext * const cct,
-                            std::string& url)
-{
-  url = cct->_conf->rgw_barbican_url;
-  if (url.empty()) {
-    ldout(cct, 0) << "ERROR: conf rgw_barbican_url is not set" << dendl;
-    return -EINVAL;
-  }
-
-  if (url.back() != '/') {
-    url.append("/");
-  }
-
-  return 0;
-}
-
 static int request_key_from_barbican(CephContext *cct,
                                      boost::string_view key_id,
                                      const std::string& barbican_token,
                                      std::string& actual_key) {
-  std::string secret_url;
   int res;
-  res = get_barbican_url(cct, secret_url);
-  if (res < 0) {
-     return res;
+
+  std::string secret_url = cct->_conf->rgw_barbican_url;
+  if (secret_url.empty()) {
+    ldout(cct, 0) << "ERROR: conf rgw_barbican_url is not set" << dendl;
+    return -EINVAL;
   }
-  secret_url += "v1/secrets/" + std::string(key_id);
+  concat_url(secret_url, "/v1/secrets/");
+  concat_url(secret_url, std::string(key_id));
 
   bufferlist secret_bl;
   RGWHTTPTransceiver secret_req(cct, "GET", secret_url, &secret_bl);
@@ -142,7 +145,7 @@ static int request_key_from_vault_with_token(CephContext *cct,
                                              boost::string_view key_id,
                                              bufferlist *secret_bl)
 {
-  std::string token_file, vault_addr, vault_token;
+  std::string token_file, secret_url, vault_token;
   int res = 0;
 
   token_file = cct->_conf->rgw_crypt_vault_token_file;
@@ -152,15 +155,25 @@ static int request_key_from_vault_with_token(CephContext *cct,
   }
   ldout(cct, 20) << "Vault token file: " << token_file << dendl;
 
+  struct stat token_st;
+  if (stat(token_file.c_str(), &token_st) != 0) {
+    ldout(cct, 0) << "ERROR: Vault token file '" << token_file << "' not found  " << dendl;
+    return -ENOENT;
+  }
+
+  if (token_st.st_mode & (S_IRWXG | S_IRWXO)) {
+    ldout(cct, 0) << "ERROR: Vault token file '" << token_file << "' permissions are "
+                  << "too open, it must not be accessible by other users" << dendl;
+    return -EACCES;
+  }
+
   char buf[2048];
   res = safe_read_file("", token_file.c_str(), buf, sizeof(buf));
   if (res < 0) {
-    if (-ENOENT == res) {
-      ldout(cct, 0) << "ERROR: Token file '" << token_file << "' not found  " << dendl;
-    } else if (-EACCES == res) {
-      ldout(cct, 0) << "ERROR: Permission denied reading token file" << dendl;
+    if (-EACCES == res) {
+      ldout(cct, 0) << "ERROR: Permission denied reading Vault token file" << dendl;
     } else {
-      ldout(cct, 0) << "ERROR: Failed to read token file with error " << res << dendl;
+      ldout(cct, 0) << "ERROR: Failed to read Vault token file with error " << res << dendl;
     }
     return res;
   }
@@ -171,13 +184,14 @@ static int request_key_from_vault_with_token(CephContext *cct,
   vault_token = std::string{buf, static_cast<size_t>(res)};
   memset(buf, 0, sizeof(buf));
 
-  vault_addr = cct->_conf->rgw_crypt_vault_addr;
-  if (vault_addr.empty()) {
+  secret_url = cct->_conf->rgw_crypt_vault_addr;
+  if (secret_url.empty()) {
     ldout(cct, 0) << "ERROR: Vault address not set in rgw_crypt_vault_addr" << dendl;
     return -EINVAL;
   }
+  concat_url(secret_url, cct->_conf->rgw_crypt_vault_prefix);
+  concat_url(secret_url, std::string(key_id));
 
-  std::string secret_url = vault_addr + std::string(key_id);
   RGWHTTPTransceiver secret_req(cct, "GET", secret_url, secret_bl);
   secret_req.append_header("X-Vault-Token", vault_token);
   vault_token.replace(0, vault_token.length(), vault_token.length(), '\000');
index 197aba6669007a1b2ea66c85e3a503827c2c6032..d1f4584358121dd99a64b9adffdd84ccf1970d94 100644 (file)
@@ -30,3 +30,27 @@ TEST(TestSSEKMS, non_existent_vault_token_file)
       -ENOENT
   );
 }
+
+TEST(TestSSEKMS, concat_url)
+{
+  // Each test has 3 strings:
+  // * the base URL
+  // * the path we want to concatenate
+  // * the exepected final URL
+  std::string tests[9][3] ={
+    {"", "", ""},
+    {"", "bar", "/bar"},
+    {"", "/bar", "/bar"},
+    {"foo", "", "foo"},
+    {"foo", "bar", "foo/bar"},
+    {"foo", "/bar", "foo/bar"},
+    {"foo/", "", "foo/"},
+    {"foo/", "bar", "foo/bar"},
+    {"foo/", "/bar", "foo/bar"},
+  };
+  for (const auto &test: tests) {
+    std::string url(test[0]), path(test[1]), expected(test[2]);
+    concat_url(url, path);
+    ASSERT_EQ(url, expected);
+  }
+}
\ No newline at end of file
index 9b83a0525f4ac9bcf58e82648f47fa9bcb7568d2..3d874510f7902cb0166b7e518ae57e3c2f97a325 100755 (executable)
@@ -681,7 +681,8 @@ EOF
         ; rgw crypt s3 kms backend = vault
         ; rgw crypt vault auth = token
         ; rgw crypt vault addr = http://127.0.0.1:8200
-        ; rgw crypt vault token file = /path/to/token.file
+        ; rgw crypt vault prefix = /v1/secret/data
+        ; rgw crypt vault token file = $CEPH_CONF_PATH/vault.token
 
 $extra_conf
 EOF