Create nova cert worker for x509 support
* Adds new worker for cert management * Makes decrypt use an rpc to the worker * Moves CA filesystem creation out of cloud.setup * Moves test for X509 into crypto * Adds test for encrypting and decrypting using cert * Cleans up extra code in cloudpipe * Fixes bug 918563 * Prepares for a future patch that will fix bug 903345 Change-Id: I4693c50c8f432706f97395af39e736f49d60e719
This commit is contained in:
parent
30a40db708
commit
0c5273c85e
@ -65,7 +65,7 @@ if __name__ == '__main__':
|
|||||||
except (Exception, SystemExit):
|
except (Exception, SystemExit):
|
||||||
logging.exception(_('Failed to load %s') % 'objectstore-wsgi')
|
logging.exception(_('Failed to load %s') % 'objectstore-wsgi')
|
||||||
for binary in ['nova-xvpvncproxy', 'nova-compute', 'nova-volume',
|
for binary in ['nova-xvpvncproxy', 'nova-compute', 'nova-volume',
|
||||||
'nova-network', 'nova-scheduler', 'nova-vsa']:
|
'nova-network', 'nova-scheduler', 'nova-vsa', 'nova-cert']:
|
||||||
try:
|
try:
|
||||||
servers.append(service.Service.create(binary=binary))
|
servers.append(service.Service.create(binary=binary))
|
||||||
except (Exception, SystemExit):
|
except (Exception, SystemExit):
|
||||||
|
47
bin/nova-cert
Executable file
47
bin/nova-cert
Executable file
@ -0,0 +1,47 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# 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.
|
||||||
|
|
||||||
|
"""Starter script for Nova Cert."""
|
||||||
|
|
||||||
|
import eventlet
|
||||||
|
eventlet.monkey_patch()
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
# If ../nova/__init__.py exists, add ../ to Python search path, so that
|
||||||
|
# it will override what happens to be installed in /usr/(local/)lib/python...
|
||||||
|
POSSIBLE_TOPDIR = os.path.normpath(os.path.join(os.path.abspath(sys.argv[0]),
|
||||||
|
os.pardir,
|
||||||
|
os.pardir))
|
||||||
|
if os.path.exists(os.path.join(POSSIBLE_TOPDIR, 'nova', '__init__.py')):
|
||||||
|
sys.path.insert(0, POSSIBLE_TOPDIR)
|
||||||
|
|
||||||
|
|
||||||
|
from nova import flags
|
||||||
|
from nova import log as logging
|
||||||
|
from nova import service
|
||||||
|
from nova import utils
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
utils.default_flagfile()
|
||||||
|
flags.FLAGS(sys.argv)
|
||||||
|
logging.setup()
|
||||||
|
utils.monkey_patch()
|
||||||
|
server = service.Service.create(binary='nova-cert')
|
||||||
|
service.serve(server)
|
||||||
|
service.wait()
|
@ -205,35 +205,10 @@ class CloudController(object):
|
|||||||
self.volume_api = volume.API()
|
self.volume_api = volume.API()
|
||||||
self.compute_api = compute.API(network_api=self.network_api,
|
self.compute_api = compute.API(network_api=self.network_api,
|
||||||
volume_api=self.volume_api)
|
volume_api=self.volume_api)
|
||||||
self.setup()
|
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return 'CloudController'
|
return 'CloudController'
|
||||||
|
|
||||||
def setup(self):
|
|
||||||
""" Ensure the keychains and folders exist. """
|
|
||||||
# FIXME(ja): this should be moved to a nova-manage command,
|
|
||||||
# if not setup throw exceptions instead of running
|
|
||||||
# Create keys folder, if it doesn't exist
|
|
||||||
if not os.path.exists(FLAGS.keys_path):
|
|
||||||
os.makedirs(FLAGS.keys_path)
|
|
||||||
# Gen root CA, if we don't have one
|
|
||||||
root_ca_path = os.path.join(FLAGS.ca_path, FLAGS.ca_file)
|
|
||||||
if not os.path.exists(root_ca_path):
|
|
||||||
genrootca_sh_path = os.path.join(os.path.dirname(__file__),
|
|
||||||
os.path.pardir,
|
|
||||||
os.path.pardir,
|
|
||||||
'CA',
|
|
||||||
'genrootca.sh')
|
|
||||||
|
|
||||||
start = os.getcwd()
|
|
||||||
if not os.path.exists(FLAGS.ca_path):
|
|
||||||
os.makedirs(FLAGS.ca_path)
|
|
||||||
os.chdir(FLAGS.ca_path)
|
|
||||||
# TODO(vish): Do this with M2Crypto instead
|
|
||||||
utils.runthis(_("Generating root CA: %s"), "sh", genrootca_sh_path)
|
|
||||||
os.chdir(start)
|
|
||||||
|
|
||||||
def _get_image_state(self, image):
|
def _get_image_state(self, image):
|
||||||
# NOTE(vish): fallback status if image_state isn't set
|
# NOTE(vish): fallback status if image_state isn't set
|
||||||
state = image.get('status')
|
state = image.get('status')
|
||||||
|
@ -60,28 +60,11 @@ class CloudpipeController(object):
|
|||||||
|
|
||||||
def setup(self):
|
def setup(self):
|
||||||
"""Ensure the keychains and folders exist."""
|
"""Ensure the keychains and folders exist."""
|
||||||
# TODO(todd): this was copyed from api.ec2.cloud
|
# NOTE(vish): One of the drawbacks of doing this in the api is
|
||||||
# FIXME(ja): this should be moved to a nova-manage command,
|
# the keys will only be on the api node that launched
|
||||||
# if not setup throw exceptions instead of running
|
# the cloudpipe.
|
||||||
# Create keys folder, if it doesn't exist
|
|
||||||
if not os.path.exists(FLAGS.keys_path):
|
if not os.path.exists(FLAGS.keys_path):
|
||||||
os.makedirs(FLAGS.keys_path)
|
os.makedirs(FLAGS.keys_path)
|
||||||
# Gen root CA, if we don't have one
|
|
||||||
root_ca_path = os.path.join(FLAGS.ca_path, FLAGS.ca_file)
|
|
||||||
if not os.path.exists(root_ca_path):
|
|
||||||
genrootca_sh_path = os.path.join(os.path.dirname(__file__),
|
|
||||||
os.path.pardir,
|
|
||||||
os.path.pardir,
|
|
||||||
'CA',
|
|
||||||
'genrootca.sh')
|
|
||||||
|
|
||||||
start = os.getcwd()
|
|
||||||
if not os.path.exists(FLAGS.ca_path):
|
|
||||||
os.makedirs(FLAGS.ca_path)
|
|
||||||
os.chdir(FLAGS.ca_path)
|
|
||||||
# TODO(vish): Do this with M2Crypto instead
|
|
||||||
utils.runthis(_("Generating root CA: %s"), "sh", genrootca_sh_path)
|
|
||||||
os.chdir(start)
|
|
||||||
|
|
||||||
def _get_cloudpipe_for_project(self, context, project_id):
|
def _get_cloudpipe_for_project(self, context, project_id):
|
||||||
"""Get the cloudpipe instance for a project ID."""
|
"""Get the cloudpipe instance for a project ID."""
|
||||||
|
15
nova/cert/__init__.py
Normal file
15
nova/cert/__init__.py
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
# 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.
|
67
nova/cert/manager.py
Normal file
67
nova/cert/manager.py
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
# 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.
|
||||||
|
"""
|
||||||
|
Cert manager manages x509 certificates.
|
||||||
|
|
||||||
|
**Related Flags**
|
||||||
|
:cert_topic: What :mod:`rpc` topic to listen to (default: `cert`).
|
||||||
|
:cert_manager: The module name of a class derived from
|
||||||
|
:class:`manager.Manager` (default:
|
||||||
|
:class:`nova.cert.manager.Manager`).
|
||||||
|
"""
|
||||||
|
|
||||||
|
import base64
|
||||||
|
|
||||||
|
from nova import crypto
|
||||||
|
from nova import flags
|
||||||
|
from nova import log as logging
|
||||||
|
from nova import manager
|
||||||
|
|
||||||
|
LOG = logging.getLogger('nova.cert.manager')
|
||||||
|
FLAGS = flags.FLAGS
|
||||||
|
|
||||||
|
|
||||||
|
class CertManager(manager.Manager):
|
||||||
|
def init_host(self):
|
||||||
|
crypto.ensure_ca_filesystem()
|
||||||
|
|
||||||
|
def revoke_certs_by_user(self, context, user_id):
|
||||||
|
"""Revoke all user certs."""
|
||||||
|
return crypto.revoke_certs_by_user(user_id)
|
||||||
|
|
||||||
|
def revoke_certs_by_project(self, context, project_id):
|
||||||
|
"""Revoke all project certs."""
|
||||||
|
return crypto.revoke_certs_by_project(project_id)
|
||||||
|
|
||||||
|
def revoke_certs_by_user_and_project(self, context, user_id, project_id):
|
||||||
|
"""Revoke certs for user in project."""
|
||||||
|
return crypto.revoke_certs_by_user_and_project(project_id)
|
||||||
|
|
||||||
|
def generate_x509_cert(self, context, user_id, project_id):
|
||||||
|
"""Generate and sign a cert for user in project"""
|
||||||
|
return crypto.generate_x509_cert(user_id, project_id)
|
||||||
|
|
||||||
|
def fetch_ca(self, context, project_id):
|
||||||
|
"""Get root ca for a project"""
|
||||||
|
return crypto.fetch_ca(project_id)
|
||||||
|
|
||||||
|
def fetch_crl(self, context, project_id):
|
||||||
|
"""Get crl for a project"""
|
||||||
|
return crypto.fetch_ca(project_id)
|
||||||
|
|
||||||
|
def decrypt_text(self, context, project_id, text):
|
||||||
|
"""Decrypt base64 encoded text using the projects private key."""
|
||||||
|
return crypto.decrypt_text(project_id, base64.b64decode(text))
|
@ -39,6 +39,7 @@ gettext.install('nova', unicode=1)
|
|||||||
|
|
||||||
from nova import context
|
from nova import context
|
||||||
from nova import db
|
from nova import db
|
||||||
|
from nova import exception
|
||||||
from nova import flags
|
from nova import flags
|
||||||
from nova import log as logging
|
from nova import log as logging
|
||||||
|
|
||||||
@ -85,6 +86,10 @@ def key_path(project_id=None):
|
|||||||
return os.path.join(ca_folder(project_id), FLAGS.key_file)
|
return os.path.join(ca_folder(project_id), FLAGS.key_file)
|
||||||
|
|
||||||
|
|
||||||
|
def crl_path(project_id=None):
|
||||||
|
return os.path.join(ca_folder(project_id), FLAGS.crl_file)
|
||||||
|
|
||||||
|
|
||||||
def fetch_ca(project_id=None):
|
def fetch_ca(project_id=None):
|
||||||
if not FLAGS.use_project_ca:
|
if not FLAGS.use_project_ca:
|
||||||
project_id = None
|
project_id = None
|
||||||
@ -92,6 +97,22 @@ def fetch_ca(project_id=None):
|
|||||||
return cafile.read()
|
return cafile.read()
|
||||||
|
|
||||||
|
|
||||||
|
def ensure_ca_filesystem():
|
||||||
|
"""Ensure the CA filesystem exists."""
|
||||||
|
ca_dir = ca_folder()
|
||||||
|
if not os.path.exists(ca_path()):
|
||||||
|
genrootca_sh_path = os.path.join(os.path.dirname(__file__),
|
||||||
|
'CA',
|
||||||
|
'genrootca.sh')
|
||||||
|
|
||||||
|
start = os.getcwd()
|
||||||
|
if not os.path.exists(ca_dir):
|
||||||
|
os.makedirs(ca_dir)
|
||||||
|
os.chdir(ca_dir)
|
||||||
|
utils.runthis(_("Generating root CA: %s"), "sh", genrootca_sh_path)
|
||||||
|
os.chdir(start)
|
||||||
|
|
||||||
|
|
||||||
def _generate_fingerprint(public_key_file):
|
def _generate_fingerprint(public_key_file):
|
||||||
(out, err) = utils.execute('ssh-keygen', '-q', '-l', '-f', public_key_file)
|
(out, err) = utils.execute('ssh-keygen', '-q', '-l', '-f', public_key_file)
|
||||||
fingerprint = out.split(' ')[1]
|
fingerprint = out.split(' ')[1]
|
||||||
@ -148,6 +169,29 @@ def ssl_pub_to_ssh_pub(ssl_public_key, name='root', suffix='nova'):
|
|||||||
return '%s %s %s@%s\n' % (key_type, b64_blob, name, suffix)
|
return '%s %s %s@%s\n' % (key_type, b64_blob, name, suffix)
|
||||||
|
|
||||||
|
|
||||||
|
def fetch_crl(project_id):
|
||||||
|
"""Get crl file for project."""
|
||||||
|
if not FLAGS.use_project_ca:
|
||||||
|
project_id = None
|
||||||
|
with open(crl_path(project_id), 'r') as crlfile:
|
||||||
|
return crlfile.read()
|
||||||
|
|
||||||
|
|
||||||
|
def decrypt_text(project_id, text):
|
||||||
|
private_key = key_path(project_id)
|
||||||
|
if not os.path.exists(private_key):
|
||||||
|
raise exception.ProjectNotFound(project_id=project_id)
|
||||||
|
try:
|
||||||
|
dec, _err = utils.execute('openssl',
|
||||||
|
'rsautl',
|
||||||
|
'-decrypt',
|
||||||
|
'-inkey', '%s' % private_key,
|
||||||
|
process_input=text)
|
||||||
|
return dec
|
||||||
|
except exception.ProcessExecutionError:
|
||||||
|
raise exception.DecryptionFailure()
|
||||||
|
|
||||||
|
|
||||||
def revoke_cert(project_id, file_name):
|
def revoke_cert(project_id, file_name):
|
||||||
"""Revoke a cert by file name."""
|
"""Revoke a cert by file name."""
|
||||||
start = os.getcwd()
|
start = os.getcwd()
|
||||||
|
@ -179,6 +179,10 @@ class NovaException(Exception):
|
|||||||
super(NovaException, self).__init__(message)
|
super(NovaException, self).__init__(message)
|
||||||
|
|
||||||
|
|
||||||
|
class DecryptionFailure(NovaException):
|
||||||
|
message = _("Failed to decrypt text")
|
||||||
|
|
||||||
|
|
||||||
class ImagePaginationFailed(NovaException):
|
class ImagePaginationFailed(NovaException):
|
||||||
message = _("Failed to paginate through images from image service")
|
message = _("Failed to paginate through images from image service")
|
||||||
|
|
||||||
|
@ -275,6 +275,7 @@ DEFINE_integer('glance_num_retries', 0,
|
|||||||
DEFINE_integer('s3_port', 3333, 's3 port')
|
DEFINE_integer('s3_port', 3333, 's3 port')
|
||||||
DEFINE_string('s3_host', '$my_ip', 's3 host (for infrastructure)')
|
DEFINE_string('s3_host', '$my_ip', 's3 host (for infrastructure)')
|
||||||
DEFINE_string('s3_dmz', '$my_ip', 's3 dmz ip (for instances)')
|
DEFINE_string('s3_dmz', '$my_ip', 's3 dmz ip (for instances)')
|
||||||
|
DEFINE_string('cert_topic', 'cert', 'the topic cert nodes listen on')
|
||||||
DEFINE_string('compute_topic', 'compute', 'the topic compute nodes listen on')
|
DEFINE_string('compute_topic', 'compute', 'the topic compute nodes listen on')
|
||||||
DEFINE_string('console_topic', 'console',
|
DEFINE_string('console_topic', 'console',
|
||||||
'the topic console proxy nodes listen on')
|
'the topic console proxy nodes listen on')
|
||||||
@ -367,6 +368,8 @@ DEFINE_string('compute_manager', 'nova.compute.manager.ComputeManager',
|
|||||||
'Manager for compute')
|
'Manager for compute')
|
||||||
DEFINE_string('console_manager', 'nova.console.manager.ConsoleProxyManager',
|
DEFINE_string('console_manager', 'nova.console.manager.ConsoleProxyManager',
|
||||||
'Manager for console proxy')
|
'Manager for console proxy')
|
||||||
|
DEFINE_string('cert_manager', 'nova.cert.manager.CertManager',
|
||||||
|
'Manager for cert')
|
||||||
DEFINE_string('instance_dns_manager',
|
DEFINE_string('instance_dns_manager',
|
||||||
'nova.network.dns_driver.DNSDriver',
|
'nova.network.dns_driver.DNSDriver',
|
||||||
'DNS Manager for instance IPs')
|
'DNS Manager for instance IPs')
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
"""Proxy AMI-related calls from cloud controller to objectstore service."""
|
"""Proxy AMI-related calls from cloud controller to objectstore service."""
|
||||||
|
|
||||||
|
import base64
|
||||||
import binascii
|
import binascii
|
||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
@ -28,7 +29,7 @@ from xml.etree import ElementTree
|
|||||||
import boto.s3.connection
|
import boto.s3.connection
|
||||||
import eventlet
|
import eventlet
|
||||||
|
|
||||||
from nova import crypto
|
from nova import rpc
|
||||||
import nova.db.api
|
import nova.db.api
|
||||||
from nova import exception
|
from nova import exception
|
||||||
from nova import flags
|
from nova import flags
|
||||||
@ -302,14 +303,9 @@ class S3ImageService(object):
|
|||||||
hex_iv = manifest.find('image/ec2_encrypted_iv').text
|
hex_iv = manifest.find('image/ec2_encrypted_iv').text
|
||||||
encrypted_iv = binascii.a2b_hex(hex_iv)
|
encrypted_iv = binascii.a2b_hex(hex_iv)
|
||||||
|
|
||||||
# FIXME(vish): grab key from common service so this can run on
|
|
||||||
# any host.
|
|
||||||
cloud_pk = crypto.key_path(context.project_id)
|
|
||||||
|
|
||||||
dec_filename = os.path.join(image_path, 'image.tar.gz')
|
dec_filename = os.path.join(image_path, 'image.tar.gz')
|
||||||
self._decrypt_image(enc_filename, encrypted_key,
|
self._decrypt_image(context, enc_filename, encrypted_key,
|
||||||
encrypted_iv, cloud_pk,
|
encrypted_iv, dec_filename)
|
||||||
dec_filename)
|
|
||||||
except Exception:
|
except Exception:
|
||||||
LOG.exception(_("Failed to decrypt %(image_location)s "
|
LOG.exception(_("Failed to decrypt %(image_location)s "
|
||||||
"to %(image_path)s"), log_vars)
|
"to %(image_path)s"), log_vars)
|
||||||
@ -353,39 +349,38 @@ class S3ImageService(object):
|
|||||||
return image
|
return image
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _decrypt_image(encrypted_filename, encrypted_key, encrypted_iv,
|
def _decrypt_image(context, encrypted_filename, encrypted_key,
|
||||||
cloud_private_key, decrypted_filename):
|
encrypted_iv, decrypted_filename):
|
||||||
key, err = utils.execute('openssl',
|
elevated = context.elevated()
|
||||||
'rsautl',
|
try:
|
||||||
'-decrypt',
|
key = rpc.call(elevated, FLAGS.cert_topic,
|
||||||
'-inkey', '%s' % cloud_private_key,
|
{"method": "decrypt_text",
|
||||||
process_input=encrypted_key,
|
"args": {"project_id": context.project_id,
|
||||||
check_exit_code=False)
|
"text": base64.b64encode(encrypted_key)}})
|
||||||
if err:
|
except Exception, exc:
|
||||||
raise exception.Error(_('Failed to decrypt private key: %s')
|
raise exception.Error(_('Failed to decrypt private key: %s')
|
||||||
% err)
|
% exc)
|
||||||
iv, err = utils.execute('openssl',
|
try:
|
||||||
'rsautl',
|
iv = rpc.call(elevated, FLAGS.cert_topic,
|
||||||
'-decrypt',
|
{"method": "decrypt_text",
|
||||||
'-inkey', '%s' % cloud_private_key,
|
"args": {"project_id": context.project_id,
|
||||||
process_input=encrypted_iv,
|
"text": base64.b64encode(encrypted_iv)}})
|
||||||
check_exit_code=False)
|
except Exception, exc:
|
||||||
if err:
|
|
||||||
raise exception.Error(_('Failed to decrypt initialization '
|
raise exception.Error(_('Failed to decrypt initialization '
|
||||||
'vector: %s') % err)
|
'vector: %s') % exc)
|
||||||
|
|
||||||
_out, err = utils.execute('openssl', 'enc',
|
try:
|
||||||
'-d', '-aes-128-cbc',
|
utils.execute('openssl', 'enc',
|
||||||
'-in', '%s' % (encrypted_filename,),
|
'-d', '-aes-128-cbc',
|
||||||
'-K', '%s' % (key,),
|
'-in', '%s' % (encrypted_filename,),
|
||||||
'-iv', '%s' % (iv,),
|
'-K', '%s' % (key,),
|
||||||
'-out', '%s' % (decrypted_filename,),
|
'-iv', '%s' % (iv,),
|
||||||
check_exit_code=False)
|
'-out', '%s' % (decrypted_filename,))
|
||||||
if err:
|
except exception.ProcessExecutionError, exc:
|
||||||
raise exception.Error(_('Failed to decrypt image file '
|
raise exception.Error(_('Failed to decrypt image file '
|
||||||
'%(image_file)s: %(err)s') %
|
'%(image_file)s: %(err)s') %
|
||||||
{'image_file': encrypted_filename,
|
{'image_file': encrypted_filename,
|
||||||
'err': err})
|
'err': exc.stdout})
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _test_for_malicious_tarball(path, filename):
|
def _test_for_malicious_tarball(path, filename):
|
||||||
|
@ -16,7 +16,6 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
from M2Crypto import X509
|
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
from nova import crypto
|
from nova import crypto
|
||||||
@ -245,28 +244,6 @@ class _AuthManagerBaseTestCase(test.TestCase):
|
|||||||
project))
|
project))
|
||||||
self.assertFalse(self.manager.is_project_member(user, project))
|
self.assertFalse(self.manager.is_project_member(user, project))
|
||||||
|
|
||||||
def test_can_generate_x509(self):
|
|
||||||
# NOTE(todd): this doesn't assert against the auth manager
|
|
||||||
# so it probably belongs in crypto_unittest
|
|
||||||
# but I'm leaving it where I found it.
|
|
||||||
with user_and_project_generator(self.manager) as (user, project):
|
|
||||||
# NOTE(vish): Setup runs genroot.sh if it hasn't been run
|
|
||||||
cloud.CloudController().setup()
|
|
||||||
_key, cert_str = crypto.generate_x509_cert(user.id, project.id)
|
|
||||||
LOG.debug(cert_str)
|
|
||||||
|
|
||||||
int_cert = crypto.fetch_ca(project_id=project.id)
|
|
||||||
cloud_cert = crypto.fetch_ca()
|
|
||||||
signed_cert = X509.load_cert_string(cert_str)
|
|
||||||
int_cert = X509.load_cert_string(int_cert)
|
|
||||||
cloud_cert = X509.load_cert_string(cloud_cert)
|
|
||||||
self.assertTrue(signed_cert.verify(int_cert.get_pubkey()))
|
|
||||||
|
|
||||||
if not FLAGS.use_project_ca:
|
|
||||||
self.assertTrue(signed_cert.verify(cloud_cert.get_pubkey()))
|
|
||||||
else:
|
|
||||||
self.assertFalse(signed_cert.verify(cloud_cert.get_pubkey()))
|
|
||||||
|
|
||||||
def test_adding_role_to_project_is_ignored_unless_added_to_user(self):
|
def test_adding_role_to_project_is_ignored_unless_added_to_user(self):
|
||||||
with user_and_project_generator(self.manager) as (user, project):
|
with user_and_project_generator(self.manager) as (user, project):
|
||||||
self.assertFalse(self.manager.has_role(user, 'sysadmin', project))
|
self.assertFalse(self.manager.has_role(user, 'sysadmin', project))
|
||||||
|
@ -16,12 +16,20 @@
|
|||||||
Tests for Crypto module.
|
Tests for Crypto module.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
import tempfile
|
||||||
|
|
||||||
import mox
|
import mox
|
||||||
import stubout
|
from M2Crypto import X509
|
||||||
|
|
||||||
from nova import crypto
|
from nova import crypto
|
||||||
from nova import db
|
from nova import db
|
||||||
|
from nova import flags
|
||||||
from nova import test
|
from nova import test
|
||||||
|
from nova import utils
|
||||||
|
|
||||||
|
FLAGS = flags.FLAGS
|
||||||
|
|
||||||
|
|
||||||
class SymmetricKeyTestCase(test.TestCase):
|
class SymmetricKeyTestCase(test.TestCase):
|
||||||
@ -52,16 +60,55 @@ class SymmetricKeyTestCase(test.TestCase):
|
|||||||
self.assertEquals(plain_text, plain)
|
self.assertEquals(plain_text, plain)
|
||||||
|
|
||||||
|
|
||||||
|
class X509Test(test.TestCase):
|
||||||
|
def test_can_generate_x509(self):
|
||||||
|
tmpdir = tempfile.mkdtemp()
|
||||||
|
self.flags(ca_path=tmpdir)
|
||||||
|
try:
|
||||||
|
crypto.ensure_ca_filesystem()
|
||||||
|
_key, cert_str = crypto.generate_x509_cert('fake', 'fake')
|
||||||
|
|
||||||
|
project_cert = crypto.fetch_ca(project_id='fake')
|
||||||
|
cloud_cert = crypto.fetch_ca()
|
||||||
|
# TODO(vish): This will need to be replaced with something else
|
||||||
|
# when we remove M2Crypto
|
||||||
|
signed_cert = X509.load_cert_string(cert_str)
|
||||||
|
project_cert = X509.load_cert_string(project_cert)
|
||||||
|
cloud_cert = X509.load_cert_string(cloud_cert)
|
||||||
|
self.assertTrue(signed_cert.verify(project_cert.get_pubkey()))
|
||||||
|
|
||||||
|
if not FLAGS.use_project_ca:
|
||||||
|
self.assertTrue(signed_cert.verify(cloud_cert.get_pubkey()))
|
||||||
|
else:
|
||||||
|
self.assertFalse(signed_cert.verify(cloud_cert.get_pubkey()))
|
||||||
|
finally:
|
||||||
|
shutil.rmtree(tmpdir)
|
||||||
|
|
||||||
|
def test_encrypt_decrypt_x509(self):
|
||||||
|
tmpdir = tempfile.mkdtemp()
|
||||||
|
self.flags(ca_path=tmpdir)
|
||||||
|
project_id = "fake"
|
||||||
|
try:
|
||||||
|
crypto.ensure_ca_filesystem()
|
||||||
|
cert = crypto.fetch_ca(project_id)
|
||||||
|
public_key = os.path.join(tmpdir, "public.pem")
|
||||||
|
with open(public_key, 'w') as keyfile:
|
||||||
|
keyfile.write(cert)
|
||||||
|
text = "some @#!%^* test text"
|
||||||
|
enc, _err = utils.execute('openssl',
|
||||||
|
'rsautl',
|
||||||
|
'-certin',
|
||||||
|
'-encrypt',
|
||||||
|
'-inkey', '%s' % public_key,
|
||||||
|
process_input=text)
|
||||||
|
dec = crypto.decrypt_text(project_id, enc)
|
||||||
|
self.assertEqual(text, dec)
|
||||||
|
finally:
|
||||||
|
shutil.rmtree(tmpdir)
|
||||||
|
|
||||||
|
|
||||||
class RevokeCertsTest(test.TestCase):
|
class RevokeCertsTest(test.TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
super(RevokeCertsTest, self).setUp()
|
|
||||||
self.stubs = stubout.StubOutForTesting()
|
|
||||||
|
|
||||||
def tearDown(self):
|
|
||||||
self.stubs.UnsetAll()
|
|
||||||
super(RevokeCertsTest, self).tearDown()
|
|
||||||
|
|
||||||
def test_revoke_certs_by_user_and_project(self):
|
def test_revoke_certs_by_user_and_project(self):
|
||||||
user_id = 'test_user'
|
user_id = 'test_user'
|
||||||
project_id = 2
|
project_id = 2
|
||||||
|
1
setup.py
1
setup.py
@ -89,6 +89,7 @@ setup(name='nova',
|
|||||||
'bin/nova-api-metadata',
|
'bin/nova-api-metadata',
|
||||||
'bin/nova-api-os-compute',
|
'bin/nova-api-os-compute',
|
||||||
'bin/nova-api-os-volume',
|
'bin/nova-api-os-volume',
|
||||||
|
'bin/nova-cert',
|
||||||
'bin/nova-compute',
|
'bin/nova-compute',
|
||||||
'bin/nova-console',
|
'bin/nova-console',
|
||||||
'bin/nova-consoleauth',
|
'bin/nova-consoleauth',
|
||||||
|
Loading…
Reference in New Issue
Block a user