diff options
| author | Jamie Lennox <jlennox@redhat.com> | 2013-04-04 17:44:01 +1000 |
|---|---|---|
| committer | Jamie Lennox <jlennox@redhat.com> | 2013-04-11 14:41:15 +1000 |
| commit | 28ef9cdcc6073c2f6600d30b401dcbce81afd4df (patch) | |
| tree | 954fa9f9dce47b8b320ceb3fca3f6c8a83855c9d /keystone/common | |
| parent | cbac77110ee1d7b9abc5a23f973dab27e8b32015 (diff) | |
Generate HTTPS certificates with ssl_setup.
Extracts common OpenSSL functionality from pki_setup and adds a new cli
command ssl_setup which re-uses this base to generate SSL certificates
for https.
Change-Id: Ia34827583bcdfbd871133250681010e642271f07
Fixes: bug 1155361
Diffstat (limited to 'keystone/common')
| -rw-r--r-- | keystone/common/config.py | 20 | ||||
| -rw-r--r-- | keystone/common/openssl.py | 173 |
2 files changed, 119 insertions, 74 deletions
diff --git a/keystone/common/config.py b/keystone/common/config.py index f749300d..a4fa4901 100644 --- a/keystone/common/config.py +++ b/keystone/common/config.py @@ -217,10 +217,20 @@ def configure(): # ssl register_bool('enable', group='ssl', default=False) - register_str('certfile', group='ssl', default=None) - register_str('keyfile', group='ssl', default=None) - register_str('ca_certs', group='ssl', default=None) + register_str('certfile', group='ssl', + default="/etc/keystone/ssl/certs/keystone.pem") + register_str('keyfile', group='ssl', + default="/etc/keystone/ssl/private/keystonekey.pem") + register_str('ca_certs', group='ssl', + default="/etc/keystone/ssl/certs/ca.pem") + register_str('ca_key', group='ssl', + default="/etc/keystone/ssl/certs/cakey.pem") register_bool('cert_required', group='ssl', default=False) + register_int('key_size', group='ssl', default=1024) + register_int('valid_days', group='ssl', default=3650) + register_str('ca_password', group='ssl', default=None) + register_str('cert_subject', group='ssl', + default='/C=US/ST=Unset/L=Unset/O=Unset/CN=localhost') # signing register_str( @@ -237,9 +247,13 @@ def configure(): 'ca_certs', group='signing', default="/etc/keystone/ssl/certs/ca.pem") + register_str('ca_key', group='signing', + default="/etc/keystone/ssl/certs/cakey.pem") register_int('key_size', group='signing', default=1024) register_int('valid_days', group='signing', default=3650) register_str('ca_password', group='signing', default=None) + register_str('cert_subject', group='signing', + default='/C=US/ST=Unset/L=Unset/O=Unset/CN=www.example.com') # sql register_str('connection', group='sql', secret=True, diff --git a/keystone/common/openssl.py b/keystone/common/openssl.py index d4dc3af0..dd31fb71 100644 --- a/keystone/common/openssl.py +++ b/keystone/common/openssl.py @@ -29,41 +29,34 @@ DIR_PERMS = (stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR | stat.S_IROTH | stat.S_IXOTH) CERT_PERMS = stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH PRIV_PERMS = stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR -DEFAULT_SUBJECT = '/C=US/ST=Unset/L=Unset/O=Unset/CN=www.example.com' def file_exists(file_path): return os.path.exists(file_path) -class ConfigurePKI(object): - """Generate files for PKI signing using OpenSSL. - - Signed tokens require a private key and signing certificate which itself - must be signed by a CA. This class generates them with workable defaults - if each of the files are not present - - """ +class BaseCertificateConfigure(object): + """Create a certificate signing environment from a config section + and reasonable OpenSSL defaults""" - def __init__(self, keystone_user, keystone_group, **kw): - self.conf_dir = os.path.dirname(CONF.signing.ca_certs) + def __init__(self, conf_obj, keystone_user, keystone_group, **kwargs): + self.conf_dir = os.path.dirname(conf_obj.ca_certs) self.use_keystone_user = keystone_user self.use_keystone_group = keystone_group self.ssl_config_file_name = os.path.join(self.conf_dir, "openssl.conf") - self.ca_key_file = os.path.join(self.conf_dir, "cakey.pem") self.request_file_name = os.path.join(self.conf_dir, "req.pem") self.ssl_dictionary = {'conf_dir': self.conf_dir, - 'ca_cert': CONF.signing.ca_certs, + 'ca_cert': conf_obj.ca_certs, 'ssl_config': self.ssl_config_file_name, - 'ca_private_key': self.ca_key_file, - 'ca_cert_cn': 'hostname', + 'ca_private_key': conf_obj.ca_key, 'request_file': self.request_file_name, - 'signing_key': CONF.signing.keyfile, - 'signing_cert': CONF.signing.certfile, - 'default_subject': DEFAULT_SUBJECT, - 'key_size': int(CONF.signing.key_size), - 'valid_days': int(CONF.signing.valid_days), - 'ca_password': CONF.signing.ca_password} + 'signing_key': conf_obj.keyfile, + 'signing_cert': conf_obj.certfile, + 'key_size': int(conf_obj.key_size), + 'valid_days': int(conf_obj.valid_days), + 'cert_subject': conf_obj.cert_subject, + 'ca_password': conf_obj.ca_password} + self.ssl_dictionary.update(kwargs) def _make_dirs(self, file_name): dir = os.path.dirname(file_name) @@ -106,20 +99,25 @@ class ConfigurePKI(object): self._set_permissions(self.ssl_config_file_name, PRIV_PERMS) def build_ca_cert(self): - if not file_exists(CONF.signing.ca_certs): - if not os.path.exists(self.ca_key_file): - self._make_dirs(self.ca_key_file) - self.exec_command('openssl genrsa -out %(ca_private_key)s ' - '%(key_size)d -config %(ssl_config)s') - self._set_permissions(self.ssl_dictionary['ca_private_key'], - stat.S_IRUSR) + ca_key_file = self.ssl_dictionary['ca_private_key'] + ca_cert = self.ssl_dictionary['ca_cert'] + + if not file_exists(ca_key_file): + self._make_dirs(ca_key_file) + self.exec_command('openssl genrsa -out %(ca_private_key)s ' + '%(key_size)d') + self._set_permissions(self.ssl_dictionary['ca_private_key'], + stat.S_IRUSR) + + if not file_exists(ca_cert): + self._make_dirs(ca_cert) self.exec_command('openssl req -new -x509 -extensions v3_ca ' '-passin pass:%(ca_password)s ' '-key %(ca_private_key)s -out %(ca_cert)s ' '-days %(valid_days)d ' '-config %(ssl_config)s ' - '-subj %(default_subject)s') - self._set_permissions(self.ssl_dictionary['ca_cert'], CERT_PERMS) + '-subj %(cert_subject)s') + self._set_permissions(ca_cert, CERT_PERMS) def build_private_key(self): signing_keyfile = self.ssl_dictionary['signing_key'] @@ -128,19 +126,23 @@ class ConfigurePKI(object): self._make_dirs(signing_keyfile) self.exec_command('openssl genrsa -out %(signing_key)s ' - '%(key_size)d ' - '-config %(ssl_config)s') + '%(key_size)d ') self._set_permissions(os.path.dirname(signing_keyfile), PRIV_PERMS) self._set_permissions(signing_keyfile, stat.S_IRUSR) def build_signing_cert(self): - if not file_exists(CONF.signing.certfile): - self._make_dirs(CONF.signing.certfile) + signing_cert = self.ssl_dictionary['signing_cert'] + + if not file_exists(signing_cert): + self._make_dirs(signing_cert) + self.exec_command('openssl req -key %(signing_key)s -new -nodes ' '-out %(request_file)s -config %(ssl_config)s ' - '-subj %(default_subject)s') + '-subj %(cert_subject)s') + self.exec_command('openssl ca -batch -out %(signing_cert)s ' - '-config %(ssl_config)s ' + '-config %(ssl_config)s -days %(valid_days)dd ' + '-cert %(ca_cert)s -keyfile %(ca_private_key)s ' '-infiles %(request_file)s') def run(self): @@ -149,13 +151,41 @@ class ConfigurePKI(object): self.build_private_key() self.build_signing_cert() - sslconfig = """ + +class ConfigurePKI(BaseCertificateConfigure): + """Generate files for PKI signing using OpenSSL. + + Signed tokens require a private key and signing certificate which itself + must be signed by a CA. This class generates them with workable defaults + if each of the files are not present + + """ + + def __init__(self, keystone_user, keystone_group): + super(ConfigurePKI, self).__init__(CONF.signing, + keystone_user, keystone_group) + + +class ConfigureSSL(BaseCertificateConfigure): + """Generate files for HTTPS using OpenSSL. + + Creates a public/private key and certificates. If a CA is not given + one will be generated using provided arguments. + """ + + def __init__(self, keystone_user, keystone_group): + super(ConfigureSSL, self).__init__(CONF.ssl, + keystone_user, keystone_group) + + +BaseCertificateConfigure.sslconfig = """ # OpenSSL configuration file. # # Establish working directory. dir = %(conf_dir)s + [ ca ] default_ca = CA_default @@ -163,55 +193,56 @@ default_ca = CA_default new_certs_dir = $dir serial = $dir/serial database = $dir/index.txt -certificate = %(ca_cert)s -private_key = %(ca_private_key)s default_days = 365 -default_md = md5 +default_md = sha1 preserve = no email_in_dn = no nameopt = default_ca certopt = default_ca -policy = policy_match -[ policy_match ] -countryName = match -stateOrProvinceName = match -organizationName = match +policy = policy_anything +x509_extensions = usr_cert +unique_subject = no + +[ policy_anything ] +countryName = optional +stateOrProvinceName = optional +organizationName = optional organizationalUnitName = optional commonName = supplied emailAddress = optional [ req ] -default_bits = 1024 # Size of keys -default_keyfile = key.pem # name of generated keys -default_md = md5 # message digest algorithm +default_bits = 1024 # Size of keys +default_keyfile = key.pem # name of generated keys +default_md = default # message digest algorithm string_mask = nombstr # permitted characters -distinguished_name = req_distinguished_name -req_extensions = v3_req +distinguished_name = req_distinguished_name +req_extensions = v3_req [ req_distinguished_name ] -0.organizationName = Organization Name (company) -organizationalUnitName = Organizational Unit Name (department, division) -emailAddress = Email Address -emailAddress_max = 40 -localityName = Locality Name (city, district) -stateOrProvinceName = State or Province Name (full name) -countryName = Country Name (2 letter code) -countryName_min = 2 -countryName_max = 2 -commonName = Common Name (hostname, IP, or your name) -commonName_max = 64 -# Default values for the above, for consistency and less typing. -0.organizationName_default = Openstack, Inc -localityName_default = Undefined -stateOrProvinceName_default = Undefined -countryName_default = US -commonName_default = %(ca_cert_cn)s +0.organizationName = Organization Name (company) +organizationalUnitName = Organizational Unit Name (department, division) +emailAddress = Email Address +emailAddress_max = 40 +localityName = Locality Name (city, district) +stateOrProvinceName = State or Province Name (full name) +countryName = Country Name (2 letter code) +countryName_min = 2 +countryName_max = 2 +commonName = Common Name (hostname, IP, or your name) +commonName_max = 64 [ v3_ca ] -basicConstraints = CA:TRUE -subjectKeyIdentifier = hash +basicConstraints = CA:TRUE +subjectKeyIdentifier = hash authorityKeyIdentifier = keyid:always,issuer:always [ v3_req ] -basicConstraints = CA:FALSE -subjectKeyIdentifier = hash""" +basicConstraints = CA:FALSE +subjectKeyIdentifier = hash + +[ usr_cert ] +basicConstraints = CA:FALSE +subjectKeyIdentifier = hash +authorityKeyIdentifier = keyid:always,issuer:always +""" |
