From 1c0c36bf40879435e17c1701dbc2927b8dfd4699 Mon Sep 17 00:00:00 2001 From: Lukas Bezdicka Date: Tue, 14 Apr 2015 12:16:25 -0400 Subject: [PATCH] Refactor SSL setup to use CA to sign certificates Users can now use Packstack generated CA or provide Subordinate CA to packstack to sign certificates used by OpenStack. Resolves: rhbz#1163866 Change-Id: Idd89dbb7f197a194fd87576be6d95a75d059231e --- docs/packstack.rst | 64 ++-- packstack/installer/validators.py | 20 +- packstack/modules/ospluginutils.py | 70 +++++ packstack/plugins/amqp_002.py | 88 +----- packstack/plugins/ceilometer_800.py | 13 + packstack/plugins/cinder_250.py | 13 + packstack/plugins/dashboard_500.py | 62 ++-- packstack/plugins/glance_200.py | 13 + packstack/plugins/heat_650.py | 13 + packstack/plugins/ironic_275.py | 12 + packstack/plugins/manila_255.py | 13 + packstack/plugins/neutron_350.py | 14 + packstack/plugins/nova_300.py | 86 ++++++ packstack/plugins/sahara_800.py | 16 +- packstack/plugins/ssl_001.py | 288 ++++++++++++++++++ packstack/plugins/trove_850.py | 13 + packstack/puppet/templates/amqp.pp | 22 +- .../puppet/templates/ceilometer_rabbitmq.pp | 35 ++- packstack/puppet/templates/cinder_rabbitmq.pp | 19 +- .../templates/glance_ceilometer_rabbitmq.pp | 28 +- packstack/puppet/templates/heat_rabbitmq.pp | 19 +- packstack/puppet/templates/horizon.pp | 45 +-- packstack/puppet/templates/ironic_rabbitmq.pp | 19 +- packstack/puppet/templates/manila_rabbitmq.pp | 16 +- packstack/puppet/templates/nagios_server.pp | 2 +- .../puppet/templates/neutron_rabbitmq.pp | 20 +- .../templates/nova_ceilometer_rabbitmq.pp | 38 ++- .../puppet/templates/nova_common_qpid.pp | 4 +- .../puppet/templates/nova_common_rabbitmq.pp | 24 +- packstack/puppet/templates/nova_compute.pp | 14 +- packstack/puppet/templates/nova_vncproxy.pp | 10 +- packstack/puppet/templates/sahara_rabbitmq.pp | 19 +- packstack/puppet/templates/trove_rabbitmq.pp | 20 +- requirements.txt | 2 + setup.py | 2 +- tests/installer/test_run_setup.py | 3 +- tests/installer/test_validators.py | 10 +- tools/test-requires | 1 + 38 files changed, 938 insertions(+), 232 deletions(-) create mode 100644 packstack/plugins/ssl_001.py diff --git a/docs/packstack.rst b/docs/packstack.rst index cdefb702d..afddc6c3d 100644 --- a/docs/packstack.rst +++ b/docs/packstack.rst @@ -104,6 +104,43 @@ Global Options **CONFIG_USE_SUBNETS** Specify 'y' if you want to use subnet addresses (in CIDR format) instead of interface names in following options: CONFIG_NOVA_COMPUTE_PRIVIF, CONFIG_NOVA_NETWORK_PRIVIF, CONFIG_NOVA_NETWORK_PUBIF, CONFIG_NEUTRON_OVS_BRIDGE_IFACES, CONFIG_NEUTRON_LB_INTERFACE_MAPPINGS, CONFIG_NEUTRON_OVS_TUNNEL_IF. This is useful for cases when interface names are not same on all installation hosts. +SSL setup +--------- +Packstack supports ability to be get CA certificate and use it to sign all certificates used in Openstack deployment. Standard workflow for this would be creating Subordinate CA. If Root CA is given to Packstack it is not safe to use it for anything else. If no CA is provided and CONFIG_SSL_CACERT_SELFSIGN is set to 'y' Packstack will use Selfsigned CA. + +**CONFIG_SSL_CERT_DIR** + Enter the path to use to store generated SSL certificates in. + +**CONFIG_SSL_CACERT_FILE** + Specify filepath for CA cert file. If CONFIG_SSL_CACERT_SELFSIGN is set to 'n' it has to be preexisting file. + +**CONFIG_SSL_CACERT_KEY_FILE** + Specify filepath for CA cert key file. If CONFIG_SSL_CACERT_SELFSIGN is set to 'n' it has to be preexisting file. + +**CONFIG_SSL_CACERT_SELFSIGN** + Specify 'y' if you want Packstack to pregenerate the CA Certificate. + +SSL selfsigned CACert options +----------------------------- + +**CONFIG_SELFSIGN_CACERT_SUBJECT_C** + Enter the selfsigned CAcert subject country. + +**CONFIG_SELFSIGN_CACERT_SUBJECT_ST** + Enter the selfsigned CAcert subject state. + +**CONFIG_SELFSIGN_CACERT_SUBJECT_L** + Enter the selfsigned CAcert subject location. + +**CONFIG_SELFSIGN_CACERT_SUBJECT_O** + Enter the selfsigned CAcert subject organization. + +**CONFIG_SELFSIGN_CACERT_SUBJECT_OU** + Enter the selfsigned CAcert subject organizational unit. + +**CONFIG_SELFSIGN_CACERT_SUBJECT_CN** + Enter the selfsigned CAcert subject common name. + vCenter Config Parameters ------------------------- @@ -227,21 +264,6 @@ AMQP Config SSL parameters **CONFIG_AMQP_NSS_CERTDB_PW** Password for the NSS certificate database of the AMQP service. -**CONFIG_AMQP_SSL_PORT** - Port on which the AMQP service listens for SSL connections. - -**CONFIG_AMQP_SSL_CACERT_FILE** - File name of the CAcertificate that the AMQP service will use for verification. - -**CONFIG_AMQP_SSL_CERT_FILE** - File name of the certificate that the AMQP service will use for verification. - -**CONFIG_AMQP_SSL_KEY_FILE** - File name of the private key that the AMQP service will use for verification. - -**CONFIG_AMQP_SSL_SELF_SIGNED** - Specify 'y' to automatically generate a self-signed SSL certificate and key. ['y', 'n'] - AMQP Config Athentication parameters ------------------------------------ @@ -687,6 +709,12 @@ Nova Options **CONFIG_NOVA_COMPUTE_MANAGER** Manager that runs the Compute service. +**CONFIG_VNC_SSL_CERT** + PEM encoded certificate to be used for ssl on the https server, leave blank if one should be generated, this certificate should not require a passphrase. If CONFIG_HORIZON_SSL is set to 'n' this parameter is ignored. + +**CONFIG_VNC_SSL_KEY** + SSL keyfile corresponding to the certificate if one was entered. If CONFIG_HORIZON_SSL is set to 'n' this parameter is ignored. + Nova Network Options -------------------- @@ -816,13 +844,13 @@ OpenStack Horizon Config parameters SSL Config parameters --------------------- -**CONFIG_SSL_CERT** +**CONFIG_HORIZON_SSL_CERT** PEM-encoded certificate to be used for SSL connections on the https server (the certificate should not require a passphrase). To generate a certificate, leave blank. -**CONFIG_SSL_KEY** +**CONFIG_HORIZON_SSL_KEY** SSL keyfile corresponding to the certificate if one was specified. -**CONFIG_SSL_CACHAIN** +**CONFIG_HORIZON_SSL_CACHAIN** PEM-encoded CA certificates from which the certificate chain of the server certificate can be assembled. OpenStack Swift Config parameters diff --git a/packstack/installer/validators.py b/packstack/installer/validators.py index b3beeefce..03d214d10 100644 --- a/packstack/installer/validators.py +++ b/packstack/installer/validators.py @@ -32,7 +32,8 @@ __all__ = ('ParamValidationError', 'validate_integer', 'validate_float', 'validate_multi_ip', 'validate_file', 'validate_ping', 'validate_multi_ping', 'validate_ssh', 'validate_multi_ssh', 'validate_sshkey', 'validate_ldap_url', 'validate_ldap_dn', - 'validate_export', 'validate_multi_export') + 'validate_export', 'validate_multi_export', + 'validate_writeable_directory') def validate_integer(param, options=None): @@ -197,6 +198,23 @@ def validate_file(param, options=None): raise ParamValidationError(msg % param) +def validate_writeable_directory(param, options=None): + """ + Raises ParamValidationError if provided directory does not exist or + is not writeable. + """ + if not param: + return + + options = options or [] + if not ((os.path.isdir(param) and os.access(param, os.W_OK)) or + os.access(os.path.join(param, os.pardir), os.W_OK)): + logging.debug('validate_writeable_directory(%s, options=%s) failed.' % + (param, options)) + msg = 'Given directory does not exist or is not writeable: %s' + raise ParamValidationError(msg % param) + + def validate_ping(param, options=None): """ Raises ParamValidationError if provided host does not answer to ICMP diff --git a/packstack/modules/ospluginutils.py b/packstack/modules/ospluginutils.py index c13f0bbf2..c27fe1752 100644 --- a/packstack/modules/ospluginutils.py +++ b/packstack/modules/ospluginutils.py @@ -14,7 +14,10 @@ import os import yaml +from OpenSSL import crypto +from time import time from packstack.installer import basedefs +from packstack.installer import utils from packstack.installer.setup_controller import Controller controller = Controller() @@ -84,6 +87,73 @@ def createFirewallResources(hiera_key, default_value='{}'): return "create_resources(packstack::firewall, %s)\n\n" % hiera_function +def generate_ssl_cert(config, host, service, ssl_key_file, ssl_cert_file): + """ + Wrapper on top of openssl + """ + # We have to check whether the certificate already exists + cert_dir = os.path.join(config['CONFIG_SSL_CERT_DIR'], 'certs') + local_cert_name = host + os.path.basename(ssl_cert_file) + local_cert_path = os.path.join(cert_dir, local_cert_name) + if not os.path.exists(local_cert_path): + ca_file = open(config['CONFIG_SSL_CACERT_FILE'], 'rt').read() + ca_key_file = open(config['CONFIG_SSL_CACERT_KEY_FILE'], 'rt').read() + ca_key = crypto.load_privatekey(crypto.FILETYPE_PEM, ca_key_file) + ca = crypto.load_certificate(crypto.FILETYPE_PEM, ca_file) + + k = crypto.PKey() + k.generate_key(crypto.TYPE_RSA, 4096) + mail = config['CONFIG_SELFSIGN_CACERT_SUBJECT_MAIL'] + hostinfo = config['HOST_DETAILS'][host] + fqdn = hostinfo['fqdn'] + cert = crypto.X509() + subject = cert.get_subject() + subject.C = config['CONFIG_SELFSIGN_CACERT_SUBJECT_C'] + subject.ST = config['CONFIG_SELFSIGN_CACERT_SUBJECT_ST'] + subject.L = config['CONFIG_SELFSIGN_CACERT_SUBJECT_L'] + subject.O = config['CONFIG_SELFSIGN_CACERT_SUBJECT_O'] + subject.OU = config['CONFIG_SELFSIGN_CACERT_SUBJECT_OU'] + subject.CN = "%s/%s" % (service, fqdn) + subject.emailAddress = mail + + cert.add_extensions([ + crypto.X509Extension( + "keyUsage".encode('ascii'), + False, + "nonRepudiation,digitalSignature,keyEncipherment".encode('ascii')), + crypto.X509Extension( + "extendedKeyUsage".encode('ascii'), + False, + "clientAuth,serverAuth".encode('ascii')), + ]) + + cert.gmtime_adj_notBefore(0) + cert.gmtime_adj_notAfter(315360000) + cert.set_issuer(ca.get_subject()) + cert.set_pubkey(k) + serial = int(time()) + cert.set_serial_number(serial) + cert.sign(ca_key, 'sha1') + + final_cert = crypto.dump_certificate(crypto.FILETYPE_PEM, cert) + final_key = crypto.dump_privatekey(crypto.FILETYPE_PEM, k) + deliver_ssl_file(ca_file, config['CONFIG_SSL_CACERT_FILE'], host) + deliver_ssl_file(final_cert, ssl_cert_file, host) + deliver_ssl_file(final_key, ssl_key_file, host) + + with open(local_cert_path, 'w') as f: + f.write(final_cert) + + +def deliver_ssl_file(content, path, host): + server = utils.ScriptRunner(host) + server.append("grep -- '{content}' {path} || " + "echo '{content}' > {path} ".format( + content=content, + path=path)) + server.execute() + + def gethostlist(CONF): hosts = [] for key, value in CONF.items(): diff --git a/packstack/plugins/amqp_002.py b/packstack/plugins/amqp_002.py index 9104fe8ba..177cb0efd 100644 --- a/packstack/plugins/amqp_002.py +++ b/packstack/plugins/amqp_002.py @@ -26,6 +26,7 @@ from packstack.modules.documentation import update_params_usage from packstack.modules.ospluginutils import appendManifestFile from packstack.modules.ospluginutils import createFirewallResources from packstack.modules.ospluginutils import getManifestTemplate +from packstack.modules.ospluginutils import generate_ssl_cert # ------------- AMQP Packstack Plugin Initialization -------------- @@ -106,68 +107,6 @@ def initConfig(controller): "USE_DEFAULT": False, "NEED_CONFIRM": True, "CONDITION": False}, - - {"CMD_OPTION": "amqp-ssl-port", - "PROMPT": "Enter the SSL port for the AMQP service", - "OPTION_LIST": [], - "VALIDATORS": [validators.validate_not_empty], - "DEFAULT_VALUE": "5671", - "MASK_INPUT": False, - "LOOSE_VALIDATION": True, - "CONF_NAME": "CONFIG_AMQP_SSL_PORT", - "USE_DEFAULT": False, - "NEED_CONFIRM": False, - "CONDITION": False}, - - {"CMD_OPTION": "amqp-ssl-cacert-file", - "PROMPT": ("Enter the filename of the SSL CAcertificate for the AMQP" - " service"), - "OPTION_LIST": [], - "VALIDATORS": [validators.validate_not_empty], - "DEFAULT_VALUE": "/etc/pki/tls/certs/amqp_selfcert.pem", - "MASK_INPUT": False, - "LOOSE_VALIDATION": True, - "CONF_NAME": "CONFIG_AMQP_SSL_CACERT_FILE", - "USE_DEFAULT": False, - "NEED_CONFIRM": False, - "CONDITION": False}, - - {"CMD_OPTION": "amqp-ssl-cert-file", - "PROMPT": ("Enter the filename of the SSL certificate for the AMQP " - "service"), - "OPTION_LIST": [], - "VALIDATORS": [validators.validate_not_empty], - "DEFAULT_VALUE": "/etc/pki/tls/certs/amqp_selfcert.pem", - "MASK_INPUT": False, - "LOOSE_VALIDATION": True, - "CONF_NAME": "CONFIG_AMQP_SSL_CERT_FILE", - "USE_DEFAULT": False, - "NEED_CONFIRM": False, - "CONDITION": False}, - - {"CMD_OPTION": "amqp-ssl-key-file", - "PROMPT": "Enter the private key filename", - "OPTION_LIST": [], - "VALIDATORS": [validators.validate_not_empty], - "DEFAULT_VALUE": "/etc/pki/tls/private/amqp_selfkey.pem", - "MASK_INPUT": False, - "LOOSE_VALIDATION": True, - "CONF_NAME": "CONFIG_AMQP_SSL_KEY_FILE", - "USE_DEFAULT": False, - "NEED_CONFIRM": False, - "CONDITION": False}, - - {"CMD_OPTION": "amqp-ssl-self-signed", - "PROMPT": "Generate Self Signed SSL Certificate", - "OPTION_LIST": ["y", "n"], - "VALIDATORS": [validators.validate_not_empty], - "DEFAULT_VALUE": "y", - "MASK_INPUT": False, - "LOOSE_VALIDATION": True, - "CONF_NAME": "CONFIG_AMQP_SSL_SELF_SIGNED", - "USE_DEFAULT": False, - "NEED_CONFIRM": False, - "CONDITION": False}, ] update_params_usage(basedefs.PACKSTACK_DOC, params, sectioned=False) group = {"GROUP_NAME": "AMQPSSL", @@ -227,25 +166,22 @@ def initSequences(controller): def create_manifest(config, messages): server = utils.ScriptRunner(config['CONFIG_AMQP_HOST']) if config['CONFIG_AMQP_ENABLE_SSL'] == 'y': - config['CONFIG_AMQP_ENABLE_SSL'] = True + config['CONFIG_AMQP_SSL_ENABLED'] = True config['CONFIG_AMQP_PROTOCOL'] = 'ssl' config['CONFIG_AMQP_CLIENTS_PORT'] = "5671" - if config['CONFIG_AMQP_SSL_SELF_SIGNED'] == 'y': - server.append( - "openssl req -batch -new -x509 -nodes -keyout %s " - "-out %s -days 1095" - % (config['CONFIG_AMQP_SSL_KEY_FILE'], - config['CONFIG_AMQP_SSL_CERT_FILE']) - ) - server.execute() + amqp_host = config['CONFIG_AMQP_HOST'] + service = 'AMQP' + ssl_key_file = '/etc/pki/tls/private/ssl_amqp.key' + ssl_cert_file = '/etc/pki/tls/certs/ssl_amqp.crt' + cacert = config['CONFIG_AMQP_SSL_CACERT_FILE'] = ( + config['CONFIG_SSL_CACERT_FILE'] + ) + generate_ssl_cert(config, amqp_host, service, ssl_key_file, + ssl_cert_file) else: # Set default values config['CONFIG_AMQP_CLIENTS_PORT'] = "5672" - config['CONFIG_AMQP_SSL_PORT'] = "5671" - config['CONFIG_AMQP_SSL_CERT_FILE'] = '' - config['CONFIG_AMQP_SSL_KEY_FILE'] = '' - config['CONFIG_AMQP_NSS_CERTDB_PW'] = '' - config['CONFIG_AMQP_ENABLE_SSL'] = False + config['CONFIG_AMQP_SSL_ENABLED'] = False config['CONFIG_AMQP_PROTOCOL'] = 'tcp' if config['CONFIG_AMQP_ENABLE_AUTH'] == 'n': diff --git a/packstack/plugins/ceilometer_800.py b/packstack/plugins/ceilometer_800.py index f301ce045..c6b92a838 100644 --- a/packstack/plugins/ceilometer_800.py +++ b/packstack/plugins/ceilometer_800.py @@ -29,6 +29,7 @@ from packstack.modules.shortcuts import get_mq from packstack.modules.ospluginutils import appendManifestFile from packstack.modules.ospluginutils import createFirewallResources from packstack.modules.ospluginutils import getManifestTemplate +from packstack.modules.ospluginutils import generate_ssl_cert # ------------- Ceilometer Packstack Plugin Initialization -------------- @@ -274,6 +275,18 @@ def create_manifest(config, messages): sentinel_fallbacks = '' config['CONFIG_REDIS_SENTINEL_FALLBACKS'] = sentinel_fallbacks + if config['CONFIG_AMQP_ENABLE_SSL'] == 'y': + ssl_cert_file = config['CONFIG_CEILOMETER_SSL_CERT'] = ( + '/etc/pki/tls/certs/ssl_amqp_ceilometer.crt' + ) + ssl_key_file = config['CONFIG_CEILOMETER_SSL_KEY'] = ( + '/etc/pki/tls/private/ssl_amqp_ceilometer.key' + ) + ssl_host = config['CONFIG_CONTROLLER_HOST'] + service = 'ceilometer' + generate_ssl_cert(config, ssl_host, service, ssl_key_file, + ssl_cert_file) + fw_details = dict() key = "ceilometer_api" fw_details.setdefault(key, {}) diff --git a/packstack/plugins/cinder_250.py b/packstack/plugins/cinder_250.py index 160a857d3..a860f011d 100644 --- a/packstack/plugins/cinder_250.py +++ b/packstack/plugins/cinder_250.py @@ -31,6 +31,7 @@ from packstack.modules.shortcuts import get_mq from packstack.modules.ospluginutils import appendManifestFile from packstack.modules.ospluginutils import createFirewallResources from packstack.modules.ospluginutils import getManifestTemplate +from packstack.modules.ospluginutils import generate_ssl_cert # ------------------ Cinder Packstack Plugin initialization ------------------ @@ -657,6 +658,18 @@ def create_keystone_manifest(config, messages): def create_manifest(config, messages): + if config['CONFIG_AMQP_ENABLE_SSL'] == 'y': + ssl_host = config['CONFIG_STORAGE_HOST'] + ssl_cert_file = config['CONFIG_CINDER_SSL_CERT'] = ( + '/etc/pki/tls/certs/ssl_amqp_cinder.crt' + ) + ssl_key_file = config['CONFIG_CINDER_SSL_KEY'] = ( + '/etc/pki/tls/private/ssl_amqp_cinder.key' + ) + service = 'cinder' + generate_ssl_cert(config, ssl_host, service, ssl_key_file, + ssl_cert_file) + manifestdata = getManifestTemplate(get_mq(config, "cinder")) manifestfile = "%s_cinder.pp" % config['CONFIG_STORAGE_HOST'] manifestdata += getManifestTemplate("cinder") diff --git a/packstack/plugins/dashboard_500.py b/packstack/plugins/dashboard_500.py index a18c90857..2b4e790c3 100644 --- a/packstack/plugins/dashboard_500.py +++ b/packstack/plugins/dashboard_500.py @@ -20,13 +20,15 @@ import os import uuid from packstack.installer import basedefs -from packstack.installer import validators from packstack.installer import exceptions from packstack.installer import utils +from packstack.installer import validators from packstack.modules.documentation import update_params_usage from packstack.modules.ospluginutils import appendManifestFile from packstack.modules.ospluginutils import getManifestTemplate +from packstack.modules.ospluginutils import generate_ssl_cert +from packstack.modules.ospluginutils import deliver_ssl_file # ------------- Horizon Packstack Plugin Initialization -------------- @@ -59,9 +61,6 @@ def initConfig(controller): params = [ {"CMD_OPTION": "os-ssl-cert", - "USAGE": ("PEM encoded certificate to be used for ssl on the https " - "server, leave blank if one should be generated, this " - "certificate should not require a passphrase"), "PROMPT": ("Enter the path to a PEM encoded certificate to be used " "on the https server, leave blank if one should be " "generated, this certificate should not require " @@ -71,14 +70,13 @@ def initConfig(controller): "DEFAULT_VALUE": '', "MASK_INPUT": False, "LOOSE_VALIDATION": True, - "CONF_NAME": "CONFIG_SSL_CERT", + "CONF_NAME": "CONFIG_HORIZON_SSL_CERT", "USE_DEFAULT": False, "NEED_CONFIRM": False, - "CONDITION": False}, + "CONDITION": False, + "DEPRECATES": ['CONFIG_SSL_CERT']}, {"CMD_OPTION": "os-ssl-key", - "USAGE": ("SSL keyfile corresponding to the certificate if one was " - "entered"), "PROMPT": ("Enter the SSL keyfile corresponding to the certificate " "if one was entered"), "OPTION_LIST": [], @@ -86,14 +84,13 @@ def initConfig(controller): "DEFAULT_VALUE": "", "MASK_INPUT": False, "LOOSE_VALIDATION": True, - "CONF_NAME": "CONFIG_SSL_KEY", + "CONF_NAME": "CONFIG_HORIZON_SSL_KEY", "USE_DEFAULT": False, "NEED_CONFIRM": False, - "CONDITION": False}, + "CONDITION": False, + "DEPRECATES": ['CONFIG_SSL_KEY']}, {"CMD_OPTION": "os-ssl-cachain", - "USAGE": ("PEM encoded CA certificates from which the certificate " - "chain of the server certificate can be assembled."), "PROMPT": ("Enter the CA cahin file corresponding to the certificate " "if one was entered"), "OPTION_LIST": [], @@ -101,10 +98,11 @@ def initConfig(controller): "DEFAULT_VALUE": "", "MASK_INPUT": False, "LOOSE_VALIDATION": True, - "CONF_NAME": "CONFIG_SSL_CACHAIN", + "CONF_NAME": "CONFIG_HORIZON_SSL_CACERT", "USE_DEFAULT": False, "NEED_CONFIRM": False, - "CONDITION": False}, + "CONDITION": False, + "DEPRECATES": ['CONFIG_SSL_CACHAIN']}, ] update_params_usage(basedefs.PACKSTACK_DOC, params, sectioned=False) group = {"GROUP_NAME": "OSSSL", @@ -138,15 +136,14 @@ def create_manifest(config, messages): config["CONFIG_HORIZON_PORT"] = 80 sslmanifestdata = '' if config["CONFIG_HORIZON_SSL"] == 'y': - config["CONFIG_HORIZON_SSL"] = True config["CONFIG_HORIZON_PORT"] = 443 proto = "https" # Are we using the users cert/key files - if config["CONFIG_SSL_CERT"]: - ssl_cert = config["CONFIG_SSL_CERT"] - ssl_key = config["CONFIG_SSL_KEY"] - ssl_chain = config["CONFIG_SSL_CACHAIN"] + if config["CONFIG_HORIZON_SSL_CERT"]: + ssl_cert_file = config["CONFIG_HORIZON_SSL_CERT"] + ssl_key_file = config["CONFIG_HORIZON_SSL_KEY"] + ssl_chain_file = config["CONFIG_HORIZON_SSL_CACERT"] if not os.path.exists(ssl_cert): raise exceptions.ParamValidationError( @@ -160,19 +157,32 @@ def create_manifest(config, messages): raise exceptions.ParamValidationError( "The file %s doesn't exist" % ssl_chain) - resources = config.setdefault('RESOURCES', {}) - host_resources = resources.setdefault(horizon_host, []) - host_resources.append((ssl_cert, 'ssl_ps_server.crt')) - host_resources.append((ssl_key, 'ssl_ps_server.key')) - host_resources.append((ssl_chain, 'ssl_ps_chain.crt')) + final_cert = open(ssl_cert_file, 'rt').read() + final_key = open(ssl_key_file, 'rt').read() + final_cacert = open(ssl_chain_file, 'rt').read() + host = config['CONFIG_CONTROLLER_HOST'] + deliver_ssl_file(final_cacert, ssl_chain_file, host) + deliver_ssl_file(final_cert, ssl_cert_file, host) + deliver_ssl_file(final_key, ssl_key_file, host) + else: + ssl_cert_file = config["CONFIG_HORIZON_SSL_CERT"] = ( + '/etc/pki/tls/certs/ssl_dashboard.crt' + ) + ssl_key_file = config["CONFIG_HORIZON_SSL_KEY"] = ( + '/etc/pki/tls/private/ssl_dashboard.key' + ) + cacert = config['CONFIG_SSL_CACERT_FILE'] + config["CONFIG_HORIZON_SSL_CACERT"] = cacert + ssl_host = config['CONFIG_CONTROLLER_HOST'] + service = 'dashboard' + generate_ssl_cert(config, ssl_host, service, ssl_key_file, + ssl_cert_file) messages.append( "%sNOTE%s : A certificate was generated to be used for ssl, " "You should change the ssl certificate configured in " "/etc/httpd/conf.d/ssl.conf on %s to use a CA signed cert." % (utils.COLORS['red'], utils.COLORS['nocolor'], horizon_host)) - else: - config["CONFIG_HORIZON_SSL"] = False config["CONFIG_HORIZON_NEUTRON_LB"] = False config["CONFIG_HORIZON_NEUTRON_FW"] = False diff --git a/packstack/plugins/glance_200.py b/packstack/plugins/glance_200.py index d79575ee6..2b09a22a0 100644 --- a/packstack/plugins/glance_200.py +++ b/packstack/plugins/glance_200.py @@ -26,6 +26,7 @@ from packstack.modules.shortcuts import get_mq from packstack.modules.ospluginutils import appendManifestFile from packstack.modules.ospluginutils import createFirewallResources from packstack.modules.ospluginutils import getManifestTemplate +from packstack.modules.ospluginutils import generate_ssl_cert # ------------- Glance Packstack Plugin Initialization -------------- @@ -118,6 +119,18 @@ def create_keystone_manifest(config, messages): def create_manifest(config, messages): + if config['CONFIG_AMQP_ENABLE_SSL'] == 'y': + ssl_host = config['CONFIG_STORAGE_HOST'] + ssl_cert_file = config['CONFIG_GLANCE_SSL_CERT'] = ( + '/etc/pki/tls/certs/ssl_amqp_glance.crt' + ) + ssl_key_file = config['CONFIG_GLANCE_SSL_KEY'] = ( + '/etc/pki/tls/private/ssl_amqp_glance.key' + ) + service = 'glance' + generate_ssl_cert(config, ssl_host, service, ssl_key_file, + ssl_cert_file) + manifestfile = "%s_glance.pp" % config['CONFIG_STORAGE_HOST'] manifestdata = getManifestTemplate("glance") if config['CONFIG_CEILOMETER_INSTALL'] == 'y': diff --git a/packstack/plugins/heat_650.py b/packstack/plugins/heat_650.py index f07fe523d..c7e4fe7f4 100644 --- a/packstack/plugins/heat_650.py +++ b/packstack/plugins/heat_650.py @@ -28,6 +28,7 @@ from packstack.modules.shortcuts import get_mq from packstack.modules.ospluginutils import appendManifestFile from packstack.modules.ospluginutils import createFirewallResources from packstack.modules.ospluginutils import getManifestTemplate +from packstack.modules.ospluginutils import generate_ssl_cert # ------------- Heat Packstack Plugin Initialization -------------- @@ -170,6 +171,18 @@ def initSequences(controller): # ------------------------ step functions ------------------------- def create_manifest(config, messages): + if config['CONFIG_AMQP_ENABLE_SSL'] == 'y': + ssl_host = config['CONFIG_CONTROLLER_HOST'] + ssl_cert_file = config['CONFIG_HEAT_SSL_CERT'] = ( + '/etc/pki/tls/certs/ssl_amqp_heat.crt' + ) + ssl_key_file = config['CONFIG_HEAT_SSL_KEY'] = ( + '/etc/pki/tls/private/ssl_amqp_heat.key' + ) + service = 'heat' + generate_ssl_cert(config, ssl_host, service, ssl_key_file, + ssl_cert_file) + manifestfile = "%s_heat.pp" % config['CONFIG_CONTROLLER_HOST'] manifestdata = getManifestTemplate(get_mq(config, "heat")) manifestdata += getManifestTemplate("heat") diff --git a/packstack/plugins/ironic_275.py b/packstack/plugins/ironic_275.py index 9f2679da7..f8034f289 100644 --- a/packstack/plugins/ironic_275.py +++ b/packstack/plugins/ironic_275.py @@ -26,6 +26,7 @@ from packstack.modules.shortcuts import get_mq from packstack.modules.ospluginutils import appendManifestFile from packstack.modules.ospluginutils import createFirewallResources from packstack.modules.ospluginutils import getManifestTemplate +from packstack.modules.ospluginutils import generate_ssl_cert # ------------------ Ironic Packstack Plugin initialization ------------------ @@ -90,6 +91,17 @@ def initSequences(controller): # -------------------------- step functions -------------------------- def create_manifest(config, messages): + if config['CONFIG_AMQP_ENABLE_SSL'] == 'y': + ssl_host = config['CONFIG_CONTROLLER_HOST'] + ssl_cert_file = config['CONFIG_IRONIC_SSL_CERT'] = ( + '/etc/pki/tls/certs/ssl_amqp_ironic.crt' + ) + ssl_key_file = config['CONFIG_IRONIC_SSL_KEY'] = ( + '/etc/pki/tls/private/ssl_amqp_ironic.key' + ) + service = 'ironic' + generate_ssl_cert(config, ssl_host, service, ssl_key_file, + ssl_cert_file) manifestfile = "%s_ironic.pp" % config['CONFIG_CONTROLLER_HOST'] manifestdata = getManifestTemplate(get_mq(config, "ironic")) diff --git a/packstack/plugins/manila_255.py b/packstack/plugins/manila_255.py index 37453ac05..d62da58b8 100644 --- a/packstack/plugins/manila_255.py +++ b/packstack/plugins/manila_255.py @@ -26,6 +26,7 @@ from packstack.modules.shortcuts import get_mq from packstack.modules.ospluginutils import getManifestTemplate from packstack.modules.ospluginutils import appendManifestFile from packstack.modules.ospluginutils import createFirewallResources +from packstack.modules.ospluginutils import generate_ssl_cert # ------------- Manila Packstack Plugin Initialization -------------- @@ -491,6 +492,18 @@ def create_manifest(config, messages): if config['CONFIG_UNSUPPORTED'] != 'y': config['CONFIG_STORAGE_HOST'] = config['CONFIG_CONTROLLER_HOST'] + if config['CONFIG_AMQP_ENABLE_SSL'] == 'y': + ssl_host = config['CONFIG_STORAGE_HOST'] + ssl_cert_file = config['CONFIG_MANILA_SSL_CERT'] = ( + '/etc/pki/tls/certs/ssl_amqp_manila.crt' + ) + ssl_key_file = config['CONFIG_MANILA_SSL_KEY'] = ( + '/etc/pki/tls/private/ssl_amqp_manila.key' + ) + service = 'manila' + generate_ssl_cert(config, ssl_host, service, ssl_key_file, + ssl_cert_file) + # Change these from text to Boolean values boolean_keys = ['CONFIG_MANILA_GENERIC_DRV_HANDLES_SHARE_SERVERS', 'CONFIG_MANILA_NETAPP_DRV_HANDLES_SHARE_SERVERS'] diff --git a/packstack/plugins/neutron_350.py b/packstack/plugins/neutron_350.py index 92d954f16..4bd1bbdca 100644 --- a/packstack/plugins/neutron_350.py +++ b/packstack/plugins/neutron_350.py @@ -29,6 +29,7 @@ from packstack.modules.shortcuts import get_mq from packstack.modules.ospluginutils import appendManifestFile from packstack.modules.ospluginutils import createFirewallResources from packstack.modules.ospluginutils import getManifestTemplate +from packstack.modules.ospluginutils import generate_ssl_cert # ------------- Neutron Packstack Plugin Initialization -------------- @@ -518,7 +519,20 @@ def create_manifests(config, messages): plugin_manifest = 'neutron_ml2_plugin' + if config['CONFIG_AMQP_ENABLE_SSL'] == 'y': + ssl_cert_file = config['CONFIG_NEUTRON_SSL_CERT'] = ( + '/etc/pki/tls/certs/ssl_amqp_neutron.crt' + ) + ssl_key_file = config['CONFIG_NEUTRON_SSL_KEY'] = ( + '/etc/pki/tls/private/ssl_amqp_neutron.key' + ) + service = 'neutron' + for host in q_hosts: + if config['CONFIG_AMQP_ENABLE_SSL'] == 'y': + generate_ssl_cert(config, host, service, ssl_key_file, + ssl_cert_file) + manifest_file = "%s_neutron.pp" % (host,) manifest_data = getManifestTemplate("neutron") manifest_data += getManifestTemplate(get_mq(config, "neutron")) diff --git a/packstack/plugins/nova_300.py b/packstack/plugins/nova_300.py index 637cd6afb..458dd895a 100644 --- a/packstack/plugins/nova_300.py +++ b/packstack/plugins/nova_300.py @@ -32,6 +32,7 @@ from packstack.modules.shortcuts import get_mq from packstack.modules.ospluginutils import appendManifestFile from packstack.modules.ospluginutils import createFirewallResources from packstack.modules.ospluginutils import getManifestTemplate +from packstack.modules.ospluginutils import generate_ssl_cert from packstack.modules.ospluginutils import manifestfiles # ------------- Nova Packstack Plugin Initialization -------------- @@ -128,6 +129,33 @@ def initConfig(controller): "NEED_CONFIRM": False, "CONDITION": False}, + {"CMD_OPTION": "nova-ssl-cert", + "PROMPT": ("Enter the path to a PEM encoded certificate to be used " + "on the https server, leave blank if one should be " + "generated, this certificate should not require " + "a passphrase"), + "OPTION_LIST": [], + "VALIDATORS": [], + "DEFAULT_VALUE": '', + "MASK_INPUT": False, + "LOOSE_VALIDATION": True, + "CONF_NAME": "CONFIG_VNC_SSL_CERT", + "USE_DEFAULT": False, + "NEED_CONFIRM": False, + "CONDITION": False}, + + {"CMD_OPTION": "nova-ssl-key", + "PROMPT": ("Enter the SSL keyfile corresponding to the certificate " + "if one was entered"), + "OPTION_LIST": [], + "VALIDATORS": [], + "DEFAULT_VALUE": "", + "MASK_INPUT": False, + "LOOSE_VALIDATION": True, + "CONF_NAME": "CONFIG_VNC_SSL_KEY", + "USE_DEFAULT": False, + "NEED_CONFIRM": False, + "CONDITION": False}, ], "NOVA_NETWORK": [ @@ -458,6 +486,11 @@ def create_conductor_manifest(config, messages): def create_compute_manifest(config, messages): global compute_hosts, network_hosts + if config["CONFIG_HORIZON_SSL"] == 'y': + config["CONFIG_VNCPROXY_PROTOCOL"] = "https" + else: + config["CONFIG_VNCPROXY_PROTOCOL"] = "http" + migrate_protocol = config['CONFIG_NOVA_COMPUTE_MIGRATE_PROTOCOL'] if migrate_protocol == 'ssh': config['CONFIG_NOVA_COMPUTE_MIGRATE_URL'] = ( @@ -550,6 +583,18 @@ def create_compute_manifest(config, messages): messages.append(str(ex)) if config['CONFIG_CEILOMETER_INSTALL'] == 'y': + if config['CONFIG_AMQP_ENABLE_SSL'] == 'y': + ssl_cert_file = config['CONFIG_CEILOMETER_SSL_CERT'] = ( + '/etc/pki/tls/certs/ssl_amqp_ceilometer.crt' + ) + ssl_key_file = config['CONFIG_CEILOMETER_SSL_KEY'] = ( + '/etc/pki/tls/private/ssl_amqp_ceilometer.key' + ) + ssl_host = config['CONFIG_CONTROLLER_HOST'] + service = 'ceilometer' + generate_ssl_cert(config, host, service, ssl_key_file, + ssl_cert_file) + mq_template = get_mq(config, "nova_ceilometer") manifestdata += getManifestTemplate(mq_template) manifestdata += getManifestTemplate("nova_ceilometer") @@ -630,6 +675,33 @@ def create_sched_manifest(config, messages): def create_vncproxy_manifest(config, messages): + if config["CONFIG_HORIZON_SSL"] == 'y': + if config["CONFIG_VNC_SSL_CERT"]: + ssl_cert_file = config["CONFIG_VNC_SSL_CERT"] + ssl_key_file = config["CONFIG_VNC_SSL_KEY"] + if not os.path.exists(ssl_cert): + raise exceptions.ParamValidationError( + "The file %s doesn't exist" % ssl_cert_file) + + if not os.path.exists(ssl_key): + raise exceptions.ParamValidationError( + "The file %s doesn't exist" % ssl_key_file) + + final_cert = open(ssl_cert_file, 'rt').read() + final_key = open(ssl_key_file, 'rt').read() + deliver_ssl_file(final_cert, ssl_cert_file, config['CONFIG_CONTROLLER_HOST']) + deliver_ssl_file(final_key, ssl_key_file, config['CONFIG_CONTROLLER_HOST']) + + else: + config["CONFIG_VNC_SSL_CERT"] = '/etc/pki/tls/certs/ssl_vnc.crt' + config["CONFIG_VNC_SSL_KEY"] = '/etc/pki/tls/private/ssl_vnc.key' + ssl_key_file = config["CONFIG_VNC_SSL_KEY"] + ssl_cert_file = config["CONFIG_VNC_SSL_CERT"] + ssl_host = config['CONFIG_CONTROLLER_HOST'] + service = 'vnc' + generate_ssl_cert(config, ssl_host, service, ssl_key_file, + ssl_cert_file) + manifestfile = "%s_nova.pp" % config['CONFIG_CONTROLLER_HOST'] manifestdata = getManifestTemplate("nova_vncproxy") appendManifestFile(manifestfile, manifestdata) @@ -681,6 +753,20 @@ def create_common_manifest(config, messages): data += getManifestTemplate("nova_common_nopw") appendManifestFile(os.path.split(manifestfile)[1], data) + if config['CONFIG_AMQP_ENABLE_SSL'] == 'y': + nova_hosts = compute_hosts + nova_hosts |= set([config.get('CONFIG_CONTROLLER_HOST')]) + ssl_cert_file = config['CONFIG_NOVA_SSL_CERT'] = ( + '/etc/pki/tls/certs/ssl_amqp_nova.crt' + ) + ssl_key_file = config['CONFIG_NOVA_SSL_KEY'] = ( + '/etc/pki/tls/private/ssl_amqp_nova.key' + ) + service = 'nova' + for host in nova_hosts: + generate_ssl_cert(config, host, service, + ssl_key_file, ssl_cert_file) + def create_neutron_manifest(config, messages): if config['CONFIG_NEUTRON_INSTALL'] != "y": diff --git a/packstack/plugins/sahara_800.py b/packstack/plugins/sahara_800.py index dd7ba557c..73dd5d35b 100644 --- a/packstack/plugins/sahara_800.py +++ b/packstack/plugins/sahara_800.py @@ -26,6 +26,7 @@ from packstack.modules.shortcuts import get_mq from packstack.modules.ospluginutils import appendManifestFile from packstack.modules.ospluginutils import createFirewallResources from packstack.modules.ospluginutils import getManifestTemplate +from packstack.modules.ospluginutils import generate_ssl_cert # ------------------ Sahara installer initialization ------------------ @@ -90,13 +91,24 @@ def initSequences(controller): def create_keystone_manifest(config, messages): if config['CONFIG_UNSUPPORTED'] != 'y': config['CONFIG_SAHARA_HOST'] = config['CONFIG_CONTROLLER_HOST'] - - manifestfile = "%s_keystone.pp" % config['CONFIG_CONTROLLER_HOST'] + manifestfile = "%s_keystone.pp" % config['CONFIG_SAHARA_HOST'] manifestdata = getManifestTemplate("keystone_sahara") appendManifestFile(manifestfile, manifestdata) def create_manifest(config, messages): + if config['CONFIG_AMQP_ENABLE_SSL'] == 'y': + ssl_host = config['CONFIG_SAHARA_HOST'] + ssl_cert_file = config['CONFIG_SAHARA_SSL_CERT'] = ( + '/etc/pki/tls/certs/ssl_amqp_sahara.crt' + ) + ssl_key_file = config['CONFIG_SAHARA_SSL_KEY'] = ( + '/etc/pki/tls/private/ssl_amqp_sahara.key' + ) + service = 'sahara' + generate_ssl_cert(config, ssl_host, service, ssl_key_file, + ssl_cert_file) + manifestfile = "%s_sahara.pp" % config['CONFIG_STORAGE_HOST'] manifestdata = getManifestTemplate(get_mq(config, "sahara")) manifestdata += getManifestTemplate("sahara.pp") diff --git a/packstack/plugins/ssl_001.py b/packstack/plugins/ssl_001.py new file mode 100644 index 000000000..8a8779c01 --- /dev/null +++ b/packstack/plugins/ssl_001.py @@ -0,0 +1,288 @@ +# -*- coding: utf-8 -*- +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" +Plugin responsible for managing SSL options +""" +import os + +from OpenSSL import crypto +from socket import gethostname + +from packstack.installer import basedefs +from packstack.installer import utils +from packstack.installer import validators + +from packstack.modules.documentation import update_params_usage + +# ------------- SSL Packstack Plugin Initialization -------------- + +PLUGIN_NAME = "SSL" +PLUGIN_NAME_COLORED = utils.color_text(PLUGIN_NAME, 'blue') + + +def initConfig(controller): + params = { + "SSL": [ + {"CMD_OPTION": "ssl-cacert-file", + "PROMPT": ("Enter the filename of the SSL CAcertificate, if the" + " CONFIG_SSL_CACERT_SELFSIGN is set to y the path " + "will be CONFIG_SSL_CERT_DIR/certs/selfcert.crt"), + "OPTION_LIST": [], + "VALIDATORS": [validators.validate_not_empty], + "DEFAULT_VALUE": "/etc/pki/tls/certs/selfcert.crt", + "MASK_INPUT": False, + "LOOSE_VALIDATION": True, + "CONF_NAME": "CONFIG_SSL_CACERT_FILE", + "USE_DEFAULT": False, + "NEED_CONFIRM": False, + "CONDITION": False}, + + {"CMD_OPTION": "ssl-cacert-key-file", + "PROMPT": ("Enter the filename of the SSL CAcertificate Key file" + ", if the CONFIG_SSL_CACERT_SELFSIGN is set to y the " + "path will be CONFIG_SSL_CERT_DIR/keys/selfkey.key"), + "OPTION_LIST": [], + "VALIDATORS": [validators.validate_not_empty], + "DEFAULT_VALUE": "/etc/pki/tls/private/selfkey.key", + "MASK_INPUT": False, + "LOOSE_VALIDATION": True, + "CONF_NAME": "CONFIG_SSL_CACERT_KEY_FILE", + "USE_DEFAULT": False, + "NEED_CONFIRM": False, + "CONDITION": False}, + + {"CMD_OPTION": "ssl-cert-dir", + "PROMPT": ("Enter the path to use to store generated SSL certificates in"), + "OPTION_LIST": [], + "VALIDATORS": [validators.validate_not_empty, + validators.validate_writeable_directory], + "DEFAULT_VALUE": "~/packstackca/", + "MASK_INPUT": False, + "LOOSE_VALIDATION": True, + "CONF_NAME": "CONFIG_SSL_CERT_DIR", + "USE_DEFAULT": False, + "NEED_CONFIRM": False, + "CONDITION": False}, + + {"CMD_OPTION": "ssl-cacert-selfsign", + "PROMPT": "Should packstack use selfsigned CAcert.", + "OPTION_LIST": ["y", "n"], + "VALIDATORS": [validators.validate_options], + "DEFAULT_VALUE": "y", + "MASK_INPUT": False, + "LOOSE_VALIDATION": False, + "CONF_NAME": 'CONFIG_SSL_CACERT_SELFSIGN', + "USE_DEFAULT": False, + "NEED_CONFIRM": False, + "CONDITION": False} + ], + + "SSL_SELFSIGN": [ + {"CMD_OPTION": "selfsign-cacert-subject-country", + "PROMPT": "Enter the selfsigned CAcert subject country.", + "OPTION_LIST": [], + "VALIDATORS": [validators.validate_not_empty], + "DEFAULT_VALUE": "--", + "MASK_INPUT": False, + "LOOSE_VALIDATION": False, + "CONF_NAME": 'CONFIG_SELFSIGN_CACERT_SUBJECT_C', + "USE_DEFAULT": False, + "NEED_CONFIRM": False, + "CONDITION": False}, + + {"CMD_OPTION": "selfsign-cacert-subject-state", + "PROMPT": "Enter the selfsigned CAcert subject state.", + "OPTION_LIST": [], + "VALIDATORS": [validators.validate_not_empty], + "DEFAULT_VALUE": "State", + "MASK_INPUT": False, + "LOOSE_VALIDATION": False, + "CONF_NAME": 'CONFIG_SELFSIGN_CACERT_SUBJECT_ST', + "USE_DEFAULT": False, + "NEED_CONFIRM": False, + "CONDITION": False}, + + {"CMD_OPTION": "selfsign-cacert-subject-location", + "PROMPT": "Enter the selfsigned CAcert subject location.", + "OPTION_LIST": [], + "VALIDATORS": [validators.validate_not_empty], + "DEFAULT_VALUE": "City", + "MASK_INPUT": False, + "LOOSE_VALIDATION": False, + "CONF_NAME": 'CONFIG_SELFSIGN_CACERT_SUBJECT_L', + "USE_DEFAULT": False, + "NEED_CONFIRM": False, + "CONDITION": False}, + + {"CMD_OPTION": "selfsign-cacert-subject-organization", + "PROMPT": "Enter the selfsigned CAcert subject organization.", + "OPTION_LIST": [], + "VALIDATORS": [validators.validate_not_empty], + "DEFAULT_VALUE": "openstack", + "MASK_INPUT": False, + "LOOSE_VALIDATION": False, + "CONF_NAME": 'CONFIG_SELFSIGN_CACERT_SUBJECT_O', + "USE_DEFAULT": False, + "NEED_CONFIRM": False, + "CONDITION": False}, + + {"CMD_OPTION": "selfsign-cacert-subject-organizational-unit", + "PROMPT": "Enter the selfsigned CAcert subject organizational unit.", + "OPTION_LIST": [], + "VALIDATORS": [validators.validate_not_empty], + "DEFAULT_VALUE": "packstack", + "MASK_INPUT": False, + "LOOSE_VALIDATION": False, + "CONF_NAME": 'CONFIG_SELFSIGN_CACERT_SUBJECT_OU', + "USE_DEFAULT": False, + "NEED_CONFIRM": False, + "CONDITION": False}, + + {"CMD_OPTION": "selfsign-cacert-subject-common-name", + "PROMPT": "Enter the selfsigned CAcert subject common name.", + "OPTION_LIST": [], + "VALIDATORS": [validators.validate_not_empty], + "DEFAULT_VALUE": gethostname(), + "MASK_INPUT": False, + "LOOSE_VALIDATION": False, + "CONF_NAME": 'CONFIG_SELFSIGN_CACERT_SUBJECT_CN', + "USE_DEFAULT": False, + "NEED_CONFIRM": False, + "CONDITION": False}, + + {"CMD_OPTION": "selfsign-cacert-subject-email", + "PROMPT": "Enter the selfsigned CAcert subject admin email.", + "OPTION_LIST": [], + "VALIDATORS": [validators.validate_not_empty], + "DEFAULT_VALUE": "admin@%s" % gethostname(), + "MASK_INPUT": False, + "LOOSE_VALIDATION": False, + "CONF_NAME": 'CONFIG_SELFSIGN_CACERT_SUBJECT_MAIL', + "USE_DEFAULT": False, + "NEED_CONFIRM": False, + "CONDITION": False}, + ] + } + update_params_usage(basedefs.PACKSTACK_DOC, params) + + groups = [ + {"GROUP_NAME": "SSL", + "DESCRIPTION": "SSL Config parameters", + "PRE_CONDITION": lambda x: 'yes', + "PRE_CONDITION_MATCH": "yes", + "POST_CONDITION": False, + "POST_CONDITION_MATCH": True}, + + {"GROUP_NAME": "SSL_SELFSIGN", + "DESCRIPTION": "SSL selfsigned CAcert Config parameters", + "PRE_CONDITION": 'CONFIG_SSL_CACERT_SELFSIGN', + "PRE_CONDITION_MATCH": "y", + "POST_CONDITION": False, + "POST_CONDITION_MATCH": True} + ] + for group in groups: + controller.addGroup(group, params[group['GROUP_NAME']]) + + +def initSequences(controller): + ssl_steps = [ + {'title': 'Setting up CACERT', + 'functions': [create_self_signed_cert]} + ] + controller.addSequence("Setting up SSL", [], [], + ssl_steps) + + +# ------------------------- helper functions ------------------------- + +def create_self_signed_cert(config, messages): + """ + OpenSSL wrapper to create selfsigned CA. + """ + + if (config['CONFIG_AMQP_ENABLE_SSL'] != 'y' and + config["CONFIG_HORIZON_SSL"] != 'y'): + return + + if not os.path.isdir(config['CONFIG_SSL_CERT_DIR']): + os.mkdir(config['CONFIG_SSL_CERT_DIR']) + certs = os.path.join(config['CONFIG_SSL_CERT_DIR'], 'certs') + if not os.path.isdir(certs): + os.mkdir(certs) + keys = os.path.join(config['CONFIG_SSL_CERT_DIR'], 'keys') + if not os.path.isdir(keys): + os.mkdir(keys) + + if config['CONFIG_SSL_CACERT_SELFSIGN'] != 'y': + return + + CERT_FILE = config['CONFIG_SSL_CACERT_FILE'] = ( + '%s/certs/selfcert.crt' % config['CONFIG_SSL_CERT_DIR'] + ) + KEY_FILE = config['CONFIG_SSL_CACERT_KEY_FILE'] = ( + '%s/keys/selfcert.crt' % config['CONFIG_SSL_CERT_DIR'] + ) + if not os.path.exists(CERT_FILE) or not os.path.exists(KEY_FILE): + # create a key pair + k = crypto.PKey() + k.generate_key(crypto.TYPE_RSA, 4096) + + # create a self-signed cert + mail = config['CONFIG_SELFSIGN_CACERT_SUBJECT_MAIL'] + cert = crypto.X509() + subject = cert.get_subject() + subject.C = config['CONFIG_SELFSIGN_CACERT_SUBJECT_C'] + subject.ST = config['CONFIG_SELFSIGN_CACERT_SUBJECT_ST'] + subject.L = config['CONFIG_SELFSIGN_CACERT_SUBJECT_L'] + subject.O = config['CONFIG_SELFSIGN_CACERT_SUBJECT_O'] + subject.OU = config['CONFIG_SELFSIGN_CACERT_SUBJECT_OU'] + subject.CN = config['CONFIG_SELFSIGN_CACERT_SUBJECT_CN'] + subject.emailAddress = mail + cert.set_serial_number(1000) + cert.gmtime_adj_notBefore(0) + cert.gmtime_adj_notAfter(10 * 365 * 24 * 60 * 60) + cert.set_issuer(cert.get_subject()) + cert.set_pubkey(k) + + # CA extensions + cert.add_extensions([ + crypto.X509Extension("basicConstraints".encode('ascii'), False, + "CA:TRUE".encode('ascii')), + crypto.X509Extension("keyUsage".encode('ascii'), False, + "keyCertSign, cRLSign".encode('ascii')), + crypto.X509Extension("subjectKeyIdentifier".encode('ascii'), False, + "hash".encode('ascii'), + subject=cert), + ]) + + cert.add_extensions([ + crypto.X509Extension( + "authorityKeyIdentifier".encode('ascii'), False, + "keyid:always".encode('ascii'), issuer=cert) + ]) + + cert.sign(k, 'sha1') + + open((CERT_FILE), "wt").write( + crypto.dump_certificate(crypto.FILETYPE_PEM, cert)) + open((KEY_FILE), "wt").write( + crypto.dump_privatekey(crypto.FILETYPE_PEM, k)) + + messages.append( + "%sNOTE%s : A selfsigned CA certificate was generated to be used " + "for ssl, you should still change it do subordinate CA cert. In " + "any case please save the contents of %s." + % (utils.COLORS['red'], utils.COLORS['nocolor'], + config['CONFIG_SSL_CERT_DIR'])) diff --git a/packstack/plugins/trove_850.py b/packstack/plugins/trove_850.py index 17380ad43..e023a8f76 100644 --- a/packstack/plugins/trove_850.py +++ b/packstack/plugins/trove_850.py @@ -26,6 +26,7 @@ from packstack.modules.shortcuts import get_mq from packstack.modules.ospluginutils import appendManifestFile from packstack.modules.ospluginutils import createFirewallResources from packstack.modules.ospluginutils import getManifestTemplate +from packstack.modules.ospluginutils import generate_ssl_cert # ------------------ Trove Packstack Plugin initialization ------------------ @@ -143,6 +144,18 @@ def create_keystone_manifest(config, messages): def create_manifest(config, messages): + if config['CONFIG_AMQP_ENABLE_SSL'] == 'y': + ssl_cert_file = config['CONFIG_TROVE_SSL_CERT'] = ( + '/etc/pki/tls/certs/ssl_amqp_trove.crt' + ) + ssl_key_file = config['CONFIG_TROVE_SSL_KEY'] = ( + '/etc/pki/tls/private/ssl_amqp_trove.key' + ) + ssl_host = config['CONFIG_CONTROLLER_HOST'] + service = 'trove' + generate_ssl_cert(config, ssl_host, service, ssl_key_file, + ssl_cert_file) + if (config['CONFIG_TROVE_NOVA_USER'] == 'admin' and config['CONFIG_TROVE_NOVA_PW'] == ''): config['CONFIG_TROVE_NOVA_PW'] = config['CONFIG_KEYSTONE_ADMIN_PW'] diff --git a/packstack/puppet/templates/amqp.pp b/packstack/puppet/templates/amqp.pp index 017ad284b..8891eae9c 100644 --- a/packstack/puppet/templates/amqp.pp +++ b/packstack/puppet/templates/amqp.pp @@ -1,5 +1,5 @@ $amqp = hiera('CONFIG_AMQP_BACKEND') -$amqp_enable_ssl = hiera('CONFIG_AMQP_ENABLE_SSL') +$amqp_enable_ssl = hiera('CONFIG_AMQP_SSL_ENABLED') case $amqp { 'qpid': { @@ -21,10 +21,9 @@ define enable_rabbitmq { } if $::amqp_enable_ssl { - - $kombu_ssl_ca_certs = hiera('CONFIG_AMQP_SSL_CACERT_FILE') - $kombu_ssl_keyfile = hiera('CONFIG_AMQP_SSL_KEY_FILE') - $kombu_ssl_certfile = hiera('CONFIG_AMQP_SSL_CERT_FILE') + $kombu_ssl_ca_certs = hiera('CONFIG_AMQP_SSL_CACERT_FILE', undef) + $kombu_ssl_keyfile = '/etc/pki/tls/private/ssl_amqp.key' + $kombu_ssl_certfile = '/etc/pki/tls/certs/ssl_amqp.crt' $files_to_set_owner = [ $kombu_ssl_keyfile, $kombu_ssl_certfile ] file { $files_to_set_owner: @@ -35,7 +34,8 @@ define enable_rabbitmq { } class { '::rabbitmq': - ssl_port => hiera('CONFIG_AMQP_SSL_PORT'), + port => undef, + ssl_port => hiera('CONFIG_AMQP_CLIENTS_PORT'), ssl_only => true, ssl => $::amqp_enable_ssl, ssl_cacert => $kombu_ssl_ca_certs, @@ -46,7 +46,7 @@ define enable_rabbitmq { package_provider => 'yum', admin_enable => false, # FIXME: it's ugly to not to require client certs - ssl_fail_if_no_peer_cert => false, + ssl_fail_if_no_peer_cert => true, config_variables => { 'tcp_listen_options' => '[binary,{packet, raw},{reuseaddr, true},{backlog, 128},{nodelay, true},{exit_on_close, false},{keepalive, true}]', 'loopback_users' => '[]', @@ -100,10 +100,10 @@ define enable_qpid($enable_ssl = 'n', $enable_auth = 'n') { default => 'no', }, clustered => false, - ssl_port => hiera('CONFIG_AMQP_SSL_PORT'), - ssl => hiera('CONFIG_AMQP_ENABLE_SSL'), - ssl_cert => hiera('CONFIG_AMQP_SSL_CERT_FILE'), - ssl_key => hiera('CONFIG_AMQP_SSL_KEY_FILE'), + ssl_port => hiera('CONFIG_AMQP_CLIENTS_PORT'), + ssl => hiera('CONFIG_AMQP_SSL_ENABLED'), + ssl_cert => '/etc/pki/tls/certs/ssl_amqp.crt', + ssl_key => '/etc/pki/tls/private/ssl_amqp.key', ssl_database_password => hiera('CONFIG_AMQP_NSS_CERTDB_PW'), } diff --git a/packstack/puppet/templates/ceilometer_rabbitmq.pp b/packstack/puppet/templates/ceilometer_rabbitmq.pp index 3a8854a49..7d10c2fda 100644 --- a/packstack/puppet/templates/ceilometer_rabbitmq.pp +++ b/packstack/puppet/templates/ceilometer_rabbitmq.pp @@ -1,10 +1,27 @@ -class { '::ceilometer': - metering_secret => hiera('CONFIG_CEILOMETER_SECRET'), - verbose => true, - debug => hiera('CONFIG_DEBUG_MODE'), - rabbit_host => hiera('CONFIG_AMQP_HOST_URL'), - rabbit_port => hiera('CONFIG_AMQP_CLIENTS_PORT'), - rabbit_use_ssl => hiera('CONFIG_AMQP_ENABLE_SSL'), - rabbit_userid => hiera('CONFIG_AMQP_AUTH_USER'), - rabbit_password => hiera('CONFIG_AMQP_AUTH_PASSWORD'), +$kombu_ssl_ca_certs = hiera('CONFIG_AMQP_SSL_CACERT_FILE', undef) +$kombu_ssl_keyfile = hiera('CONFIG_CEILOMETER_SSL_KEY', undef) +$kombu_ssl_certfile = hiera('CONFIG_CEILOMETER_SSL_CERT', undef) + +if $kombu_ssl_keyfile { + $files_to_set_owner = [ $kombu_ssl_keyfile, $kombu_ssl_certfile ] + file { $files_to_set_owner: + owner => 'ceilometer', + group => 'ceilometer', + require => Package['openstack-ceilometer-common'], + } + File[$files_to_set_owner] ~> Service<||> +} + +class { '::ceilometer': + metering_secret => hiera('CONFIG_CEILOMETER_SECRET'), + verbose => true, + debug => hiera('CONFIG_DEBUG_MODE'), + rabbit_host => hiera('CONFIG_AMQP_HOST_URL'), + rabbit_port => hiera('CONFIG_AMQP_CLIENTS_PORT'), + rabbit_use_ssl => hiera('CONFIG_AMQP_SSL_ENABLED'), + rabbit_userid => hiera('CONFIG_AMQP_AUTH_USER'), + rabbit_password => hiera('CONFIG_AMQP_AUTH_PASSWORD'), + kombu_ssl_ca_certs => $kombu_ssl_ca_certs, + kombu_ssl_keyfile => $kombu_ssl_keyfile, + kombu_ssl_certfile => $kombu_ssl_certfile, } diff --git a/packstack/puppet/templates/cinder_rabbitmq.pp b/packstack/puppet/templates/cinder_rabbitmq.pp index 68545c259..d2f0b0fea 100644 --- a/packstack/puppet/templates/cinder_rabbitmq.pp +++ b/packstack/puppet/templates/cinder_rabbitmq.pp @@ -1,13 +1,30 @@ $cinder_rab_cfg_cinder_db_pw = hiera('CONFIG_CINDER_DB_PW') $cinder_rab_cfg_mariadb_host = hiera('CONFIG_MARIADB_HOST_URL') +$kombu_ssl_ca_certs = hiera('CONFIG_AMQP_SSL_CACERT_FILE', undef) +$kombu_ssl_keyfile = hiera('CONFIG_CINDER_SSL_KEY', undef) +$kombu_ssl_certfile = hiera('CONFIG_CINDER_SSL_CERT', undef) + +if $kombu_ssl_keyfile { + $files_to_set_owner = [ $kombu_ssl_keyfile, $kombu_ssl_certfile ] + file { $files_to_set_owner: + owner => 'cinder', + group => 'cinder', + require => Class['cinder'], + notify => Service['cinder-api'], + } +} + class { '::cinder': rabbit_host => hiera('CONFIG_AMQP_HOST_URL'), rabbit_port => hiera('CONFIG_AMQP_CLIENTS_PORT'), - rabbit_use_ssl => hiera('CONFIG_AMQP_ENABLE_SSL'), + rabbit_use_ssl => hiera('CONFIG_AMQP_SSL_ENABLED'), rabbit_userid => hiera('CONFIG_AMQP_AUTH_USER'), rabbit_password => hiera('CONFIG_AMQP_AUTH_PASSWORD'), database_connection => "mysql://cinder:${cinder_rab_cfg_cinder_db_pw}@${cinder_rab_cfg_mariadb_host}/cinder", verbose => true, debug => hiera('CONFIG_DEBUG_MODE'), + kombu_ssl_ca_certs => $kombu_ssl_ca_certs, + kombu_ssl_keyfile => $kombu_ssl_keyfile, + kombu_ssl_certfile => $kombu_ssl_certfile, } diff --git a/packstack/puppet/templates/glance_ceilometer_rabbitmq.pp b/packstack/puppet/templates/glance_ceilometer_rabbitmq.pp index d2ecb0356..21e1ca4bc 100644 --- a/packstack/puppet/templates/glance_ceilometer_rabbitmq.pp +++ b/packstack/puppet/templates/glance_ceilometer_rabbitmq.pp @@ -1,9 +1,23 @@ +$kombu_ssl_ca_certs = hiera('CONFIG_AMQP_SSL_CACERT_FILE', undef) +$kombu_ssl_keyfile = hiera('CONFIG_GLANCE_SSL_KEY', undef) +$kombu_ssl_certfile = hiera('CONFIG_GLANCE_SSL_CERT', undef) -class { '::glance::notify::rabbitmq': - rabbit_host => hiera('CONFIG_AMQP_HOST_URL'), - rabbit_port => hiera('CONFIG_AMQP_CLIENTS_PORT'), - rabbit_use_ssl => hiera('CONFIG_AMQP_ENABLE_SSL'), - rabbit_userid => hiera('CONFIG_AMQP_AUTH_USER'), - rabbit_password => hiera('CONFIG_AMQP_AUTH_PASSWORD'), +if $kombu_ssl_keyfile { + $files_to_set_owner = [ $kombu_ssl_keyfile, $kombu_ssl_certfile ] + file { $files_to_set_owner: + owner => 'glance', + group => 'glance', + require => Class['::glance::notify::rabbitmq'], + notify => Service['glance-api'], + } +} +class { '::glance::notify::rabbitmq': + rabbit_host => hiera('CONFIG_AMQP_HOST_URL'), + rabbit_port => hiera('CONFIG_AMQP_CLIENTS_PORT'), + rabbit_use_ssl => hiera('CONFIG_AMQP_SSL_ENABLED'), + rabbit_userid => hiera('CONFIG_AMQP_AUTH_USER'), + rabbit_password => hiera('CONFIG_AMQP_AUTH_PASSWORD'), + kombu_ssl_ca_certs => $kombu_ssl_ca_certs, + kombu_ssl_keyfile => $kombu_ssl_keyfile, + kombu_ssl_certfile => $kombu_ssl_certfile, } - diff --git a/packstack/puppet/templates/heat_rabbitmq.pp b/packstack/puppet/templates/heat_rabbitmq.pp index d8d9fc141..dc5adb49b 100644 --- a/packstack/puppet/templates/heat_rabbitmq.pp +++ b/packstack/puppet/templates/heat_rabbitmq.pp @@ -2,6 +2,20 @@ $heat_rabbitmq_cfg_ctrl_host = hiera('CONFIG_KEYSTONE_HOST_URL') $heat_rabbitmq_cfg_heat_db_pw = hiera('CONFIG_HEAT_DB_PW') $heat_rabbitmq_cfg_mariadb_host = hiera('CONFIG_MARIADB_HOST_URL') +$kombu_ssl_ca_certs = hiera('CONFIG_AMQP_SSL_CACERT_FILE', undef) +$kombu_ssl_keyfile = hiera('CONFIG_HEAT_SSL_KEY', undef) +$kombu_ssl_certfile = hiera('CONFIG_HEAT_SSL_CERT', undef) + +if $kombu_ssl_keyfile { + $files_to_set_owner = [ $kombu_ssl_keyfile, $kombu_ssl_certfile ] + file { $files_to_set_owner: + owner => 'heat', + group => 'heat', + require => Package['openstack-heat-common'], + } + File[$files_to_set_owner] ~> Service<||> +} + class { '::heat': keystone_host => $heat_rabbitmq_cfg_ctrl_host, keystone_password => hiera('CONFIG_HEAT_KS_PW'), @@ -10,10 +24,13 @@ class { '::heat': rpc_backend => 'heat.openstack.common.rpc.impl_kombu', rabbit_host => hiera('CONFIG_AMQP_HOST_URL'), rabbit_port => hiera('CONFIG_AMQP_CLIENTS_PORT'), - rabbit_use_ssl => hiera('CONFIG_AMQP_ENABLE_SSL'), + rabbit_use_ssl => hiera('CONFIG_AMQP_SSL_ENABLED'), rabbit_userid => hiera('CONFIG_AMQP_AUTH_USER'), rabbit_password => hiera('CONFIG_AMQP_AUTH_PASSWORD'), verbose => true, debug => hiera('CONFIG_DEBUG_MODE'), database_connection => "mysql://heat:${heat_rabbitmq_cfg_heat_db_pw}@${heat_rabbitmq_cfg_mariadb_host}/heat", + kombu_ssl_ca_certs => $kombu_ssl_ca_certs, + kombu_ssl_keyfile => $kombu_ssl_keyfile, + kombu_ssl_certfile => $kombu_ssl_certfile, } diff --git a/packstack/puppet/templates/horizon.pp b/packstack/puppet/templates/horizon.pp index b6e723ad6..33d21e7f7 100644 --- a/packstack/puppet/templates/horizon.pp +++ b/packstack/puppet/templates/horizon.pp @@ -19,6 +19,11 @@ $bind_host = hiera('CONFIG_IP_VERSION') ? { 'ipv4' => '0.0.0.0', } +$horizon_ssl = hiera('CONFIG_HORIZON_SSL') ? { + 'y' => true, + 'n' => false, +} + class {'::horizon': secret_key => hiera('CONFIG_HORIZON_SECRET_KEY'), keystone_url => "http://${keystone_host}:5000/v2.0", @@ -29,48 +34,18 @@ class {'::horizon': compress_offline => false, django_debug => $is_django_debug, file_upload_temp_dir => '/var/tmp', - listen_ssl => hiera('CONFIG_HORIZON_SSL'), - horizon_cert => '/etc/pki/tls/certs/ssl_ps_server.crt', - horizon_key => '/etc/pki/tls/private/ssl_ps_server.key', - horizon_ca => '/etc/pki/tls/certs/ssl_ps_chain.crt', + listen_ssl => $horizon_ssl, + horizon_cert => hiera('CONFIG_HORIZON_SSL_CERT', undef), + horizon_key => hiera('CONFIG_HORIZON_SSL_KEY', undef), + horizon_ca => hiera('CONFIG_HORIZON_SSL_CACERT', undef), neutron_options => { 'enable_lb' => hiera('CONFIG_HORIZON_NEUTRON_LB'), 'enable_firewall' => hiera('CONFIG_HORIZON_NEUTRON_FW'), }, } -$is_horizon_ssl = hiera('CONFIG_HORIZON_SSL') - -if $is_horizon_ssl == true { - file {'/etc/pki/tls/certs/ps_generate_ssl_certs.ssh': - ensure => file, - content => template('packstack/ssl/generate_ssl_certs.sh.erb'), - mode => '0755', - } - - exec {'/etc/pki/tls/certs/ps_generate_ssl_certs.ssh': - require => File['/etc/pki/tls/certs/ps_generate_ssl_certs.ssh'], - notify => Service['httpd'], - before => Class['horizon'], - } -> - exec { 'nova-novncproxy-restart': - # ps_generate_ssl_certs.ssh is generating ssl certs for nova-novncproxy - # so openstack-nova-novncproxy should be restarted. - path => ['/sbin', '/usr/sbin', '/bin', '/usr/bin'], - command => 'systemctl restart openstack-nova-novncproxy.service', - logoutput => 'on_failure', - } - +if $horizon_ssl { apache::listen { '443': } - - # little bit of hatred as we'll have to patch upstream puppet-horizon - file_line {'horizon_ssl_wsgi_fix': - path => '/etc/httpd/conf.d/15-horizon_ssl_vhost.conf', - match => 'WSGIProcessGroup.*', - line => ' WSGIProcessGroup horizon-ssl', - require => File['15-horizon_ssl_vhost.conf'], - notify => Service['httpd'], - } } class { '::memcached': diff --git a/packstack/puppet/templates/ironic_rabbitmq.pp b/packstack/puppet/templates/ironic_rabbitmq.pp index 12be93e86..e656483d3 100644 --- a/packstack/puppet/templates/ironic_rabbitmq.pp +++ b/packstack/puppet/templates/ironic_rabbitmq.pp @@ -1,14 +1,31 @@ $ironic_rabbitmq_cfg_ironic_db_pw = hiera('CONFIG_IRONIC_DB_PW') $ironic_rabbitmq_cfg_mariadb_host = hiera('CONFIG_MARIADB_HOST_URL') +$kombu_ssl_ca_certs = hiera('CONFIG_AMQP_SSL_CACERT_FILE', undef) +$kombu_ssl_keyfile = hiera('CONFIG_IRONIC_SSL_KEY', undef) +$kombu_ssl_certfile = hiera('CONFIG_IRONIC_SSL_CERT', undef) + +if $kombu_ssl_keyfile { + $files_to_set_owner = [ $kombu_ssl_keyfile, $kombu_ssl_certfile ] + file { $files_to_set_owner: + owner => 'ironic', + group => 'ironic', + require => Package['openstack-ironic-common'], + } + File[$files_to_set_owner] ~> Service<||> +} + class { '::ironic': rpc_backend => 'ironic.openstack.common.rpc.impl_kombu', rabbit_host => hiera('CONFIG_AMQP_HOST_URL'), rabbit_port => hiera('CONFIG_AMQP_CLIENTS_PORT'), - rabbit_use_ssl => hiera('CONFIG_AMQP_ENABLE_SSL'), + rabbit_use_ssl => hiera('CONFIG_AMQP_SSL_ENABLED'), rabbit_user => hiera('CONFIG_AMQP_AUTH_USER'), rabbit_password => hiera('CONFIG_AMQP_AUTH_PASSWORD'), database_connection => "mysql://ironic:${ironic_rabbitmq_cfg_ironic_db_pw}@${ironic_rabbitmq_cfg_mariadb_host}/ironic", debug => true, verbose => true, + kombu_ssl_ca_certs => $kombu_ssl_ca_certs, + kombu_ssl_keyfile => $kombu_ssl_keyfile, + kombu_ssl_certfile => $kombu_ssl_certfile, } diff --git a/packstack/puppet/templates/manila_rabbitmq.pp b/packstack/puppet/templates/manila_rabbitmq.pp index 2fe783180..65c80e4ca 100644 --- a/packstack/puppet/templates/manila_rabbitmq.pp +++ b/packstack/puppet/templates/manila_rabbitmq.pp @@ -1,3 +1,17 @@ +$kombu_ssl_ca_certs = hiera('CONFIG_AMQP_SSL_CACERT_FILE', undef) +$kombu_ssl_keyfile = hiera('CONFIG_MANILA_SSL_KEY', undef) +$kombu_ssl_certfile = hiera('CONFIG_MANILA_SSL_CERT', undef) + +if $kombu_ssl_keyfile { + $files_to_set_owner = [ $kombu_ssl_keyfile, $kombu_ssl_certfile ] + file { $files_to_set_owner: + owner => 'manila', + group => 'manila', + # manila user on RH/Fedora is provided by python-manila + require => Package['openstack-manila'], + } + File[$files_to_set_owner] ~> Service<||> +} $db_pw = hiera('CONFIG_MANILA_DB_PW') $mariadb_host = hiera('CONFIG_MARIADB_HOST_URL') @@ -5,7 +19,7 @@ $mariadb_host = hiera('CONFIG_MARIADB_HOST_URL') class { '::manila': rabbit_host => hiera('CONFIG_AMQP_HOST_URL'), rabbit_port => hiera('CONFIG_AMQP_CLIENTS_PORT'), - rabbit_use_ssl => hiera('CONFIG_AMQP_ENABLE_SSL'), + rabbit_use_ssl => hiera('CONFIG_AMQP_SSL_ENABLED'), rabbit_userid => hiera('CONFIG_AMQP_AUTH_USER'), rabbit_password => hiera('CONFIG_AMQP_AUTH_PASSWORD'), sql_connection => "mysql://manila:${db_pw}@${mariadb_host}/manila", diff --git a/packstack/puppet/templates/nagios_server.pp b/packstack/puppet/templates/nagios_server.pp index 246e3f43f..6739c8f76 100644 --- a/packstack/puppet/templates/nagios_server.pp +++ b/packstack/puppet/templates/nagios_server.pp @@ -94,6 +94,6 @@ firewall { '001 nagios incoming': } # ensure that we won't stop listening on 443 if horizon has ssl enabled -if hiera('CONFIG_HORIZON_SSL') { +if hiera('CONFIG_HORIZON_SSL') == 'y' { apache::listen { '443': } } diff --git a/packstack/puppet/templates/neutron_rabbitmq.pp b/packstack/puppet/templates/neutron_rabbitmq.pp index 1fadc5d92..434a7a79b 100644 --- a/packstack/puppet/templates/neutron_rabbitmq.pp +++ b/packstack/puppet/templates/neutron_rabbitmq.pp @@ -3,11 +3,26 @@ $bind_host = hiera('CONFIG_IP_VERSION') ? { 'ipv4' => '0.0.0.0', } +$kombu_ssl_ca_certs = hiera('CONFIG_AMQP_SSL_CACERT_FILE', undef) +$kombu_ssl_keyfile = hiera('CONFIG_NEUTRON_SSL_KEY', undef) +$kombu_ssl_certfile = hiera('CONFIG_NEUTRON_SSL_CERT', undef) + +if $kombu_ssl_keyfile { + $files_to_set_owner = [ $kombu_ssl_keyfile, $kombu_ssl_certfile ] + file { $files_to_set_owner: + owner => 'neutron', + group => 'neutron', + require => Class['neutron'], + } + File[$files_to_set_owner] ~> Service<||> +} + + class { '::neutron': bind_host => $bind_host, rabbit_host => hiera('CONFIG_AMQP_HOST_URL'), rabbit_port => hiera('CONFIG_AMQP_CLIENTS_PORT'), - rabbit_use_ssl => hiera('CONFIG_AMQP_ENABLE_SSL'), + rabbit_use_ssl => hiera('CONFIG_AMQP_SSL_ENABLED'), rabbit_user => hiera('CONFIG_AMQP_AUTH_USER'), rabbit_password => hiera('CONFIG_AMQP_AUTH_PASSWORD'), core_plugin => hiera('CONFIG_NEUTRON_CORE_PLUGIN'), @@ -15,4 +30,7 @@ class { '::neutron': service_plugins => hiera_array('SERVICE_PLUGINS'), verbose => true, debug => hiera('CONFIG_DEBUG_MODE'), + kombu_ssl_ca_certs => $kombu_ssl_ca_certs, + kombu_ssl_keyfile => $kombu_ssl_keyfile, + kombu_ssl_certfile => $kombu_ssl_certfile, } diff --git a/packstack/puppet/templates/nova_ceilometer_rabbitmq.pp b/packstack/puppet/templates/nova_ceilometer_rabbitmq.pp index 44199e2b5..3a65e6936 100644 --- a/packstack/puppet/templates/nova_ceilometer_rabbitmq.pp +++ b/packstack/puppet/templates/nova_ceilometer_rabbitmq.pp @@ -1,14 +1,30 @@ +$ceilometer_kombu_ssl_ca_certs = hiera('CONFIG_AMQP_SSL_CACERT_FILE', undef) +$ceilometer_kombu_ssl_keyfile = hiera('CONFIG_CEILOMETER_SSL_KEY', undef) +$ceilometer_kombu_ssl_certfile = hiera('CONFIG_CEILOMETER_SSL_CERT', undef) -class { '::ceilometer': - metering_secret => hiera('CONFIG_CEILOMETER_SECRET'), - rabbit_host => hiera('CONFIG_AMQP_HOST_URL'), - rabbit_port => hiera('CONFIG_AMQP_CLIENTS_PORT'), - rabbit_use_ssl => hiera('CONFIG_AMQP_ENABLE_SSL'), - rabbit_userid => hiera('CONFIG_AMQP_AUTH_USER'), - rabbit_password => hiera('CONFIG_AMQP_AUTH_PASSWORD'), - verbose => true, - debug => hiera('CONFIG_DEBUG_MODE'), - # for some strange reason ceilometer needs to be in nova group - require => Package['nova-common'], +if $ceilometer_kombu_ssl_keyfile { + $ceilometer_files_to_set_owner = [ $ceilometer_kombu_ssl_keyfile, $ceilometer_kombu_ssl_certfile ] + file { $ceilometer_files_to_set_owner: + owner => 'ceilometer', + group => 'ceilometer', + require => Package['nova-common'], + } + File[$ceilometer_files_to_set_owner] ~> Service<||> +} + +class { '::ceilometer': + metering_secret => hiera('CONFIG_CEILOMETER_SECRET'), + rabbit_host => hiera('CONFIG_AMQP_HOST_URL'), + rabbit_port => hiera('CONFIG_AMQP_CLIENTS_PORT'), + rabbit_use_ssl => hiera('CONFIG_AMQP_SSL_ENABLED'), + rabbit_userid => hiera('CONFIG_AMQP_AUTH_USER'), + rabbit_password => hiera('CONFIG_AMQP_AUTH_PASSWORD'), + verbose => true, + debug => hiera('CONFIG_DEBUG_MODE'), + # for some strange reason ceilometer needs to be in nova group + require => Package['nova-common'], + kombu_ssl_ca_certs => $ceilometer_kombu_ssl_ca_certs, + kombu_ssl_keyfile => $ceilometer_kombu_ssl_keyfile, + kombu_ssl_certfile => $ceilometer_kombu_ssl_certfile, } diff --git a/packstack/puppet/templates/nova_common_qpid.pp b/packstack/puppet/templates/nova_common_qpid.pp index 096bbedb5..3b1f131b7 100644 --- a/packstack/puppet/templates/nova_common_qpid.pp +++ b/packstack/puppet/templates/nova_common_qpid.pp @@ -1,9 +1,9 @@ $private_key = { - type => hiera('NOVA_MIGRATION_KEY_TYPE'), + 'type' => hiera('NOVA_MIGRATION_KEY_TYPE'), key => hiera('NOVA_MIGRATION_KEY_SECRET'), } $public_key = { - type => hiera('NOVA_MIGRATION_KEY_TYPE'), + 'type' => hiera('NOVA_MIGRATION_KEY_TYPE'), key => hiera('NOVA_MIGRATION_KEY_PUBLIC'), } diff --git a/packstack/puppet/templates/nova_common_rabbitmq.pp b/packstack/puppet/templates/nova_common_rabbitmq.pp index b60bbe73f..7838397b3 100644 --- a/packstack/puppet/templates/nova_common_rabbitmq.pp +++ b/packstack/puppet/templates/nova_common_rabbitmq.pp @@ -1,23 +1,41 @@ $private_key = { - type => hiera('NOVA_MIGRATION_KEY_TYPE'), + 'type' => hiera('NOVA_MIGRATION_KEY_TYPE'), key => hiera('NOVA_MIGRATION_KEY_SECRET'), } $public_key = { - type => hiera('NOVA_MIGRATION_KEY_TYPE'), + 'type' => hiera('NOVA_MIGRATION_KEY_TYPE'), key => hiera('NOVA_MIGRATION_KEY_PUBLIC'), } + +$kombu_ssl_ca_certs = hiera('CONFIG_AMQP_SSL_CACERT_FILE', undef) +$kombu_ssl_keyfile = hiera('CONFIG_NOVA_SSL_KEY', undef) +$kombu_ssl_certfile = hiera('CONFIG_NOVA_SSL_CERT', undef) + +if $kombu_ssl_keyfile { + $files_to_set_owner = [ $kombu_ssl_keyfile, $kombu_ssl_certfile ] + file { $files_to_set_owner: + owner => 'nova', + group => 'nova', + require => Package['nova-common'], + } + File[$files_to_set_owner] ~> Service<||> +} + $nova_common_rabbitmq_cfg_storage_host = hiera('CONFIG_STORAGE_HOST_URL') class { '::nova': glance_api_servers => "${nova_common_rabbitmq_cfg_storage_host}:9292", rabbit_host => hiera('CONFIG_AMQP_HOST_URL'), rabbit_port => hiera('CONFIG_AMQP_CLIENTS_PORT'), - rabbit_use_ssl => hiera('CONFIG_AMQP_ENABLE_SSL'), + rabbit_use_ssl => hiera('CONFIG_AMQP_SSL_ENABLED'), rabbit_userid => hiera('CONFIG_AMQP_AUTH_USER'), rabbit_password => hiera('CONFIG_AMQP_AUTH_PASSWORD'), verbose => true, debug => hiera('CONFIG_DEBUG_MODE'), nova_public_key => $public_key, nova_private_key => $private_key, + kombu_ssl_ca_certs => $kombu_ssl_ca_certs, + kombu_ssl_keyfile => $kombu_ssl_keyfile, + kombu_ssl_certfile => $kombu_ssl_certfile, } diff --git a/packstack/puppet/templates/nova_compute.pp b/packstack/puppet/templates/nova_compute.pp index 04b8841fb..c3be6bb25 100644 --- a/packstack/puppet/templates/nova_compute.pp +++ b/packstack/puppet/templates/nova_compute.pp @@ -27,18 +27,6 @@ nova_config{ value => hiera('CONFIG_NOVA_COMPUTE_MIGRATE_URL'); } -if $is_horizon_ssl == undef { - $is_horizon_ssl = hiera('CONFIG_HORIZON_SSL') -} - -if $vncproxy_protocol == undef { - $vncproxy_protocol = $is_horizon_ssl ? { - true => 'https', - false => 'http', - default => 'http', - } -} - if ($::fqdn == '' or $::fqdn =~ /localhost/) { # For cases where FQDNs have not been correctly set $vncproxy_server = choose_my_ip(hiera('HOST_LIST')) @@ -49,7 +37,7 @@ if ($::fqdn == '' or $::fqdn =~ /localhost/) { class { '::nova::compute': enabled => true, vncproxy_host => hiera('CONFIG_KEYSTONE_HOST_URL'), - vncproxy_protocol => $vncproxy_protocol, + vncproxy_protocol => hiera('CONFIG_VNCPROXY_PROTOCOL'), vncserver_proxyclient_address => $vncproxy_server, compute_manager => hiera('CONFIG_NOVA_COMPUTE_MANAGER'), } diff --git a/packstack/puppet/templates/nova_vncproxy.pp b/packstack/puppet/templates/nova_vncproxy.pp index ff3563103..1d0dab79a 100644 --- a/packstack/puppet/templates/nova_vncproxy.pp +++ b/packstack/puppet/templates/nova_vncproxy.pp @@ -1,12 +1,8 @@ -if $is_horizon_ssl == undef { - $is_horizon_ssl = hiera('CONFIG_HORIZON_SSL') -} - -if $is_horizon_ssl == true { +if hiera('CONFIG_HORIZON_SSL') == 'y' { nova_config { 'DEFAULT/ssl_only': value => true; - 'DEFAULT/cert': value => '/etc/nova/nova.crt'; - 'DEFAULT/key': value => '/etc/nova/nova.key'; + 'DEFAULT/cert': value => hiera('CONFIG_VNC_SSL_CERT'); + 'DEFAULT/key': value => hiera('CONFIG_VNC_SSL_KEY'); } } diff --git a/packstack/puppet/templates/sahara_rabbitmq.pp b/packstack/puppet/templates/sahara_rabbitmq.pp index 11b620f38..6b8a27464 100644 --- a/packstack/puppet/templates/sahara_rabbitmq.pp +++ b/packstack/puppet/templates/sahara_rabbitmq.pp @@ -1,7 +1,24 @@ +$kombu_ssl_ca_certs = hiera('CONFIG_AMQP_SSL_CACERT_FILE', undef) +$kombu_ssl_keyfile = hiera('CONFIG_SAHARA_SSL_KEY', undef) +$kombu_ssl_certfile = hiera('CONFIG_SAHARA_SSL_CERT', undef) + +if $kombu_ssl_keyfile { + $files_to_set_owner = [ $kombu_ssl_keyfile, $kombu_ssl_certfile ] + file { $files_to_set_owner: + owner => 'sahara', + group => 'sahara', + require => Package['openstack-sahara'], + } + File[$files_to_set_owner] ~> Service<||> +} + class { '::sahara::notify::rabbitmq': rabbit_host => hiera('CONFIG_AMQP_HOST_URL'), rabbit_port => hiera('CONFIG_AMQP_CLIENTS_PORT'), - rabbit_use_ssl => hiera('CONFIG_AMQP_ENABLE_SSL'), + rabbit_use_ssl => hiera('CONFIG_AMQP_SSL_ENABLED'), rabbit_userid => hiera('CONFIG_AMQP_AUTH_USER'), rabbit_password => hiera('CONFIG_AMQP_AUTH_PASSWORD'), + kombu_ssl_ca_certs => $kombu_ssl_ca_certs, + kombu_ssl_keyfile => $kombu_ssl_keyfile, + kombu_ssl_certfile => $kombu_ssl_certfile, } diff --git a/packstack/puppet/templates/trove_rabbitmq.pp b/packstack/puppet/templates/trove_rabbitmq.pp index 7578ab14c..583a32013 100644 --- a/packstack/puppet/templates/trove_rabbitmq.pp +++ b/packstack/puppet/templates/trove_rabbitmq.pp @@ -2,10 +2,25 @@ $trove_rabmq_cfg_trove_db_pw = hiera('CONFIG_TROVE_DB_PW') $trove_rabmq_cfg_mariadb_host = hiera('CONFIG_MARIADB_HOST_URL') $trove_rabmq_cfg_controller_host = hiera('CONFIG_KEYSTONE_HOST_URL') +$kombu_ssl_ca_certs = hiera('CONFIG_AMQP_SSL_CACERT_FILE', undef) +$kombu_ssl_keyfile = hiera('CONFIG_TROVE_SSL_KEY', undef) +$kombu_ssl_certfile = hiera('CONFIG_TROVE_SSL_CERT', undef) + +if $kombu_ssl_keyfile { + $files_to_set_owner = [ $kombu_ssl_keyfile, $kombu_ssl_certfile ] + file { $files_to_set_owner: + owner => 'trove', + group => 'trove', + } + Package<|tag=='trove'|> -> File[$files_to_set_owner] + File[$files_to_set_owner] ~> Service<||> +} + + class { '::trove': rpc_backend => 'trove.openstack.common.rpc.impl_kombu', rabbit_host => hiera('CONFIG_AMQP_HOST_URL'), - rabbit_use_ssl => hiera('CONFIG_AMQP_ENABLE_SSL'), + rabbit_use_ssl => hiera('CONFIG_AMQP_SSL_ENABLED'), rabbit_port => hiera('CONFIG_AMQP_CLIENTS_PORT'), rabbit_userid => hiera('CONFIG_AMQP_AUTH_USER'), rabbit_password => hiera('CONFIG_AMQP_AUTH_PASSWORD'), @@ -17,4 +32,7 @@ class { '::trove': cinder_url => "http://${trove_rabmq_cfg_controller_host}:8776/v1", swift_url => "http://${trove_rabmq_cfg_controller_host}:8080/v1/AUTH_", use_neutron => hiera('CONFIG_NEUTRON_INSTALL'), + kombu_ssl_ca_certs => $kombu_ssl_ca_certs, + kombu_ssl_keyfile => $kombu_ssl_keyfile, + kombu_ssl_certfile => $kombu_ssl_certfile, } diff --git a/requirements.txt b/requirements.txt index 23f2069b7..1c819217f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,5 @@ netaddr>=0.7.6 PyYAML>=3.10 docutils>=0.11 +pyOpenSSL +cryptography diff --git a/setup.py b/setup.py index b00b36036..33def63a9 100644 --- a/setup.py +++ b/setup.py @@ -104,7 +104,7 @@ setup( include_package_data=True, long_description=read('README.md'), zip_safe=False, - install_requires=['netaddr', 'PyYAML', 'docutils'], + install_requires=['netaddr', 'pyOpenSSL', 'PyYAML', 'docutils'], classifiers=[ "Development Status :: 3 - Alpha", "Topic :: Utilities", diff --git a/tests/installer/test_run_setup.py b/tests/installer/test_run_setup.py index 4515a19d4..1c3dd28fd 100644 --- a/tests/installer/test_run_setup.py +++ b/tests/installer/test_run_setup.py @@ -91,7 +91,8 @@ class CommandLineTestCase(PackstackTestCaseMixin, TestCase): sys.argv = ['packstack', '--debug', '--ssh-public-key=%s' % dummy_public_key, '--install-hosts=127.0.0.1', '--os-swift-install=y', - '--nagios-install=y', '--use-epel=y'] + '--nagios-install=y', '--use-epel=y', '--ssl-cacert-selfsign=y', + '--ssl-cert-dir=%s' % os.path.expanduser('~/')] # There is no puppet logfile to validate, so replace # ospluginutils.validate_puppet_logfile with a mock function diff --git a/tests/installer/test_validators.py b/tests/installer/test_validators.py index 68e094878..64a8c655e 100644 --- a/tests/installer/test_validators.py +++ b/tests/installer/test_validators.py @@ -73,7 +73,15 @@ class ValidatorsTestCase(PackstackTestCaseMixin, TestCase): def test_validate_file(self): """Test packstack.installer.validators.validate_file.""" - fname = os.path.join(self.tempdir, '.test_validate_file') + dname = os.path.join(self.tempdir, '.test_validate_file') + bad_name = os.path.join(self.tempdir, '.me_no_exists') + os.mkdir(dname) + validate_writeable_directory(dname) + self.assertRaises(ParamValidationError, validate_writeable_directory, bad_name) + + def test_validate_writeable_directory(self): + """Test packstack.installer.validators.validate_writeable_directory.""" + fname = os.path.join(self.tempdir, '.test_validate_writeable_directory') bad_name = os.path.join(self.tempdir, '.me_no_exists') with open(fname, 'w') as f: f.write('test') diff --git a/tools/test-requires b/tools/test-requires index 1c9867b72..11fa6bf95 100644 --- a/tools/test-requires +++ b/tools/test-requires @@ -2,3 +2,4 @@ nose coverage hacking>=0.9.5,<0.10 netaddr +pyOpenSSL