From: Ricardo Dias Date: Tue, 13 Mar 2018 10:39:15 +0000 (+0000) Subject: qa/rgw: renamed ssl task to openssl_keys X-Git-Tag: v13.1.0~543^2 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=98e4b331aa8def506958a175b2160b905b9c69e6;p=ceph.git qa/rgw: renamed ssl task to openssl_keys The ssl task located in a python file called `ssl.py` will generate python module loading conflicts with the `ssl` system module, when running QA tests using vstart_runner.py. Signed-off-by: Ricardo Dias --- diff --git a/qa/suites/rgw/verify/frontend/civetweb_ssl.yaml b/qa/suites/rgw/verify/frontend/civetweb_ssl.yaml index 9d4368ee539..c0e83524a82 100644 --- a/qa/suites/rgw/verify/frontend/civetweb_ssl.yaml +++ b/qa/suites/rgw/verify/frontend/civetweb_ssl.yaml @@ -1,5 +1,5 @@ overrides: - ssl: + openssl_keys: root: client: client.0 key-type: rsa:4096 diff --git a/qa/suites/rgw/verify/tasks/rgw_tests.yaml b/qa/suites/rgw/verify/tasks/rgw_tests.yaml index be55267e541..e04492c551a 100644 --- a/qa/suites/rgw/verify/tasks/rgw_tests.yaml +++ b/qa/suites/rgw/verify/tasks/rgw_tests.yaml @@ -5,7 +5,7 @@ tasks: - install: flavor: notcmalloc - ceph: -- ssl: +- openssl_keys: - rgw: client.0: valgrind: [--tool=memcheck] diff --git a/qa/tasks/openssl_keys.py b/qa/tasks/openssl_keys.py new file mode 100644 index 00000000000..9ebf5feb338 --- /dev/null +++ b/qa/tasks/openssl_keys.py @@ -0,0 +1,225 @@ +""" +Generates and installs a signed SSL certificate. +""" +import argparse +import logging +import os + +from teuthology import misc +from teuthology.exceptions import ConfigError +from teuthology.orchestra import run +from teuthology.task import Task + +log = logging.getLogger(__name__) + +class SSL(Task): + """ + Generates and installs a signed SSL certificate. + + To create a self-signed certificate: + + - ssl: + # certificate name + root: # results in root.key and root.crt + + # [required] make the private key and certificate available in this client's test directory + client: client.0 + + # common name, defaults to `hostname`. chained certificates must not share a common name + cn: teuthology + + # private key type for -newkey, defaults to rsa:2048 + key-type: rsa:4096 + + # install the certificate as trusted on these clients: + install: [client.0, client.1] + + + To create a certificate signed by a ca certificate: + + - ssl: + root: (self-signed certificate as above) + ... + + cert-for-client1: + client: client.1 + + # use another ssl certificate (by 'name') as the certificate authority + ca: root # --CAkey=root.key -CA=root.crt + + # embed the private key in the certificate file + embed-key: true + """ + + def __init__(self, ctx, config): + super(SSL, self).__init__(ctx, config) + self.certs = [] + self.installed = [] + + def setup(self): + # global dictionary allows other tasks to look up certificate paths + if not hasattr(self.ctx, 'ssl_certificates'): + self.ctx.ssl_certificates = {} + + # use testdir/ca as a working directory + self.cadir = '/'.join((misc.get_testdir(self.ctx), 'ca')) + + for name, config in self.config.items(): + # names must be unique to avoid clobbering each others files + if name in self.ctx.ssl_certificates: + raise ConfigError('ssl: duplicate certificate name {}'.format(name)) + + # create the key and certificate + cert = self.create_cert(name, config) + + self.ctx.ssl_certificates[name] = cert + self.certs.append(cert) + + # install as trusted on the requested clients + for client in config.get('install', []): + installed = self.install_cert(cert, client) + self.installed.append(installed) + + def teardown(self): + """ + Clean up any created/installed certificate files. + """ + for cert in self.certs: + self.remove_cert(cert) + + for installed in self.installed: + self.uninstall_cert(installed) + + def create_cert(self, name, config): + """ + Create a certificate with the given configuration. + """ + cert = argparse.Namespace() + cert.name = name + cert.key_type = config.get('key-type', 'rsa:2048') + + cert.client = config.get('client', None) + if not cert.client: + raise ConfigError('ssl: missing required field "client"') + + (cert.remote,) = self.ctx.cluster.only(cert.client).remotes.keys() + + cert.remote.run(args=['mkdir', '-p', self.cadir]) + + cert.key = '{}/{}.key'.format(self.cadir, cert.name) + cert.certificate = '{}/{}.crt'.format(self.cadir, cert.name) + + # provide the common name in -subj to avoid the openssl command prompts + subject = '/CN={}'.format(config.get('cn', cert.remote.hostname)) + + # if a ca certificate is provided, use it to sign the new certificate + ca = config.get('ca', None) + if ca: + # the ca certificate must have been created by a prior ssl task + ca_cert = self.ctx.ssl_certificates.get(ca, None) + if not ca_cert: + raise ConfigError('ssl: ca {} not found for certificate {}' + .format(ca, cert.name)) + + # these commands are run on the ca certificate's client because + # they need access to its private key and cert + + # generate a private key and signing request + csr = '{}/{}.csr'.format(self.cadir, cert.name) + ca_cert.remote.run(args=['openssl', 'req', '-nodes', + '-newkey', cert.key_type, '-keyout', cert.key, + '-out', csr, '-subj', subject]) + + # create the signed certificate + ca_cert.remote.run(args=['openssl', 'x509', '-req', '-in', csr, + '-CA', ca_cert.certificate, '-CAkey', ca_cert.key, '-CAcreateserial', + '-out', cert.certificate, '-days', '365', '-sha256']) + + srl = '{}/{}.srl'.format(self.cadir, ca_cert.name) + ca_cert.remote.run(args=['rm', csr, srl]) # clean up the signing request and serial + + # verify the new certificate against its ca cert + ca_cert.remote.run(args=['openssl', 'verify', + '-CAfile', ca_cert.certificate, cert.certificate]) + + if cert.remote != ca_cert.remote: + # copy to remote client + self.remote_copy_file(ca_cert.remote, cert.certificate, cert.remote, cert.certificate) + self.remote_copy_file(ca_cert.remote, cert.key, cert.remote, cert.key) + # clean up the local copies + ca_cert.remote.run(args=['rm', cert.certificate, cert.key]) + # verify the remote certificate (requires ca to be in its trusted ca certificate store) + cert.remote.run(args=['openssl', 'verify', cert.certificate]) + else: + # otherwise, generate a private key and use it to self-sign a new certificate + cert.remote.run(args=['openssl', 'req', '-x509', '-nodes', + '-newkey', cert.key_type, '-keyout', cert.key, + '-days', '365', '-out', cert.certificate, '-subj', subject]) + + if config.get('embed-key', False): + # append the private key to the certificate file + cert.remote.run(args=['cat', cert.key, run.Raw('>>'), cert.certificate]) + + return cert + + def remove_cert(self, cert): + """ + Delete all of the files associated with the given certificate. + """ + # remove the private key and certificate + cert.remote.run(args=['rm', '-f', cert.certificate, cert.key]) + + # remove ca subdirectory if it's empty + cert.remote.run(args=['rmdir', '--ignore-fail-on-non-empty', self.cadir]) + + def install_cert(self, cert, client): + """ + Install as a trusted ca certificate on the given client. + """ + (remote,) = self.ctx.cluster.only(client).remotes.keys() + + installed = argparse.Namespace() + installed.remote = remote + + if remote.os.package_type == 'deb': + installed.path = '/usr/local/share/ca-certificates/{}.crt'.format(cert.name) + installed.command = ['sudo', 'update-ca-certificates'] + else: + installed.path = '/usr/share/pki/ca-trust-source/anchors/{}.crt'.format(cert.name) + installed.command = ['sudo', 'update-ca-trust'] + + cp_or_mv = 'cp' + if remote != cert.remote: + # copy into remote cadir (with mkdir if necessary) + remote.run(args=['mkdir', '-p', self.cadir]) + self.remote_copy_file(cert.remote, cert.certificate, remote, cert.certificate) + cp_or_mv = 'mv' # move this remote copy into the certificate store + + # install into certificate store as root + remote.run(args=['sudo', cp_or_mv, cert.certificate, installed.path]) + remote.run(args=installed.command) + + return installed + + def uninstall_cert(self, installed): + """ + Uninstall a certificate from the trusted certificate store. + """ + installed.remote.run(args=['sudo', 'rm', installed.path]) + installed.remote.run(args=installed.command) + + def remote_copy_file(self, from_remote, from_path, to_remote, to_path): + """ + Copies a file from one remote to another. + + The remotes don't have public-key auth for 'scp' or misc.copy_file(), + so this copies through an intermediate local tmp file. + """ + log.info('copying from {}:{} to {}:{}...'.format(from_remote, from_path, to_remote, to_path)) + local_path = from_remote.get_file(from_path) + try: + to_remote.put_file(local_path, to_path) + finally: + os.remove(local_path) + +task = SSL diff --git a/qa/tasks/ssl.py b/qa/tasks/ssl.py deleted file mode 100644 index 9ebf5feb338..00000000000 --- a/qa/tasks/ssl.py +++ /dev/null @@ -1,225 +0,0 @@ -""" -Generates and installs a signed SSL certificate. -""" -import argparse -import logging -import os - -from teuthology import misc -from teuthology.exceptions import ConfigError -from teuthology.orchestra import run -from teuthology.task import Task - -log = logging.getLogger(__name__) - -class SSL(Task): - """ - Generates and installs a signed SSL certificate. - - To create a self-signed certificate: - - - ssl: - # certificate name - root: # results in root.key and root.crt - - # [required] make the private key and certificate available in this client's test directory - client: client.0 - - # common name, defaults to `hostname`. chained certificates must not share a common name - cn: teuthology - - # private key type for -newkey, defaults to rsa:2048 - key-type: rsa:4096 - - # install the certificate as trusted on these clients: - install: [client.0, client.1] - - - To create a certificate signed by a ca certificate: - - - ssl: - root: (self-signed certificate as above) - ... - - cert-for-client1: - client: client.1 - - # use another ssl certificate (by 'name') as the certificate authority - ca: root # --CAkey=root.key -CA=root.crt - - # embed the private key in the certificate file - embed-key: true - """ - - def __init__(self, ctx, config): - super(SSL, self).__init__(ctx, config) - self.certs = [] - self.installed = [] - - def setup(self): - # global dictionary allows other tasks to look up certificate paths - if not hasattr(self.ctx, 'ssl_certificates'): - self.ctx.ssl_certificates = {} - - # use testdir/ca as a working directory - self.cadir = '/'.join((misc.get_testdir(self.ctx), 'ca')) - - for name, config in self.config.items(): - # names must be unique to avoid clobbering each others files - if name in self.ctx.ssl_certificates: - raise ConfigError('ssl: duplicate certificate name {}'.format(name)) - - # create the key and certificate - cert = self.create_cert(name, config) - - self.ctx.ssl_certificates[name] = cert - self.certs.append(cert) - - # install as trusted on the requested clients - for client in config.get('install', []): - installed = self.install_cert(cert, client) - self.installed.append(installed) - - def teardown(self): - """ - Clean up any created/installed certificate files. - """ - for cert in self.certs: - self.remove_cert(cert) - - for installed in self.installed: - self.uninstall_cert(installed) - - def create_cert(self, name, config): - """ - Create a certificate with the given configuration. - """ - cert = argparse.Namespace() - cert.name = name - cert.key_type = config.get('key-type', 'rsa:2048') - - cert.client = config.get('client', None) - if not cert.client: - raise ConfigError('ssl: missing required field "client"') - - (cert.remote,) = self.ctx.cluster.only(cert.client).remotes.keys() - - cert.remote.run(args=['mkdir', '-p', self.cadir]) - - cert.key = '{}/{}.key'.format(self.cadir, cert.name) - cert.certificate = '{}/{}.crt'.format(self.cadir, cert.name) - - # provide the common name in -subj to avoid the openssl command prompts - subject = '/CN={}'.format(config.get('cn', cert.remote.hostname)) - - # if a ca certificate is provided, use it to sign the new certificate - ca = config.get('ca', None) - if ca: - # the ca certificate must have been created by a prior ssl task - ca_cert = self.ctx.ssl_certificates.get(ca, None) - if not ca_cert: - raise ConfigError('ssl: ca {} not found for certificate {}' - .format(ca, cert.name)) - - # these commands are run on the ca certificate's client because - # they need access to its private key and cert - - # generate a private key and signing request - csr = '{}/{}.csr'.format(self.cadir, cert.name) - ca_cert.remote.run(args=['openssl', 'req', '-nodes', - '-newkey', cert.key_type, '-keyout', cert.key, - '-out', csr, '-subj', subject]) - - # create the signed certificate - ca_cert.remote.run(args=['openssl', 'x509', '-req', '-in', csr, - '-CA', ca_cert.certificate, '-CAkey', ca_cert.key, '-CAcreateserial', - '-out', cert.certificate, '-days', '365', '-sha256']) - - srl = '{}/{}.srl'.format(self.cadir, ca_cert.name) - ca_cert.remote.run(args=['rm', csr, srl]) # clean up the signing request and serial - - # verify the new certificate against its ca cert - ca_cert.remote.run(args=['openssl', 'verify', - '-CAfile', ca_cert.certificate, cert.certificate]) - - if cert.remote != ca_cert.remote: - # copy to remote client - self.remote_copy_file(ca_cert.remote, cert.certificate, cert.remote, cert.certificate) - self.remote_copy_file(ca_cert.remote, cert.key, cert.remote, cert.key) - # clean up the local copies - ca_cert.remote.run(args=['rm', cert.certificate, cert.key]) - # verify the remote certificate (requires ca to be in its trusted ca certificate store) - cert.remote.run(args=['openssl', 'verify', cert.certificate]) - else: - # otherwise, generate a private key and use it to self-sign a new certificate - cert.remote.run(args=['openssl', 'req', '-x509', '-nodes', - '-newkey', cert.key_type, '-keyout', cert.key, - '-days', '365', '-out', cert.certificate, '-subj', subject]) - - if config.get('embed-key', False): - # append the private key to the certificate file - cert.remote.run(args=['cat', cert.key, run.Raw('>>'), cert.certificate]) - - return cert - - def remove_cert(self, cert): - """ - Delete all of the files associated with the given certificate. - """ - # remove the private key and certificate - cert.remote.run(args=['rm', '-f', cert.certificate, cert.key]) - - # remove ca subdirectory if it's empty - cert.remote.run(args=['rmdir', '--ignore-fail-on-non-empty', self.cadir]) - - def install_cert(self, cert, client): - """ - Install as a trusted ca certificate on the given client. - """ - (remote,) = self.ctx.cluster.only(client).remotes.keys() - - installed = argparse.Namespace() - installed.remote = remote - - if remote.os.package_type == 'deb': - installed.path = '/usr/local/share/ca-certificates/{}.crt'.format(cert.name) - installed.command = ['sudo', 'update-ca-certificates'] - else: - installed.path = '/usr/share/pki/ca-trust-source/anchors/{}.crt'.format(cert.name) - installed.command = ['sudo', 'update-ca-trust'] - - cp_or_mv = 'cp' - if remote != cert.remote: - # copy into remote cadir (with mkdir if necessary) - remote.run(args=['mkdir', '-p', self.cadir]) - self.remote_copy_file(cert.remote, cert.certificate, remote, cert.certificate) - cp_or_mv = 'mv' # move this remote copy into the certificate store - - # install into certificate store as root - remote.run(args=['sudo', cp_or_mv, cert.certificate, installed.path]) - remote.run(args=installed.command) - - return installed - - def uninstall_cert(self, installed): - """ - Uninstall a certificate from the trusted certificate store. - """ - installed.remote.run(args=['sudo', 'rm', installed.path]) - installed.remote.run(args=installed.command) - - def remote_copy_file(self, from_remote, from_path, to_remote, to_path): - """ - Copies a file from one remote to another. - - The remotes don't have public-key auth for 'scp' or misc.copy_file(), - so this copies through an intermediate local tmp file. - """ - log.info('copying from {}:{} to {}:{}...'.format(from_remote, from_path, to_remote, to_path)) - local_path = from_remote.get_file(from_path) - try: - to_remote.put_file(local_path, to_path) - finally: - os.remove(local_path) - -task = SSL