keystone_manage certificate generation
Bug 1017554 paths now correspond with SSL unit test for cert generation Added mode config values Explict about umask replace string concat for paths with proper use of os.path.join Change-Id: I8b3bec82d7b72993aa69653f63ff64c3f675f716
This commit is contained in:
214
keystone/common/openssl.py
Normal file
214
keystone/common/openssl.py
Normal file
@@ -0,0 +1,214 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2012 OpenStack LLC
|
||||
#
|
||||
# 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
|
||||
#
|
||||
|
||||
import os
|
||||
import stat
|
||||
import subprocess
|
||||
import sys
|
||||
import stat
|
||||
|
||||
from keystone import config
|
||||
|
||||
|
||||
CONF = config.CONF
|
||||
DIR_PERMS = stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR | \
|
||||
stat.S_IRGRP | stat.S_IXGRP | \
|
||||
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):
|
||||
sys.stdout.write("Looking for %s:\t" % file_path)
|
||||
if os.path.exists(file_path):
|
||||
print("[FOUND]")
|
||||
return True
|
||||
else:
|
||||
print("[NOT FOUND]")
|
||||
return False
|
||||
|
||||
|
||||
def make_dirs(file_name):
|
||||
dir = os.path.dirname(file_name)
|
||||
if not file_exists(dir):
|
||||
os.makedirs(dir, DIR_PERMS)
|
||||
|
||||
|
||||
class ConfigurePKI(object):
|
||||
"""Generate files for PKI siginging 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, *args, **kw):
|
||||
self.conf_dir = os.path.dirname(CONF.signing.ca_certs)
|
||||
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,
|
||||
"ssl_config": self.ssl_config_file_name,
|
||||
"ca_private_key": self.ca_key_file,
|
||||
"ca_cert_cn": "hostname",
|
||||
"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
|
||||
}
|
||||
|
||||
def exec_command(self, command):
|
||||
to_exec = command % self.ssl_dictionary
|
||||
print (to_exec)
|
||||
subprocess.check_call(to_exec.rsplit(" "))
|
||||
|
||||
def build_ssl_config_file(self):
|
||||
if not file_exists(self.ssl_config_file_name):
|
||||
make_dirs(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()
|
||||
os.chmod(self.ssl_config_file_name, CERT_PERMS)
|
||||
|
||||
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()
|
||||
os.chmod(self.ssl_config_file_name, PRIV_PERMS)
|
||||
|
||||
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()
|
||||
os.chmod(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):
|
||||
make_dirs(self.ca_key_file)
|
||||
self.exec_command("openssl genrsa -out %(ca_private_key)s "\
|
||||
"%(key_size)d -config %(ssl_config)s")
|
||||
os.chmod(self.ssl_dictionary["ca_private_key"], stat.S_IRUSR)
|
||||
print("Generating CA certificate")
|
||||
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')
|
||||
os.chmod(self.ssl_dictionary["ca_cert"], CERT_PERMS)
|
||||
|
||||
def build_private_key(self):
|
||||
if not file_exists(CONF.signing.keyfile):
|
||||
make_dirs(CONF.signing.keyfile)
|
||||
|
||||
self.exec_command("openssl genrsa -out %(signing_key)s "\
|
||||
"%(key_size)d "\
|
||||
"-config %(ssl_config)s")
|
||||
os.chmod(os.path.dirname(self.ssl_dictionary["signing_key"]),
|
||||
PRIV_PERMS)
|
||||
os.chmod(self.ssl_dictionary["signing_key"], stat.S_IRUSR)
|
||||
|
||||
def build_signing_cert(self):
|
||||
if not file_exists(CONF.signing.certfile):
|
||||
make_dirs(CONF.signing.certfile)
|
||||
self.exec_command("openssl req -key %(signing_key)s -new -nodes "\
|
||||
"-out %(request_file)s -config %(ssl_config)s "\
|
||||
"-subj %(default_subject)s")
|
||||
self.exec_command("openssl ca -batch -out %(signing_cert)s "\
|
||||
"-config %(ssl_config)s "\
|
||||
"-infiles %(request_file)s")
|
||||
|
||||
def run(self):
|
||||
self.build_ssl_config_file()
|
||||
self.build_ca_cert()
|
||||
self.build_private_key()
|
||||
self.build_signing_cert()
|
||||
|
||||
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
|
||||
certificate = %(ca_cert)s
|
||||
private_key = %(ca_private_key)s
|
||||
default_days = 365
|
||||
default_md = md5
|
||||
preserve = no
|
||||
email_in_dn = no
|
||||
nameopt = default_ca
|
||||
certopt = default_ca
|
||||
policy = policy_match
|
||||
[ policy_match ]
|
||||
countryName = match
|
||||
stateOrProvinceName = match
|
||||
organizationName = match
|
||||
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
|
||||
string_mask = nombstr # permitted characters
|
||||
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
|
||||
|
||||
[ v3_ca ]
|
||||
basicConstraints = CA:TRUE
|
||||
subjectKeyIdentifier = hash
|
||||
authorityKeyIdentifier = keyid:always,issuer:always
|
||||
|
||||
[ v3_req ]
|
||||
basicConstraints = CA:FALSE
|
||||
subjectKeyIdentifier = hash"""
|
||||
Reference in New Issue
Block a user