fixed more conflicts

This commit is contained in:
Vishvananda Ishaya
2010-12-22 21:21:56 +00:00
14 changed files with 317 additions and 95 deletions

View File

@@ -27,6 +27,7 @@ Rick Clark <rick@openstack.org>
Ryan Lucio <rlucio@internap.com> Ryan Lucio <rlucio@internap.com>
Sandy Walsh <sandy.walsh@rackspace.com> Sandy Walsh <sandy.walsh@rackspace.com>
Soren Hansen <soren.hansen@rackspace.com> Soren Hansen <soren.hansen@rackspace.com>
Thierry Carrez <thierry@openstack.org>
Todd Willey <todd@ansolabs.com> Todd Willey <todd@ansolabs.com>
Trey Morris <trey.morris@rackspace.com> Trey Morris <trey.morris@rackspace.com>
Vishvananda Ishaya <vishvananda@gmail.com> Vishvananda Ishaya <vishvananda@gmail.com>

View File

@@ -16,16 +16,24 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
# ARG is the id of the user # $1 is the id of the project and $2 is the subject of the cert
export SUBJ="/C=US/ST=California/L=MountainView/O=AnsoLabs/OU=NovaDev/CN=customer-intCA-$1" NAME=$1
mkdir INTER/$1 SUBJ=$2
cd INTER/$1 mkdir -p projects/$NAME
cd projects/$NAME
cp ../../openssl.cnf.tmpl openssl.cnf cp ../../openssl.cnf.tmpl openssl.cnf
sed -i -e s/%USERNAME%/$1/g openssl.cnf sed -i -e s/%USERNAME%/$NAME/g openssl.cnf
mkdir certs crl newcerts private mkdir certs crl newcerts private
openssl req -new -x509 -extensions v3_ca -keyout private/cakey.pem -out cacert.pem -days 365 -config ./openssl.cnf -batch -nodes
echo "10" > serial echo "10" > serial
touch index.txt touch index.txt
openssl genrsa -out private/cakey.pem 1024 -config ./openssl.cnf -batch -nodes # NOTE(vish): Disabling intermediate ca's because we don't actually need them.
openssl req -new -sha2 -key private/cakey.pem -out ../../reqs/inter$1.csr -batch -subj "$SUBJ" # It makes more sense to have each project have its own root ca.
cd ../../ # openssl genrsa -out private/cakey.pem 1024 -config ./openssl.cnf -batch -nodes
openssl ca -extensions v3_ca -days 365 -out INTER/$1/cacert.pem -in reqs/inter$1.csr -config openssl.cnf -batch # openssl req -new -sha256 -key private/cakey.pem -out ../../reqs/inter$NAME.csr -batch -subj "$SUBJ"
openssl ca -gencrl -config ./openssl.cnf -out crl.pem
if [ "`id -u`" != "`grep nova /etc/passwd | cut -d':' -f3`" ]; then
sudo chown -R nova:nogroup .
fi
# cd ../../
# openssl ca -extensions v3_ca -days 365 -out INTER/$NAME/cacert.pem -in reqs/inter$NAME.csr -config openssl.cnf -batch

View File

@@ -25,4 +25,5 @@ else
openssl req -new -x509 -extensions v3_ca -keyout private/cakey.pem -out cacert.pem -days 365 -config ./openssl.cnf -batch -nodes openssl req -new -x509 -extensions v3_ca -keyout private/cakey.pem -out cacert.pem -days 365 -config ./openssl.cnf -batch -nodes
touch index.txt touch index.txt
echo "10" > serial echo "10" > serial
openssl ca -gencrl -config ./openssl.cnf -out crl.pem
fi fi

36
CA/genvpn.sh Executable file
View File

@@ -0,0 +1,36 @@
#!/bin/bash
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved.
#
# 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.
# This gets zipped and run on the cloudpipe-managed OpenVPN server
NAME=$1
SUBJ=$2
mkdir -p projects/$NAME
cd projects/$NAME
# generate a server priv key
openssl genrsa -out server.key 2048
# generate a server CSR
openssl req -new -key server.key -out server.csr -batch -subj "$SUBJ"
novauid=`getent passwd nova | awk -F: '{print $3}'`
if [ ! -z "${novauid}" ] && [ "`id -u`" != "${novauid}" ]; then
sudo chown -R nova:nogroup .
fi

View File

@@ -24,7 +24,6 @@ dir = .
[ ca ] [ ca ]
default_ca = CA_default default_ca = CA_default
unique_subject = no
[ CA_default ] [ CA_default ]
serial = $dir/serial serial = $dir/serial
@@ -32,6 +31,8 @@ database = $dir/index.txt
new_certs_dir = $dir/newcerts new_certs_dir = $dir/newcerts
certificate = $dir/cacert.pem certificate = $dir/cacert.pem
private_key = $dir/private/cakey.pem private_key = $dir/private/cakey.pem
unique_subject = no
default_crl_days = 365
default_days = 365 default_days = 365
default_md = md5 default_md = md5
preserve = no preserve = no

View File

@@ -72,6 +72,7 @@ if os.path.exists(os.path.join(possible_topdir, 'nova', '__init__.py')):
gettext.install('nova', unicode=1) gettext.install('nova', unicode=1)
from nova import context from nova import context
from nova import crypto
from nova import db from nova import db
from nova import exception from nova import exception
from nova import flags from nova import flags
@@ -96,47 +97,43 @@ class VpnCommands(object):
self.manager = manager.AuthManager() self.manager = manager.AuthManager()
self.pipe = pipelib.CloudPipe() self.pipe = pipelib.CloudPipe()
def list(self): def list(self, project=None):
"""Print a listing of the VPNs for all projects.""" """Print a listing of the VPN data for one or all projects.
args: [project=all]"""
print "%-12s\t" % 'project', print "%-12s\t" % 'project',
print "%-20s\t" % 'ip:port', print "%-20s\t" % 'ip:port',
print "%-20s\t" % 'private_ip',
print "%s" % 'state' print "%s" % 'state'
for project in self.manager.get_projects(): if project:
projects = [self.manager.get_project(project)]
else:
projects = self.manager.get_projects()
# NOTE(vish): This hits the database a lot. We could optimize
# by getting all networks in one query and all vpns
# in aother query, then doing lookups by project
for project in projects:
print "%-12s\t" % project.name, print "%-12s\t" % project.name,
ipport = "%s:%s" % (project.vpn_ip, project.vpn_port)
try: print "%-20s\t" % ipport,
s = "%s:%s" % (project.vpn_ip, project.vpn_port) ctxt = context.get_admin_context()
except exception.NotFound: vpn = db.instance_get_project_vpn(ctxt, project.id)
s = "None"
print "%-20s\t" % s,
vpn = self._vpn_for(project.id)
if vpn: if vpn:
command = "ping -c1 -w1 %s > /dev/null; echo $?" address = None
out, _err = utils.execute(command % vpn['private_dns_name'], state = 'down'
check_exit_code=False) if vpn.get('fixed_ip', None):
if out.strip() == '0': address = vpn['fixed_ip']['address']
net = 'up' if project.vpn_ip and utils.vpn_ping(project.vpn_ip,
else: project.vpn_port):
net = 'down' state = 'up'
print vpn['private_dns_name'], print address,
print vpn['node_name'], print vpn['host'],
print vpn['instance_id'], print vpn['ec2_id'],
print vpn['state_description'], print vpn['state_description'],
print net print state
else: else:
print None print None
def _vpn_for(self, project_id):
"""Get the VPN instance for a project ID."""
for instance in db.instance_get_all(context.get_admin_context()):
if (instance['image_id'] == FLAGS.vpn_image_id
and not instance['state_description'] in
['shutting_down', 'shutdown']
and instance['project_id'] == project_id):
return instance
def spawn(self): def spawn(self):
"""Run all VPNs.""" """Run all VPNs."""
for p in reversed(self.manager.get_projects()): for p in reversed(self.manager.get_projects()):
@@ -149,6 +146,21 @@ class VpnCommands(object):
"""Start the VPN for a given project.""" """Start the VPN for a given project."""
self.pipe.launch_vpn_instance(project_id) self.pipe.launch_vpn_instance(project_id)
def change(self, project_id, ip, port):
"""Change the ip and port for a vpn.
args: project, ip, port"""
project = self.manager.get_project(project_id)
if not project:
print 'No project %s' % (project_id)
return
admin = context.get_admin_context()
network_ref = db.project_get_network(admin, project_id)
db.network_update(admin,
network_ref['id'],
{'vpn_public_address': ip,
'vpn_public_port': int(port)})
class ShellCommands(object): class ShellCommands(object):
def bpython(self): def bpython(self):
@@ -295,6 +307,14 @@ class UserCommands(object):
is_admin = False is_admin = False
self.manager.modify_user(name, access_key, secret_key, is_admin) self.manager.modify_user(name, access_key, secret_key, is_admin)
def revoke(self, user_id, project_id=None):
"""revoke certs for a user
arguments: user_id [project_id]"""
if project_id:
crypto.revoke_certs_by_user_and_project(user_id, project_id)
else:
crypto.revoke_certs_by_user(user_id)
class ProjectCommands(object): class ProjectCommands(object):
"""Class for managing projects.""" """Class for managing projects."""

View File

@@ -64,12 +64,9 @@ flags.DEFINE_string('credential_key_file', 'pk.pem',
'Filename of private key in credentials zip') 'Filename of private key in credentials zip')
flags.DEFINE_string('credential_cert_file', 'cert.pem', flags.DEFINE_string('credential_cert_file', 'cert.pem',
'Filename of certificate in credentials zip') 'Filename of certificate in credentials zip')
flags.DEFINE_string('credential_rc_file', 'novarc', flags.DEFINE_string('credential_rc_file', '%src',
'Filename of rc in credentials zip') 'Filename of rc in credentials zip, %s will be '
flags.DEFINE_string('credential_cert_subject', 'replaced by name of the region (nova by default)')
'/C=US/ST=California/L=MountainView/O=AnsoLabs/'
'OU=NovaDev/CN=%s-%s',
'Subject for certificate for users')
flags.DEFINE_string('auth_driver', 'nova.auth.dbdriver.DbDriver', flags.DEFINE_string('auth_driver', 'nova.auth.dbdriver.DbDriver',
'Driver that auth manager uses') 'Driver that auth manager uses')
@@ -543,11 +540,10 @@ class AuthManager(object):
""" """
network_ref = db.project_get_network(context.get_admin_context(), network_ref = db.project_get_network(context.get_admin_context(),
Project.safe_id(project)) Project.safe_id(project), False)
if not network_ref['vpn_public_port']: if not network_ref:
raise exception.NotFound(_('project network data has not ' return (None, None)
'been set'))
return (network_ref['vpn_public_address'], return (network_ref['vpn_public_address'],
network_ref['vpn_public_port']) network_ref['vpn_public_port'])
@@ -629,27 +625,37 @@ class AuthManager(object):
def get_key_pairs(context): def get_key_pairs(context):
return db.key_pair_get_all_by_user(context.elevated(), context.user_id) return db.key_pair_get_all_by_user(context.elevated(), context.user_id)
def get_credentials(self, user, project=None): def get_credentials(self, user, project=None, use_dmz=True):
"""Get credential zip for user in project""" """Get credential zip for user in project"""
if not isinstance(user, User): if not isinstance(user, User):
user = self.get_user(user) user = self.get_user(user)
if project is None: if project is None:
project = user.id project = user.id
pid = Project.safe_id(project) pid = Project.safe_id(project)
rc = self.__generate_rc(user.access, user.secret, pid) private_key, signed_cert = crypto.generate_x509_cert(user.id, pid)
private_key, signed_cert = self._generate_x509_cert(user.id, pid)
tmpdir = tempfile.mkdtemp() tmpdir = tempfile.mkdtemp()
zf = os.path.join(tmpdir, "temp.zip") zf = os.path.join(tmpdir, "temp.zip")
zippy = zipfile.ZipFile(zf, 'w') zippy = zipfile.ZipFile(zf, 'w')
zippy.writestr(FLAGS.credential_rc_file, rc) if use_dmz and FLAGS.region_list:
regions = {}
for item in FLAGS.region_list:
region, _sep, region_host = item.partition("=")
regions[region] = region_host
else:
regions = {'nova': FLAGS.cc_host}
for region, host in regions.iteritems():
rc = self.__generate_rc(user.access,
user.secret,
pid,
use_dmz,
host)
zippy.writestr(FLAGS.credential_rc_file % region, rc)
zippy.writestr(FLAGS.credential_key_file, private_key) zippy.writestr(FLAGS.credential_key_file, private_key)
zippy.writestr(FLAGS.credential_cert_file, signed_cert) zippy.writestr(FLAGS.credential_cert_file, signed_cert)
try: (vpn_ip, vpn_port) = self.get_project_vpn_data(project)
(vpn_ip, vpn_port) = self.get_project_vpn_data(project)
except exception.NotFound:
vpn_ip = None
if vpn_ip: if vpn_ip:
configfile = open(FLAGS.vpn_client_template, "r") configfile = open(FLAGS.vpn_client_template, "r")
s = string.Template(configfile.read()) s = string.Template(configfile.read())
@@ -662,7 +668,7 @@ class AuthManager(object):
else: else:
logging.warn(_("No vpn data for project %s"), pid) logging.warn(_("No vpn data for project %s"), pid)
zippy.writestr(FLAGS.ca_file, crypto.fetch_ca(user.id)) zippy.writestr(FLAGS.ca_file, crypto.fetch_ca(pid))
zippy.close() zippy.close()
with open(zf, 'rb') as f: with open(zf, 'rb') as f:
read_buffer = f.read() read_buffer = f.read()
@@ -670,38 +676,38 @@ class AuthManager(object):
shutil.rmtree(tmpdir) shutil.rmtree(tmpdir)
return read_buffer return read_buffer
def get_environment_rc(self, user, project=None): def get_environment_rc(self, user, project=None, use_dmz=True):
"""Get credential zip for user in project""" """Get credential zip for user in project"""
if not isinstance(user, User): if not isinstance(user, User):
user = self.get_user(user) user = self.get_user(user)
if project is None: if project is None:
project = user.id project = user.id
pid = Project.safe_id(project) pid = Project.safe_id(project)
return self.__generate_rc(user.access, user.secret, pid) return self.__generate_rc(user.access, user.secret, pid, use_dmz)
@staticmethod @staticmethod
def __generate_rc(access, secret, pid): def __generate_rc(access, secret, pid, use_dmz=True, host=None):
"""Generate rc file for user""" """Generate rc file for user"""
if use_dmz:
cc_host = FLAGS.cc_dmz
else:
cc_host = FLAGS.cc_host
# NOTE(vish): Always use the dmz since it is used from inside the
# instance
s3_host = FLAGS.s3_dmz
if host:
s3_host = host
cc_host = host
rc = open(FLAGS.credentials_template).read() rc = open(FLAGS.credentials_template).read()
rc = rc % {'access': access, rc = rc % {'access': access,
'project': pid, 'project': pid,
'secret': secret, 'secret': secret,
'ec2': FLAGS.ec2_url, 'ec2': '%s://%s:%s%s' % (FLAGS.ec2_prefix,
's3': 'http://%s:%s' % (FLAGS.s3_host, FLAGS.s3_port), cc_host,
FLAGS.cc_port,
FLAGS.ec2_suffix),
's3': 'http://%s:%s' % (s3_host, FLAGS.s3_port),
'nova': FLAGS.ca_file, 'nova': FLAGS.ca_file,
'cert': FLAGS.credential_cert_file, 'cert': FLAGS.credential_cert_file,
'key': FLAGS.credential_key_file} 'key': FLAGS.credential_key_file}
return rc return rc
def _generate_x509_cert(self, uid, pid):
"""Generate x509 cert for user"""
(private_key, csr) = crypto.generate_x509_cert(
self.__cert_subject(uid))
# TODO(joshua): This should be async call back to the cloud controller
signed_cert = crypto.sign_csr(csr, pid)
return (private_key, signed_cert)
@staticmethod
def __cert_subject(uid):
"""Helper to generate cert subject"""
return FLAGS.credential_cert_subject % (uid, utils.isotime())

59
nova/fakememcache.py Normal file
View File

@@ -0,0 +1,59 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved.
#
# 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.
"""Super simple fake memcache client."""
import utils
class Client(object):
"""Replicates a tiny subset of memcached client interface."""
def __init__(self, *args, **kwargs):
"""Ignores the passed in args"""
self.cache = {}
def get(self, key):
"""Retrieves the value for a key or None."""
(timeout, value) = self.cache.get(key, (0, None))
if timeout == 0 or utils.utcnow_ts() < timeout:
return value
return None
def set(self, key, value, time=0, min_compress_len=0):
"""Sets the value for a key."""
timeout = 0
if time != 0:
timeout = utils.utcnow_ts() + time
self.cache[key] = (timeout, value)
return True
def add(self, key, value, time=0, min_compress_len=0):
"""Sets the value for a key if it doesn't exist."""
if not self.get(key) is None:
return False
return self.set(key, value, time, min_compress_len)
def incr(self, key, delta=1):
"""Increments the value for a key."""
value = self.get(key)
if value is None:
return None
new_value = int(value) + delta
self.cache[key] = (self.cache[key][0], str(new_value))
return new_value

View File

@@ -29,6 +29,8 @@ import sys
import gflags import gflags
from nova import utils
class FlagValues(gflags.FlagValues): class FlagValues(gflags.FlagValues):
"""Extension of gflags.FlagValues that allows undefined and runtime flags. """Extension of gflags.FlagValues that allows undefined and runtime flags.
@@ -211,7 +213,8 @@ DEFINE_string('connection_type', 'libvirt', 'libvirt, xenapi or fake')
DEFINE_string('aws_access_key_id', 'admin', 'AWS Access ID') DEFINE_string('aws_access_key_id', 'admin', 'AWS Access ID')
DEFINE_string('aws_secret_access_key', 'admin', 'AWS Access Key') DEFINE_string('aws_secret_access_key', 'admin', 'AWS Access Key')
DEFINE_integer('s3_port', 3333, 's3 port') DEFINE_integer('s3_port', 3333, 's3 port')
DEFINE_string('s3_host', '127.0.0.1', 's3 host') DEFINE_string('s3_host', utils.get_my_ip(), 's3 host (for infrastructure)')
DEFINE_string('s3_dmz', utils.get_my_ip(), 's3 dmz ip (for instances)')
DEFINE_string('compute_topic', 'compute', 'the topic compute nodes listen on') DEFINE_string('compute_topic', 'compute', 'the topic compute nodes listen on')
DEFINE_string('scheduler_topic', 'scheduler', DEFINE_string('scheduler_topic', 'scheduler',
'the topic scheduler nodes listen on') 'the topic scheduler nodes listen on')
@@ -230,8 +233,11 @@ DEFINE_string('rabbit_virtual_host', '/', 'rabbit virtual host')
DEFINE_integer('rabbit_retry_interval', 10, 'rabbit connection retry interval') DEFINE_integer('rabbit_retry_interval', 10, 'rabbit connection retry interval')
DEFINE_integer('rabbit_max_retries', 12, 'rabbit connection attempts') DEFINE_integer('rabbit_max_retries', 12, 'rabbit connection attempts')
DEFINE_string('control_exchange', 'nova', 'the main exchange to connect to') DEFINE_string('control_exchange', 'nova', 'the main exchange to connect to')
DEFINE_string('ec2_url', 'http://127.0.0.1:8773/services/Cloud', DEFINE_string('ec2_prefix', 'http', 'prefix for ec2')
'Url to ec2 api server') DEFINE_string('cc_host', utils.get_my_ip(), 'ip of api server')
DEFINE_string('cc_dmz', utils.get_my_ip(), 'internal ip of api server')
DEFINE_integer('cc_port', 8773, 'cloud controller port')
DEFINE_string('ec2_suffix', '/services/Cloud', 'suffix for ec2')
DEFINE_string('default_image', 'ami-11111', DEFINE_string('default_image', 'ami-11111',
'default image to use, testing only') 'default image to use, testing only')
@@ -241,10 +247,10 @@ DEFINE_string('null_kernel', 'nokernel',
'kernel image that indicates not to use a kernel,' 'kernel image that indicates not to use a kernel,'
' but to use a raw disk image instead') ' but to use a raw disk image instead')
DEFINE_string('vpn_image_id', 'ami-CLOUDPIPE', 'AMI for cloudpipe vpn server') DEFINE_string('vpn_image_id', 'ami-cloudpipe', 'AMI for cloudpipe vpn server')
DEFINE_string('vpn_key_suffix', DEFINE_string('vpn_key_suffix',
'-key', '-vpn',
'Suffix to add to project name for vpn key') 'Suffix to add to project name for vpn key and secgroups')
DEFINE_integer('auth_token_ttl', 3600, 'Seconds for auth tokens to linger') DEFINE_integer('auth_token_ttl', 3600, 'Seconds for auth tokens to linger')

View File

@@ -208,17 +208,13 @@ class AuthManagerTestCase(object):
# so it probably belongs in crypto_unittest # so it probably belongs in crypto_unittest
# but I'm leaving it where I found it. # but I'm leaving it where I found it.
with user_and_project_generator(self.manager) as (user, project): with user_and_project_generator(self.manager) as (user, project):
# NOTE(todd): Should mention why we must setup controller first # NOTE(vish): Setup runs genroot.sh if it hasn't been run
# (somebody please clue me in) cloud.CloudController().setup()
cloud_controller = cloud.CloudController() _key, cert_str = crypto.generate_x509_cert(user.id, project.id)
cloud_controller.setup()
_key, cert_str = self.manager._generate_x509_cert('test1',
'testproj')
logging.debug(cert_str) logging.debug(cert_str)
# Need to verify that it's signed by the right intermediate CA full_chain = crypto.fetch_ca(project_id=project.id, chain=True)
full_chain = crypto.fetch_ca(project_id='testproj', chain=True) int_cert = crypto.fetch_ca(project_id=project.id, chain=False)
int_cert = crypto.fetch_ca(project_id='testproj', chain=False)
cloud_cert = crypto.fetch_ca() cloud_cert = crypto.fetch_ca()
logging.debug("CA chain:\n\n =====\n%s\n\n=====" % full_chain) logging.debug("CA chain:\n\n =====\n%s\n\n=====" % full_chain)
signed_cert = X509.load_cert_string(cert_str) signed_cert = X509.load_cert_string(cert_str)
@@ -227,7 +223,8 @@ class AuthManagerTestCase(object):
cloud_cert = X509.load_cert_string(cloud_cert) cloud_cert = X509.load_cert_string(cloud_cert)
self.assertTrue(signed_cert.verify(chain_cert.get_pubkey())) self.assertTrue(signed_cert.verify(chain_cert.get_pubkey()))
self.assertTrue(signed_cert.verify(int_cert.get_pubkey())) self.assertTrue(signed_cert.verify(int_cert.get_pubkey()))
if not FLAGS.use_intermediate_ca:
if not FLAGS.use_project_ca:
self.assertTrue(signed_cert.verify(cloud_cert.get_pubkey())) self.assertTrue(signed_cert.verify(cloud_cert.get_pubkey()))
else: else:
self.assertFalse(signed_cert.verify(cloud_cert.get_pubkey())) self.assertFalse(signed_cert.verify(cloud_cert.get_pubkey()))

View File

@@ -0,0 +1,86 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved.
#
# 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 datetime
import webob
import webob.dec
import webob.exc
from nova.api import ec2
from nova import flags
from nova import test
from nova import utils
FLAGS = flags.FLAGS
@webob.dec.wsgify
def conditional_forbid(req):
"""Helper wsgi app returns 403 if param 'die' is 1."""
if 'die' in req.params and req.params['die'] == '1':
raise webob.exc.HTTPForbidden()
return 'OK'
class LockoutTestCase(test.TrialTestCase):
"""Test case for the Lockout middleware."""
def setUp(self): # pylint: disable-msg=C0103
super(LockoutTestCase, self).setUp()
utils.set_time_override()
self.lockout = ec2.Lockout(conditional_forbid)
def tearDown(self): # pylint: disable-msg=C0103
utils.clear_time_override()
super(LockoutTestCase, self).tearDown()
def _send_bad_attempts(self, access_key, num_attempts=1):
"""Fail x."""
for i in xrange(num_attempts):
req = webob.Request.blank('/?AWSAccessKeyId=%s&die=1' % access_key)
self.assertEqual(req.get_response(self.lockout).status_int, 403)
def _is_locked_out(self, access_key):
"""Sends a test request to see if key is locked out."""
req = webob.Request.blank('/?AWSAccessKeyId=%s' % access_key)
return (req.get_response(self.lockout).status_int == 403)
def test_lockout(self):
self._send_bad_attempts('test', FLAGS.lockout_attempts)
self.assertTrue(self._is_locked_out('test'))
def test_timeout(self):
self._send_bad_attempts('test', FLAGS.lockout_attempts)
self.assertTrue(self._is_locked_out('test'))
utils.advance_time_seconds(FLAGS.lockout_minutes * 60)
self.assertFalse(self._is_locked_out('test'))
def test_multiple_keys(self):
self._send_bad_attempts('test1', FLAGS.lockout_attempts)
self.assertTrue(self._is_locked_out('test1'))
self.assertFalse(self._is_locked_out('test2'))
utils.advance_time_seconds(FLAGS.lockout_minutes * 60)
self.assertFalse(self._is_locked_out('test1'))
self.assertFalse(self._is_locked_out('test2'))
def test_window_timeout(self):
self._send_bad_attempts('test', FLAGS.lockout_attempts - 1)
self.assertFalse(self._is_locked_out('test'))
utils.advance_time_seconds(FLAGS.lockout_window * 60)
self._send_bad_attempts('test', FLAGS.lockout_attempts - 1)
self.assertFalse(self._is_locked_out('test'))

View File

@@ -60,6 +60,7 @@ from nova.tests.auth_unittest import *
from nova.tests.cloud_unittest import * from nova.tests.cloud_unittest import *
from nova.tests.compute_unittest import * from nova.tests.compute_unittest import *
from nova.tests.flags_unittest import * from nova.tests.flags_unittest import *
from nova.tests.middleware_unittest import *
from nova.tests.misc_unittest import * from nova.tests.misc_unittest import *
from nova.tests.network_unittest import * from nova.tests.network_unittest import *
#from nova.tests.objectstore_unittest import * #from nova.tests.objectstore_unittest import *