-
Notifications
You must be signed in to change notification settings - Fork 695
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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
- Loading branch information
Adam Young
committed
Jul 2, 2012
1 parent
b45c252
commit 5ad8086
Showing
5 changed files
with
299 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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""" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
# 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 under the License. | ||
|
||
import os | ||
import unittest2 as test | ||
import shutil | ||
|
||
from keystone import config | ||
from keystone.common import openssl | ||
|
||
ROOTDIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) | ||
SSLDIR = "%s/tests/ssl/" % ROOTDIR | ||
CONF = config.CONF | ||
|
||
|
||
def rootdir(*p): | ||
return os.path.join(SSLDIR, *p) | ||
|
||
|
||
CERTDIR = rootdir("certs") | ||
KEYDIR = rootdir("private") | ||
|
||
CONF.signing.certfile = os.path.join(CERTDIR, 'signing_cert.pem') | ||
CONF.signing.ca_certs = os.path.join(CERTDIR, "ca.pem") | ||
CONF.signing.keyfile = os.path.join(KEYDIR, "signing_key.pem") | ||
|
||
|
||
class CertSetupTestCase(test.TestCase): | ||
|
||
def test_create_certs(self): | ||
ssl = openssl.ConfigurePKI() | ||
ssl.run() | ||
self.assertTrue(os.path.exists(CONF.signing.certfile)) | ||
self.assertTrue(os.path.exists(CONF.signing.ca_certs)) | ||
self.assertTrue(os.path.exists(CONF.signing.keyfile)) | ||
|
||
def tearDown(self): | ||
shutil.rmtree(rootdir(SSLDIR)) |