mirror of
https://github.com/ceph/ceph
synced 2024-12-29 15:03:33 +00:00
rgw/kms/kmip - pykmip.py needs to make keys too.
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>
This commit is contained in:
parent
1e457c5b58
commit
ef294fea7e
@ -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):
|
||||
"""
|
||||
|
Loading…
Reference in New Issue
Block a user