]> git.apps.os.sepia.ceph.com Git - ceph-ci.git/commitdiff
rgw/kms/kmip - pykmip.py needs to make keys too.
authorMarcus Watts <mwatts@redhat.com>
Thu, 29 Oct 2020 03:40:58 +0000 (23:40 -0400)
committerMarcus Watts <mwatts@redhat.com>
Thu, 4 Mar 2021 00:14:10 +0000 (19:14 -0500)
The logic to deploy pykmip in teuthology was not complete.
The necessary logic to add kmip keys was missing.

Existing logic for other key services providers could use rest based
protocols directly from the teuthology host.  For kmip, it is necessary
to use a special protocol, and it is more convenient to run this directly
on the pykmip server.

Signed-off-by: Marcus Watts <mwatts@redhat.com>
qa/tasks/pykmip.py

index 976a6fc9f205f730062321b273843ced691e5343..8f9b18c297ef1aab0e0ee05575b47fd1532156c8 100644 (file)
@@ -209,12 +209,21 @@ def create_pykmip_conf(ctx, cclient, cconfig):
     log.info('#3 servercert {}'.format(servercert))
     servercert = ctx.ssl_certificates.get(servercert)
     log.info('#4 servercert {}'.format(servercert))
+    clientkey = None
+    clientcert = cconfig.get('clientcert', None)
+    log.info('#3 clientcert {}'.format(clientcert))
+    clientcert = ctx.ssl_certificates.get(clientcert)
+    log.info('#4 clientcert {}'.format(clientcert))
     clientca = ctx.ssl_certificates.get(clientca)
     log.info('#5 clientca {}'.format(clientca))
     if servercert != None:
       serverkey = servercert.key
       servercert = servercert.certificate
       log.info('#6 serverkey {} servercert {}'.format(serverkey, servercert))
+    if clientcert != None:
+      clientkey = clientcert.key
+      clientcert = clientcert.certificate
+      log.info('#6 clientkey {} clientcert {}'.format(clientkey, clientcert))
     if clientca != None:
       clientca = clientca.certificate
       log.info('#7 clientca {}'.format(clientca))
@@ -228,6 +237,8 @@ def create_pykmip_conf(ctx, cclient, cconfig):
         confdir=pykmipdir,
         hostname=pykmip_hostname,
         clientca=clientca,
+        clientkey=clientkey,
+        clientcert=clientcert,
         serverkey=serverkey,
         servercert=servercert
     )
@@ -310,157 +321,65 @@ def run_pykmip(ctx, config):
         ctx.daemons.get_daemon('pykmip', client_public_with_id,
                                cluster_name).stop()
 
+make_keys_template = """
+from kmip.pie import client
+from kmip import enums
+import ssl
+import sys
+import json
+from io import BytesIO
+
+c = client.ProxyKmipClient(config_file="{replace-with-config-file-path}")
+
+rl=[]
+for kwargs in {replace-with-secrets}:
+ with c:
+  key_id = c.create(
+   enums.CryptographicAlgorithm.AES,
+   256,
+   operation_policy_name='default',
+   cryptographic_usage_mask=[
+    enums.CryptographicUsageMask.ENCRYPT,
+    enums.CryptographicUsageMask.DECRYPT
+   ],
+   **kwargs
+  )
+  c.activate(key_id)
+  attrs = c.get_attributes(uid=key_id)
+  r = {}
+  for a in attrs[1]:
+   r[str(a.attribute_name)] = str(a.attribute_value)
+  rl.append(r)
+print(json.dumps(rl))
+"""
 
 @contextlib.contextmanager
 def create_secrets(ctx, config):
     """
-    Create a main and an alternate s3 user.
+    Create and activate any requested keys in kmip
     """
-    try:
-        yield
-    finally:
-        return
     assert isinstance(config, dict)
-    (cclient, cconfig) = next(iter(config.items()))
 
-    rgw_user = cconfig['rgw_user']
-
-    keystone_role = cconfig.get('use-keystone-role', None)
-    keystone_host, keystone_port = ctx.keystone.public_endpoints[keystone_role]
-    pykmip_ipaddr, pykmip_port, pykmip_hostname = ctx.pykmip.endpoints[cclient]
-    pykmip_url = 'http://{host}:{port}'.format(host=pykmip_hostname,
-                                                 port=pykmip_port)
-    log.info("pykmip_url=%s", pykmip_url)
-    #fetching user_id of user that gets secrets for radosgw
-    token_req = httplib.HTTPConnection(keystone_host, keystone_port, timeout=30)
-    token_req.request(
-        'POST',
-        '/v2.0/tokens',
-        headers={'Content-Type':'application/json'},
-        body=json.dumps(
-            {"auth":
-             {"passwordCredentials":
-              {"username": rgw_user["username"],
-               "password": rgw_user["password"]
-              },
-              "tenantName": rgw_user["tenantName"]
-             }
-            }
-        )
-    )
-    rgw_access_user_resp = token_req.getresponse()
-    if not (rgw_access_user_resp.status >= 200 and
-            rgw_access_user_resp.status < 300):
-        raise Exception("Cannot authenticate user "+rgw_user["username"]+" for secret creation")
-    #    baru_resp = json.loads(baru_req.data)
-    rgw_access_user_data = json.loads(rgw_access_user_resp.read())
-    rgw_user_id = rgw_access_user_data['access']['user']['id']
-
-    if 'secrets' in cconfig:
-        for secret in cconfig['secrets']:
-            if 'name' not in secret:
-                raise ConfigError('pykmip.secrets must have "name" field')
-            if 'base64' not in secret:
-                raise ConfigError('pykmip.secrets must have "base64" field')
-            if 'tenantName' not in secret:
-                raise ConfigError('pykmip.secrets must have "tenantName" field')
-            if 'username' not in secret:
-                raise ConfigError('pykmip.secrets must have "username" field')
-            if 'password' not in secret:
-                raise ConfigError('pykmip.secrets must have "password" field')
-
-            token_req = httplib.HTTPConnection(keystone_host, keystone_port, timeout=30)
-            token_req.request(
-                'POST',
-                '/v2.0/tokens',
-                headers={'Content-Type':'application/json'},
-                body=json.dumps(
-                    {
-                        "auth": {
-                            "passwordCredentials": {
-                                "username": secret["username"],
-                                "password": secret["password"]
-                            },
-                            "tenantName":secret["tenantName"]
-                        }
-                    }
-                )
-            )
-            token_resp = token_req.getresponse()
-            if not (token_resp.status >= 200 and
-                    token_resp.status < 300):
-                raise Exception("Cannot authenticate user "+secret["username"]+" for secret creation")
-
-            token_data = json.loads(token_resp.read())
-            token_id = token_data['access']['token']['id']
-
-            key1_json = json.dumps(
-                {
-                    "name": secret['name'],
-                    "expiration": "2020-12-31T19:14:44.180394",
-                    "algorithm": "aes",
-                    "bit_length": 256,
-                    "mode": "cbc",
-                    "payload": secret['base64'],
-                    "payload_content_type": "application/octet-stream",
-                    "payload_content_encoding": "base64"
-                })
-
-            sec_req = httplib.HTTPConnection(pykmip_hostname, pykmip_port, timeout=30)
-            try:
-                sec_req.request(
-                    'POST',
-                    '/v1/secrets',
-                    headers={'Content-Type': 'application/json',
-                             'Accept': '*/*',
-                             'X-Auth-Token': token_id},
-                    body=key1_json
-                )
-            except:
-                log.info("catched exception!")
-                run_in_pykmip_dir(ctx, cclient, ['sleep', '900'])
-
-            pykmip_sec_resp = sec_req.getresponse()
-            if not (pykmip_sec_resp.status >= 200 and
-                    pykmip_sec_resp.status < 300):
-                raise Exception("Cannot create secret")
-            pykmip_data = json.loads(pykmip_sec_resp.read())
-            if 'secret_ref' not in pykmip_data:
-                raise ValueError("Malformed secret creation response")
-            secret_ref = pykmip_data["secret_ref"]
-            log.info("secret_ref=%s", secret_ref)
-            secret_url_parsed = urlparse(secret_ref)
-            acl_json = json.dumps(
-                {
-                    "read": {
-                        "users": [rgw_user_id],
-                        "project-access": True
-                    }
-                })
-            acl_req = httplib.HTTPConnection(secret_url_parsed.netloc, timeout=30)
-            acl_req.request(
-                'PUT',
-                secret_url_parsed.path+'/acl',
-                headers={'Content-Type': 'application/json',
-                         'Accept': '*/*',
-                         'X-Auth-Token': token_id},
-                body=acl_json
-            )
-            pykmip_acl_resp = acl_req.getresponse()
-            if not (pykmip_acl_resp.status >= 200 and
-                    pykmip_acl_resp.status < 300):
-                raise Exception("Cannot set ACL for secret")
-
-            key = {'id': secret_ref.split('secrets/')[1], 'payload': secret['base64']}
-            ctx.pykmip.keys[secret['name']] = key
-
-    run_in_pykmip_dir(ctx, cclient, ['sleep', '3'])
+    pykmipdir = get_pykmip_dir(ctx)
+    pykmip_conf_path = pykmipdir + '/pykmip.conf'
+    my_output = BytesIO()
+    for (client,cconf) in config.items():
+        (remote,) = ctx.cluster.only(client).remotes.keys()
+        secrets=cconf.get('secrets')
+        if secrets:
+            secrets_json = json.dumps(cconf['secrets'])
+            make_keys = make_keys_template \
+                .replace("{replace-with-secrets}",secrets_json) \
+                .replace("{replace-with-config-file-path}",pykmip_conf_path)
+            my_output.truncate()
+            remote.run(args=[run.Raw('. cephtest/pykmip/.pykmipenv/bin/activate;' \
+                + 'python')], stdin=make_keys, stdout = my_output)
+            ctx.pykmip.keys[client] = json.loads(my_output.getvalue().decode())
     try:
         yield
     finally:
         pass
 
-
 @contextlib.contextmanager
 def task(ctx, config):
     """