**** CubicPower OpenStack Study ****
# Copyright 2012 OpenStack Foundation
#
# 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.
#
import os
from keystone.common import environment
from keystone.common import utils
from keystone import config
from keystone.openstack.common import log
LOG = log.getLogger(__name__)
CONF = config.CONF
PUBLIC_DIR_PERMS = 0o755 # -rwxr-xr-x
PRIVATE_DIR_PERMS = 0o750 # -rwxr-x---
PUBLIC_FILE_PERMS = 0o644 # -rw-r--r--
PRIVATE_FILE_PERMS = 0o640 # -rw-r-----
**** CubicPower OpenStack Study ****
def file_exists(file_path):
return os.path.exists(file_path)
**** CubicPower OpenStack Study ****
class BaseCertificateConfigure(object):
"""Create a certificate signing environment.
This is based on a config section and reasonable OpenSSL
**** CubicPower OpenStack Study ****
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.request_file_name = os.path.join(self.conf_dir, "req.pem")
self.ssl_dictionary = {'conf_dir': self.conf_dir,
'ca_cert': conf_obj.ca_certs,
'default_md': 'default',
'ssl_config': self.ssl_config_file_name,
'ca_private_key': conf_obj.ca_key,
'request_file': self.request_file_name,
'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}
try:
# OpenSSL 1.0 and newer support default_md = default, olders do not
openssl_ver = environment.subprocess.Popen(
['openssl', 'version'],
stdout=environment.subprocess.PIPE).stdout.read()
if "OpenSSL 0." in openssl_ver:
self.ssl_dictionary['default_md'] = 'sha1'
except OSError:
LOG.warn('Failed to invoke ``openssl version``, '
'assuming is v1.0 or newer')
self.ssl_dictionary.update(kwargs)
**** CubicPower OpenStack Study ****
def exec_command(self, command):
to_exec = []
for cmd_part in command:
to_exec.append(cmd_part % self.ssl_dictionary)
LOG.info(' '.join(to_exec))
environment.subprocess.check_call(to_exec)
**** CubicPower OpenStack Study ****
def build_ssl_config_file(self):
utils.make_dirs(os.path.dirname(self.ssl_config_file_name),
mode=PUBLIC_DIR_PERMS,
user=self.use_keystone_user,
group=self.use_keystone_group, log=LOG)
if not file_exists(self.ssl_config_file_name):
ssl_config_file = open(self.ssl_config_file_name, 'w')
ssl_config_file.write(self.sslconfig % self.ssl_dictionary)
ssl_config_file.close()
utils.set_permissions(self.ssl_config_file_name,
mode=PRIVATE_FILE_PERMS,
user=self.use_keystone_user,
group=self.use_keystone_group, log=LOG)
index_file_name = os.path.join(self.conf_dir, 'index.txt')
if not file_exists(index_file_name):
index_file = open(index_file_name, 'w')
index_file.write('')
index_file.close()
utils.set_permissions(index_file_name,
mode=PRIVATE_FILE_PERMS,
user=self.use_keystone_user,
group=self.use_keystone_group, log=LOG)
serial_file_name = os.path.join(self.conf_dir, 'serial')
if not file_exists(serial_file_name):
index_file = open(serial_file_name, 'w')
index_file.write('01')
index_file.close()
utils.set_permissions(serial_file_name,
mode=PRIVATE_FILE_PERMS,
user=self.use_keystone_user,
group=self.use_keystone_group, log=LOG)
**** CubicPower OpenStack Study ****
def build_ca_cert(self):
ca_key_file = self.ssl_dictionary['ca_private_key']
utils.make_dirs(os.path.dirname(ca_key_file),
mode=PRIVATE_DIR_PERMS,
user=self.use_keystone_user,
group=self.use_keystone_group, log=LOG)
if not file_exists(ca_key_file):
self.exec_command(['openssl', 'genrsa',
'-out', '%(ca_private_key)s',
'%(key_size)d'])
utils.set_permissions(ca_key_file,
mode=PRIVATE_FILE_PERMS,
user=self.use_keystone_user,
group=self.use_keystone_group, log=LOG)
ca_cert = self.ssl_dictionary['ca_cert']
utils.make_dirs(os.path.dirname(ca_cert),
mode=PUBLIC_DIR_PERMS,
user=self.use_keystone_user,
group=self.use_keystone_group, log=LOG)
if not file_exists(ca_cert):
self.exec_command(['openssl', 'req', '-new', '-x509',
'-extensions', 'v3_ca',
'-key', '%(ca_private_key)s',
'-out', '%(ca_cert)s',
'-days', '%(valid_days)d',
'-config', '%(ssl_config)s',
'-subj', '%(cert_subject)s'])
utils.set_permissions(ca_cert,
mode=PUBLIC_FILE_PERMS,
user=self.use_keystone_user,
group=self.use_keystone_group, log=LOG)
**** CubicPower OpenStack Study ****
def build_private_key(self):
signing_keyfile = self.ssl_dictionary['signing_key']
utils.make_dirs(os.path.dirname(signing_keyfile),
mode=PRIVATE_DIR_PERMS,
user=self.use_keystone_user,
group=self.use_keystone_group, log=LOG)
if not file_exists(signing_keyfile):
self.exec_command(['openssl', 'genrsa', '-out', '%(signing_key)s',
'%(key_size)d'])
utils.set_permissions(signing_keyfile,
mode=PRIVATE_FILE_PERMS,
user=self.use_keystone_user,
group=self.use_keystone_group, log=LOG)
**** CubicPower OpenStack Study ****
def build_signing_cert(self):
signing_cert = self.ssl_dictionary['signing_cert']
utils.make_dirs(os.path.dirname(signing_cert),
mode=PUBLIC_DIR_PERMS,
user=self.use_keystone_user,
group=self.use_keystone_group, log=LOG)
if not file_exists(signing_cert):
self.exec_command(['openssl', 'req', '-key', '%(signing_key)s',
'-new', '-out', '%(request_file)s',
'-config', '%(ssl_config)s',
'-subj', '%(cert_subject)s'])
self.exec_command(['openssl', 'ca', '-batch',
'-out', '%(signing_cert)s',
'-config', '%(ssl_config)s',
'-days', '%(valid_days)dd',
'-cert', '%(ca_cert)s',
'-keyfile', '%(ca_private_key)s',
'-infiles', '%(request_file)s'])
**** CubicPower OpenStack Study ****
def run(self):
self.build_ssl_config_file()
self.build_ca_cert()
self.build_private_key()
self.build_signing_cert()
**** CubicPower OpenStack Study ****
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
**** CubicPower OpenStack Study ****
def __init__(self, keystone_user, keystone_group):
super(ConfigurePKI, self).__init__(CONF.signing,
keystone_user, keystone_group)
**** CubicPower OpenStack Study ****
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.
"""
**** CubicPower OpenStack Study ****
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
[ CA_default ]
new_certs_dir = $dir
serial = $dir/serial
database = $dir/index.txt
default_days = 365
default_md = %(default_md)s
preserve = no
email_in_dn = no
nameopt = default_ca
certopt = default_ca
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 = 2048 # Size of keys
default_keyfile = key.pem # name of generated keys
string_mask = utf8only # permitted characters
distinguished_name = req_distinguished_name
req_extensions = v3_req
x509_extensions = v3_ca
[ req_distinguished_name ]
countryName = Country Name (2 letter code)
countryName_min = 2
countryName_max = 2
stateOrProvinceName = State or Province Name (full name)
localityName = Locality Name (city, district)
0.organizationName = Organization Name (company)
organizationalUnitName = Organizational Unit Name (department, division)
commonName = Common Name (hostname, IP, or your name)
commonName_max = 64
emailAddress = Email Address
emailAddress_max = 64
[ v3_ca ]
basicConstraints = CA:TRUE
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always,issuer
[ v3_req ]
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
[ usr_cert ]
basicConstraints = CA:FALSE
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid:always
"""