]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
rgw: improvements to SSE-KMS with Vault 31361/head
authorSergio de Carvalho <scarvalhojr@gmail.com>
Thu, 7 Nov 2019 15:33:14 +0000 (15:33 +0000)
committerAndrea Baglioni <andrea.baglioni@workday.com>
Thu, 28 Nov 2019 09:19:39 +0000 (09:19 +0000)
* Minor improvements to Vault documentation
* Add teuthology tests for Transit secrets engine
* Add unit tests for KV secrets engine, minor improvements to Transit
  secrets engine
* use string_view::npos instead of string::npos

Signed-off-by: Andrea Baglioni <andrea.baglioni@workday.com>
Signed-off-by: Sergio de Carvalho <sergio.carvalho@workday.com>
doc/radosgw/vault.rst
qa/suites/rgw/crypt/2-kms/vault.yaml [deleted file]
qa/suites/rgw/crypt/2-kms/vault_kv.yaml [new file with mode: 0644]
qa/suites/rgw/crypt/2-kms/vault_transit.yaml [new file with mode: 0644]
qa/suites/rgw/crypt/4-tests/s3tests.yaml
qa/tasks/s3tests.py
qa/tasks/vault.py
src/rgw/rgw_kms.cc
src/test/rgw/test_rgw_kms.cc
src/vstart.sh

index a384887d3aa7fac16b0734555949561c8ed03ceb..3deef8dac3b637b7ae18a33984c83f07dcfcd5e1 100644 (file)
@@ -5,75 +5,121 @@ HashiCorp Vault Integration
 HashiCorp `Vault`_ can be used as a secure key management service for
 `Server-Side Encryption`_ (SSE-KMS).
 
-#. `Vault Access`_
-#. `Secrets Engines`_
-#. `Configure Ceph Object Gateway`_
+.. ditaa:: +---------+       +---------+        +-------+     +-------+
+           |  Client |       | RadosGW |        | Vault |     |  OSD  |
+           +---------+       +---------+        +-------+     +-------+
+                | create secret   |                 |             |
+                | key for key ID  |                 |             |
+                |-----------------+---------------->|             |
+                |                 |                 |             |
+                | upload object   |                 |             |
+                | with key ID     |                 |             |
+                |---------------->| request secret  |             |
+                |                 | key for key ID  |             |
+                |                 |---------------->|             |
+                |                 |<----------------|             |
+                |                 | return secret   |             |
+                |                 | key             |             |
+                |                 |                 |             |
+                |                 | encrypt object  |             |
+                |                 | with secret key |             |
+                |                 |--------------+  |             |
+                |                 |              |  |             |
+                |                 |<-------------+  |             |
+                |                 |                 |             |
+                |                 | store encrypted |             |
+                |                 | object          |             |
+                |                 |------------------------------>|
+
+#. `Vault secrets engines`_
+#. `Vault authentication`_
+#. `Vault namespaces`_
+#. `Create a key in Vault`_
+#. `Configure the Ceph Object Gateway`_
 #. `Upload object`_
 
-Vault Access
-============
+Some examples below use the Vault command line utility to interact with
+Vault. You may need to set the following environment variable with the correct
+address of your Vault server to use this utility::
 
-Access to Vault can be done through several authentication mechanisms.
-Object Gateway supports `token authentication method`_ and `vault agent`_.
+  export VAULT_ADDR='http://vault-server:8200'
 
-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::
+Vault secrets engines
+=====================
 
-     rgw crypt vault auth = token
-     rgw crypt vault token file = /etc/ceph/vault.token
+Vault provides several secrets engines, which can store, generate, and encrypt
+data. Currently, the Object Gateway supports:
 
-For security reasons, ensure the file is readable by the Object Gateway only.
+- `KV secrets engine`_ version 2
+- `Transit engine`_
 
-.. note:: Token authentication is not recommended configuration for production environments.
+KV secrets engine
+-----------------
 
-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...
+The KV secrets engine is used to store arbitrary key/value secrets in Vault. To
+enable the KV engine version 2 in Vault, use the following command::
 
-You can enable agent authentication in Ceph's configuration file with::
+  vault secrets enable kv-v2
 
-     rgw crypt vault auth = agent
-     rgw crypt vault addr = http://localhost:8200
+The Object Gateway can be configured to use the KV engine version 2 with the
+following setting::
 
-.. note:: ``rgw_crypt_vault_token_file`` is ignored if ``rgw_crypt_vault_auth`` is set to ``agent``.
+  rgw crypt vault secret engine = kv
 
-Secrets Engines
-===============
-Vault provides several Secret Engines, which can store, generate, and encrypt data. Currently, the Object Gateway supports:
+Transit secrets engine
+----------------------
 
-- `KV Secrets engine`_ version 2
-- `Transit engine`_
+The transit engine handles cryptographic functions on data in-transit. To enable
+it in Vault, use the following command::
 
-.. important:: Examples in this section are for demonstration purposes only and assume token authentication.
+  vault secrets enable transit
 
+The Object Gateway can be configured to use the transit engine with the
+following setting::
 
-KV Secrets engine
------------------
-To enable the KV engine version 2 in Vault, use the Vault command line
-tool::
+  rgw crypt vault secret engine = transit
 
-  vault secrets enable kv-v2
+Vault authentication
+====================
+
+Vault supports several authentication mechanisms. Currently, the Object
+Gateway can be configured to authenticate to Vault using the
+`Token authentication method`_ or a `Vault agent`_.
+
+Token authentication
+--------------------
+
+.. note:: Token authentication is not recommended for production environments.
 
-When authenticating using the token method, a token must be obtained
-for the Gateway and saved in a file as plain-text.
+The token authentication method expects a Vault token to be present in a
+plaintext file. The Object Gateway can be configured to use token authentication
+with the following settings::
 
-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::
+  rgw crypt vault auth = token
+  rgw crypt vault token file = /etc/ceph/vault.token
+  rgw crypt vault addr = http://vault-server:8200
 
-  vault policy write rgw-policy -<<EOF
+For security reasons, the token file must be readable by the Object Gateway
+only. Also, the Object Gateway should be given a Vault token with a restricted
+policy that allows it to fetch keyrings from a specific path only. Such a policy
+can be created in Vault using the command line utility as in the following
+examples::
+
+  vault policy write rgw-kv-policy -<<EOF
     path "secret/data/*" {
       capabilities = ["read"]
     }
   EOF
 
-A token with the policy above can be created by a Vault administrator as
-follows::
+  vault policy write rgw-transit-policy -<<EOF
+    path "transit/export/encryption-key/*" {
+      capabilities = ["read"]
+    }
+  EOF
+
+Once the policy is created, a token can be generated by a Vault administrator::
 
-  vault token create -policy=rgw-policy
+  vault token create -policy=rgw-kv-policy
 
 Sample output::
 
@@ -83,25 +129,54 @@ Sample output::
   token_accessor       jv95ZYBUFv6Ss84x7SCSy6lZ
   token_duration       768h
   token_renewable      true
-  token_policies       ["default" "rgw-policy"]
+  token_policies       ["default" "rgw-kv-policy"]
   identity_policies    []
-  policies             ["default" "rgw-policy"]
+  policies             ["default" "rgw-kv-policy"]
+
+The actual token, displayed in the ``Value`` column of the first line of the
+output, must be saved in a file as plaintext.
 
+Vault agent
+-----------
 
-Create a key using KV Engine
-----------------------------
+The Vault agent is a client daemon that provides authentication to Vault and
+manages token renewal and caching. It typically runs on the same host as the
+Object Gateway. With a Vault agent, it is possible to use other Vault
+authentication mechanism such as AppRole, AWS, Certs, JWT, and Azure.
 
-.. note:: Server-side encryption keys must be 256-bit long and base64 encoded.
+The Object Gateway can be configured to use a Vault agent with the following
+settings::
 
-To create a key in the KV version 2 engine using Vault's command line tool,
-use the commands below::
+  rgw crypt vault auth = agent
+  rgw crypt vault addr = http://localhost:8100
 
-  export VAULT_ADDR='http://vaultserver:8200'
-  vault kv put secret/myproject/mybucketkey key=$(openssl rand -base64 32)
+Vault namespaces
+================
+
+In the Enterprise version, Vault supports the concept of `namespaces`_, which
+allows centralized management for teams within an organization while ensuring
+that those teams operate within isolated environments known as tenants.
+
+The Object Gateway can be configured to access Vault within a particular
+namespace using the following configuration setting::
+
+  rgw crypt vault namespace = tenant1
+
+Create a key in Vault
+=====================
+
+.. note:: Keys for server-side encryption must be 256-bit long and base-64
+   encoded.
 
-.. 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>.
+Using the KV engine
+-------------------
 
-Output::
+A key for server-side encryption can be created in the KV version 2 engine using
+the command line utility, as in the following example::
+
+  vault kv put secret/myproject/mybucketkey key=$(openssl rand -base64 32)
+
+Sample output::
 
   ====== Metadata ======
   Key              Value
@@ -111,32 +186,26 @@ Output::
   destroyed        false
   version          1
 
-  === Data ===
-  Key    Value
-  ---    -----
-  key    Ak5dRyLQjwX/wb7vo6Fq1qjsfk1dh2CiSicX-gLAhwk=
-
-
-Transit Secrets Engine
-----------------------
-To enable the Transit engine in Vault, use the Vault command line tool::
+Note that 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=<secret key>``.
 
-  vault secrets enable transit
+Using the Transit engine
+------------------------
 
-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::
+Keys created with the Transit engine must be exportable in order to be used for
+server-side encryption with the Object Gateway. An exportable key can be created
+with the command line utility as follows::
 
-   export VAULT_ADDR='http://vaultserver:8200'
-   vault write -f transit/keys/mybucketkey exportable=true
+  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::
+The command above creates a keyring, which contains a key of type
+``aes256-gcm96`` by default. To verify that the key was correctly created, use
+the following command::
 
   vault read transit/export/encryption-key/mybucketkey/1
 
-Output::
+Sample output::
 
   Key     Value
   ---     -----
@@ -144,82 +213,76 @@ Output::
   name    mybucketkey
   type    aes256-gcm96
 
-.. note:: To use Transit Secrets engine in Object Gateway, you shall specify the full key, including its version.
+Note that in order to read the key created with the Transit engine, the full
+path must be provided including the key 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::
+Configure the Ceph Object Gateway
+=================================
 
-  vault policy write rgw-transit-policy -<<EOF
-    path "transit/export/encryption-key/mybucketkey/*" {
-      capabilities = ["read"]
-    }
-  EOF
+Edit the Ceph configuration file to enable Vault as a KMS backend for
+server-side encryption::
 
-Once the policy is created, a token can be generated by Vault administrator::
+  rgw crypt s3 kms backend = vault
 
-  vault token create -policy=rgw-transit-policy
+Choose the Vault authentication method, e.g.::
 
-Sample output::
+  rgw crypt vault auth = token
+  rgw crypt vault token file = /etc/ceph/vault.token
+  rgw crypt vault addr = http://vault-server:8200
 
-  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"]
+Or::
 
+  rgw crypt vault auth = agent
+  rgw crypt vault addr = http://localhost:8100
 
-Configure Ceph Object Gateway
-=================================
+Choose the secrets engine::
+
+  rgw crypt vault secret engine = kv
+
+Or::
+
+  rgw crypt vault secret engine = transit
+
+Optionally, set the Vault namespace where encryption keys will be fetched from::
+
+  rgw crypt vault namespace = tenant1
 
-Edit the Ceph configuration file to enable Vault as a KMS for server-side
-encryption.::
+Finally, the URLs where the Gateway will retrieve encryption keys from Vault can
+be restricted by setting a path prefix. For instance, the Gateway can be
+restricted to fetch KV keys as follows::
 
-   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 }
+  rgw crypt vault prefix = /v1/secret/data
 
-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::
+Or, in the case of exportable transit 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
+  rgw crypt vault prefix = /v1/transit/export/encryption-key
 
+In the example above, the Gateway would only fetch transit encryption keys under
+``http://vault-server:8200/v1/transit/export/encryption-key``.
 
 Upload object
 =============
 
-When uploading an object, provide the SSE key ID in the request. As an example,
-using the AWS command-line client::
+When uploading an object to the Gateway, 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 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.
+it in the bucket. Any request to download the object will make the Gateway
+automatically retrieve the correspondent key from Vault and decrypt the object.
 
 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::
+above, the Gateway would fetch the secret from::
 
   http://vaultserver:8200/v1/secret/data/myproject/mybucketkey
 
 .. _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
+.. _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
+.. _namespaces: https://www.vaultproject.io/docs/enterprise/namespaces/index.html
diff --git a/qa/suites/rgw/crypt/2-kms/vault.yaml b/qa/suites/rgw/crypt/2-kms/vault.yaml
deleted file mode 100644 (file)
index c80e31d..0000000
+++ /dev/null
@@ -1,23 +0,0 @@
-overrides:
-  ceph:
-    conf:
-      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
-
-tasks:
-- vault:
-    client.0:
-      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: my-key-1
-          secret: a2V5MS5GcWVxKzhzTGNLaGtzQkg5NGVpb1FKcFpGb2c=
-        - path: my-key-2
-          secret: a2V5Mi5yNUNNMGFzMVdIUVZxcCt5NGVmVGlQQ1k4YWg=
diff --git a/qa/suites/rgw/crypt/2-kms/vault_kv.yaml b/qa/suites/rgw/crypt/2-kms/vault_kv.yaml
new file mode 100644 (file)
index 0000000..9ee9366
--- /dev/null
@@ -0,0 +1,25 @@
+overrides:
+  ceph:
+    conf:
+      client:
+        rgw crypt s3 kms backend: vault
+        rgw crypt vault auth: token
+        rgw crypt vault secret engine: kv
+        rgw crypt vault prefix: /v1/kv/data
+  rgw:
+    client.0:
+      use-vault-role: client.0
+
+tasks:
+- vault:
+    client.0:
+      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
+      engine: kv
+      prefix: /v1/kv/data/
+      secrets:
+        - path: my-key-1
+          secret: a2V5MS5GcWVxKzhzTGNLaGtzQkg5NGVpb1FKcFpGb2c=
+        - path: my-key-2
+          secret: a2V5Mi5yNUNNMGFzMVdIUVZxcCt5NGVmVGlQQ1k4YWg=
diff --git a/qa/suites/rgw/crypt/2-kms/vault_transit.yaml b/qa/suites/rgw/crypt/2-kms/vault_transit.yaml
new file mode 100644 (file)
index 0000000..775f546
--- /dev/null
@@ -0,0 +1,23 @@
+overrides:
+  ceph:
+    conf:
+      client:
+        rgw crypt s3 kms backend: vault
+        rgw crypt vault auth: token
+        rgw crypt vault secret engine: transit
+        rgw crypt vault prefix: /v1/transit/export/encryption-key/
+  rgw:
+    client.0:
+      use-vault-role: client.0
+
+tasks:
+- vault:
+    client.0:
+      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
+      engine: transit
+      prefix: /v1/transit/keys/
+      secrets:
+        - path: my-key-1
+        - path: my-key-2
index d2a66ca35a7d93076ee201b1e583ee695109d169..9f52fee08e00159ed5115ce2e9ad0cae205e8c4f 100644 (file)
@@ -5,6 +5,9 @@ tasks:
       barbican:
         kms_key: my-key-1
         kms_key2: my-key-2
-      vault:
+      vault_kv:
         key_path: my-key-1
         key_path2: my-key-2
+      vault_transit:
+        key_path: my-key-1/1
+        key_path2: my-key-2/1
index eb6dc5a6eeb4e6982bcf92ad88b260a4a8aa801e..a34c4ce4d6213ff4acc41a68c499c3bbae65b9a1 100644 (file)
@@ -199,7 +199,6 @@ def configure(ctx, config):
                     's3tests: no dns-s3website-name for rgw_website_server {}'.format(website_role)
             s3tests_conf['DEFAULT']['s3website_domain'] = website_endpoint.website_dns_name
 
-
         if hasattr(ctx, 'barbican'):
             properties = properties['barbican']
             if properties is not None and 'kms_key' in properties:
@@ -216,10 +215,10 @@ def configure(ctx, config):
                 s3tests_conf['DEFAULT']['kms_keyid2'] = key['id']
 
         elif hasattr(ctx, 'vault'):
-            properties = properties['vault']
-            log.info("Vault Key")
+            properties = properties['vault_%s' % ctx.vault.engine]
             s3tests_conf['DEFAULT']['kms_keyid'] = properties['key_path']
             s3tests_conf['DEFAULT']['kms_keyid2'] = properties['key_path2']
+
         else:
             # Fallback scenario where it's the local (ceph.conf) kms being tested
             s3tests_conf['DEFAULT']['kms_keyid'] = 'testkey-1'
index 96f134ebde7b52d81619c434814a1c3054df5257..34efc4f970c61bbfaee492962b041da1a62ee462 100644 (file)
@@ -126,19 +126,30 @@ def run_vault(ctx, config):
 @contextlib.contextmanager
 def setup_vault(ctx, config):
     """
-    Mount simple kv Secret Engine
-    Note: this will be extended to support transit secret engine
+    Mount Transit or KV version 2 secrets engine
     """
-    data = {
-        "type": "kv",
-        "options": {
-            "version": "2"
+    (cclient, cconfig) = config.items()[0]
+    engine = cconfig.get('engine')
+
+    if engine == 'kv':
+        log.info('Mounting kv version 2 secrets engine')
+        mount_path = '/v1/sys/mounts/kv'
+        data = {
+            "type": "kv",
+            "options": {
+                "version": "2"
+            }
+        }
+    elif engine == 'transit':
+        log.info('Mounting transit secrets engine')
+        mount_path = '/v1/sys/mounts/transit'
+        data = {
+            "type": "transit"
         }
-    }
+    else:
+        raise Exception("Unknown or missing secrets engine")
 
-    (cclient, cconfig) = config.items()[0]
-    log.info('Mount kv version 2 secret engine')
-    send_req(ctx, cconfig, cclient, '/v1/sys/mounts/kv', json.dumps(data))
+    send_req(ctx, cconfig, cclient, mount_path, json.dumps(data))
     yield
 
 
@@ -152,13 +163,14 @@ def send_req(ctx, cconfig, client, path, body, method='POST'):
     resp = req.getresponse()
     log.info(resp.read())
     if not (resp.status >= 200 and resp.status < 300):
-        raise Exception("Error Contacting Vault Server")
+        raise Exception("Request to Vault server failed with status %d" % resp.status)
     return resp
 
 
 @contextlib.contextmanager
 def create_secrets(ctx, config):
     (cclient, cconfig) = config.items()[0]
+    engine = cconfig.get('engine')
     prefix = cconfig.get('prefix')
     secrets = cconfig.get('secrets')
     if secrets is None:
@@ -166,17 +178,25 @@ def create_secrets(ctx, config):
 
     for secret in secrets:
         try:
-            data = {
-                "data": {
-                    "key": secret['secret']
-                }
-            }
-        except KeyError:
-            raise ConfigError('vault.secrets must have "secret" field')
-        try:
-            send_req(ctx, cconfig, cclient, urljoin(prefix, secret['path']), json.dumps(data))
+            path = secret['path']
         except KeyError:
-            raise ConfigError('vault.secrets must have "path" field')
+            raise ConfigError('Missing "path" field in secret')
+
+        if engine == 'kv':
+            try:
+                data = {
+                    "data": {
+                        "key": secret['secret']
+                    }
+                }
+            except KeyError:
+                raise ConfigError('Missing "secret" field in secret')
+        elif engine == 'transit':
+            data = {"exportable": "true"}
+        else:
+            raise Exception("Unknown or missing secrets engine")
+
+        send_req(ctx, cconfig, cclient, urljoin(prefix, path), json.dumps(data))
 
     log.info("secrets created")
     yield
@@ -194,6 +214,8 @@ def task(ctx, config):
         client.0:
           version: 1.2.2
           root_token: test_root_token
+          engine: kv
+          prefix: /v1/kv/data/
           secrets:
             - path: kv/teuthology/key_a
               secret: YmluCmJvb3N0CmJvb3N0LWJ1aWxkCmNlcGguY29uZgo=
@@ -220,6 +242,7 @@ def task(ctx, config):
     ctx.vault.endpoints = assign_ports(ctx, config, 8200)
     ctx.vault.root_token = None
     ctx.vault.prefix = config[client].get('prefix')
+    ctx.vault.engine = config[client].get('engine')
 
     with contextutil.nested(
         lambda: download(ctx=ctx, config=config),
index bd9a8d94f88ec3d85beda83268d0ea7371c20907..098edeca51bddcdff31dbd5f9e739c5d6cde3f52 100644 (file)
@@ -93,7 +93,7 @@ protected:
       ldout(cct, 0) << "Loading Vault Token from filesystem" << dendl;
       res = load_token_from_file(&vault_token);
       if (res < 0){
-       return res;
+        return res;
       }
     }
 
@@ -177,9 +177,9 @@ private:
     size_t pos = 0;
 
     pos = key_id.rfind("/");
-    if (pos != std::string::npos){
+    if (pos != boost::string_view::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){
+      if (!token.empty() && token.find_first_not_of("0123456789") == boost::string_view::npos){
         version.assign(std::string(token));
         return 0;
       }
index 494924ec0d2bb414e5aac1d15529c4547d3e6507..4246a1c1d8c3145bfb7f41216f416f4a414631ee 100644 (file)
@@ -14,6 +14,7 @@ using ::testing::MakeAction;
 
 
 class MockTransitSecretEngine : public TransitSecretEngine {
+
 public:
   MockTransitSecretEngine(CephContext *cct) : TransitSecretEngine(cct){}
 
@@ -21,20 +22,31 @@ public:
 
 };
 
+class MockKvSecretEngine : public KvSecretEngine {
+
+public:
+  MockKvSecretEngine(CephContext *cct) : KvSecretEngine(cct){}
+
+  MOCK_METHOD(int, send_request, (boost::string_view key_id, JSONParser* parser), (override));
+
+};
 
 class TestSSEKMS : public ::testing::Test {
 
 protected:
   CephContext *cct;
   MockTransitSecretEngine* transit_engine;
+  MockKvSecretEngine* kv_engine;
 
   void SetUp() override {
     cct = (new CephContext(CEPH_ENTITY_TYPE_ANY))->get();
     transit_engine = new MockTransitSecretEngine(cct);
+    kv_engine = new MockKvSecretEngine(cct);
   }
 
   void TearDown() {
     delete transit_engine;
+    delete kv_engine;
   }
 
 };
@@ -42,25 +54,30 @@ protected:
 
 TEST_F(TestSSEKMS, vault_token_file_unset)
 {
+  cct->_conf.set_val("rgw_crypt_vault_auth", "token");
+  TransitSecretEngine te(cct);
+  KvSecretEngine kv(cct);
 
   boost::string_view key_id("my_key");
   std::string actual_key;
-  TransitSecretEngine te(cct);
-  int res = te.get_key(key_id, actual_key);
-  ASSERT_EQ(res, -EINVAL);
+
+  ASSERT_EQ(te.get_key(key_id, actual_key), -EINVAL);
+  ASSERT_EQ(kv.get_key(key_id, actual_key), -EINVAL);
 }
 
 
 TEST_F(TestSSEKMS, non_existent_vault_token_file)
 {
+  cct->_conf.set_val("rgw_crypt_vault_auth", "token");
   cct->_conf.set_val("rgw_crypt_vault_token_file", "/nonexistent/file");
+  TransitSecretEngine te(cct);
+  KvSecretEngine kv(cct);
 
   boost::string_view key_id("my_key/1");
   std::string actual_key;
-  TransitSecretEngine te(cct);
-  int res = te.get_key(key_id, actual_key);
 
-  ASSERT_EQ(res, -ENOENT);
+  ASSERT_EQ(te.get_key(key_id, actual_key), -ENOENT);
+  ASSERT_EQ(kv.get_key(key_id, actual_key), -ENOENT);
 }
 
 
@@ -90,15 +107,13 @@ Action<SendRequestMethod> SetPointedValue(std::string json) {
 
 
 TEST_F(TestSSEKMS, test_transit_key_version_extraction){
-
-  boost::string_view key_id("my_key");
-  std::string version;
-  std::string actual_key;
-
   string json = R"({"data": {"keys": {"6": "8qgPWvdtf6zrriS5+nkOzDJ14IGVR6Bgkub5dJn6qeg="}}})";
   EXPECT_CALL(*transit_engine, send_request(_, _)).WillOnce(SetPointedValue(json));
 
-  std::string tests[4] {"/", "my_key/", "my_key", ""};
+  std::string actual_key;
+  std::string tests[11] {"/", "my_key/", "my_key", "", "my_key/a", "my_key/1a",
+    "my_key/a1", "my_key/1a1", "my_key/1/a", "1", "my_key/1/"
+  };
 
   int res;
   for (const auto &test: tests) {
@@ -128,6 +143,22 @@ TEST_F(TestSSEKMS, test_transit_backend){
 }
 
 
+TEST_F(TestSSEKMS, test_kv_backend){
+
+  boost::string_view my_key("my_key");
+  std::string actual_key;
+
+  // Mocks the expected return value from Vault Server using custom Argument Action
+  string json = R"({"data": {"data": {"key": "8qgPWvdtf6zrriS5+nkOzDJ14IGVR6Bgkub5dJn6qeg="}}})";
+  EXPECT_CALL(*kv_engine, send_request(_, _)).WillOnce(SetPointedValue(json));
+
+  int res = kv_engine->get_key(my_key, actual_key);
+
+  ASSERT_EQ(res, 0);
+  ASSERT_EQ(actual_key, from_base64("8qgPWvdtf6zrriS5+nkOzDJ14IGVR6Bgkub5dJn6qeg="));
+}
+
+
 TEST_F(TestSSEKMS, concat_url)
 {
   // Each test has 3 strings:
@@ -155,7 +186,6 @@ TEST_F(TestSSEKMS, concat_url)
 
 TEST_F(TestSSEKMS, test_transit_backend_empty_response)
 {
-
   boost::string_view my_key("/key/nonexistent/1");
   std::string actual_key;
 
index 848ad245c22c8e2da1f5c54ee4a2cc567121e86a..a2a4e35f4c09884ff2698286d18065710d98b755 100755 (executable)
@@ -680,10 +680,12 @@ EOF
         ; The following settings are for SSE-KMS with Vault
         ;rgw crypt s3 kms backend = vault
         ;rgw crypt vault auth = token
-        ;rgw crypt vault addr = http://127.0.0.1:8200
-        ;rgw crypt vault prefix = /v1/transit/export/encryption-key/
         ;rgw crypt vault token file = $CEPH_CONF_PATH/vault.token
+        ;rgw crypt vault addr = http://127.0.0.1:8200
+        ;rgw crypt vault secret engine = kv
+        ;rgw crypt vault prefix = /v1/kv/data
         ;rgw crypt vault secret engine = transit
+        ;rgw crypt vault prefix = /v1/transit/export/encryption-key/
 
 $extra_conf
 EOF