High severityNVD Advisory· Published Mar 22, 2013· Updated Apr 29, 2026
CVE-2013-0335
CVE-2013-0335
Description
OpenStack Compute (Nova) Grizzly, Folsom (2012.2), and Essex (2012.1) allows remote authenticated users to gain access to a VM in opportunistic circumstances by using the VNC token for a deleted VM that was bound to the same VNC port.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
NovaPyPI | < 12.0.0a0 | 12.0.0a0 |
Affected products
6cpe:2.3:o:canonical:ubuntu_linux:11.10:*:*:*:*:*:*:*+ 2 more
- cpe:2.3:o:canonical:ubuntu_linux:11.10:*:*:*:*:*:*:*
- cpe:2.3:o:canonical:ubuntu_linux:12.04:-:lts:*:*:*:*:*
- cpe:2.3:o:canonical:ubuntu_linux:12.10:*:*:*:*:*:*:*
Patches
548e81f1554ceFixed broken vncproxy flush tokens patch
3 files changed · +15 −10
nova/compute/api.py+5 −3 modified@@ -1561,12 +1561,14 @@ def get_vnc_console(self, context, instance, console_type): return {'url': connect_info['access_url']} @wrap_check_policy - def validate_vnc_console(self, context, instance_id, host, port): + def validate_vnc_console(self, context, instance_id, host, port, + console_type): """Validate VNC Console for an instance.""" instance = self.get(context, instance_id) output = self._call_compute_message('get_vnc_console', - context, - instance) + context, + instance, + params={"console_type": console_type}) return (port == output['port'] and host == output['host']) @wrap_check_policy
nova/consoleauth/manager.py+5 −4 modified@@ -84,22 +84,23 @@ def authorize_console(self, context, token, console_type, host, port, LOG.audit(_("Received Token: %(token)s, %(token_dict)s)"), locals()) - def _validate_console(self, token): + def _validate_console(self, context, token): console_valid = False token_dict = self.tokens[token] try: console_valid = self.compute_api.validate_vnc_console(context, - token_dict['instance_uuid'], + token_dict['instance_id'], token_dict['host'], - token_dict['port']) + token_dict['port'], + token_dict['console_type']) except exception.InstanceNotFound: pass return console_valid def check_token(self, context, token): token_valid = token in self.tokens LOG.audit(_("Checking Token: %(token)s, %(token_valid)s)"), locals()) - if token_valid and self._validate_console(token): + if token_valid and self._validate_console(context, token): return self.tokens[token] def delete_tokens_for_instance(self, context, instance_id):
nova/tests/test_compute.py+5 −3 modified@@ -767,7 +767,8 @@ def fake(*args, **kwargs): console_valid = self.compute_api.validate_vnc_console(self.context, instance['uuid'], 'myhost', - '5900') + '5900', + 'novnc') self.assertTrue(console_valid) self.compute.terminate_instance(self.context, instance['uuid']) @@ -783,7 +784,8 @@ def fake(*args, **kwargs): console_valid = self.compute_api.validate_vnc_console(self.context, instance['uuid'], 'myhost', - '5900') + '5900', + 'novnc') self.assertFalse(console_valid) self.compute.terminate_instance(self.context, instance['uuid']) @@ -793,7 +795,7 @@ def test_validate_vnc_console_deleted_instance(self): self.compute.run_instance(self.context, instance['uuid']) self.assertRaises(exception.InstanceNotFound, self.compute_api.validate_vnc_console, - self.context, 5555, 'myhost', '5900') + self.context, 5555, 'myhost', '5900', 'novnc') self.compute.terminate_instance(self.context, instance['uuid']) def test_xvpvnc_vnc_console(self):
e98928cf7764Flush tokens on instance delete
5 files changed · +92 −2
nova/compute/api.py+10 −0 modified@@ -1554,11 +1554,21 @@ def get_vnc_console(self, context, instance, console_type): 'console_type': console_type, 'host': connect_info['host'], 'port': connect_info['port'], + 'instance_id': instance['id'], 'internal_access_path': connect_info['internal_access_path']}}) return {'url': connect_info['access_url']} + @wrap_check_policy + def validate_vnc_console(self, context, instance_id, host, port): + """Validate VNC Console for an instance.""" + instance = self.get(context, instance_id) + output = self._call_compute_message('get_vnc_console', + context, + instance) + return (port == output['port'] and host == output['host']) + @wrap_check_policy def get_console_output(self, context, instance, tail_length=None): """Get console output for an an instance."""
nova/compute/manager.py+4 −0 modified@@ -749,6 +749,10 @@ def _delete_instance(self, context, instance): terminated_at=utils.utcnow()) self.db.instance_destroy(context, instance_id) + if FLAGS.vnc_enabled: + rpc.cast(context, '%s' % FLAGS.consoleauth_topic, + {'method': 'delete_tokens_for_instance', + 'args': {'instance_id': instance['id']}}) self._notify_about_instance_usage(instance, "delete.end") @exception.wrap_exception(notifier=notifier, publisher_id=publisher_id())
nova/consoleauth/manager.py+30 −2 modified@@ -27,6 +27,8 @@ from nova import manager from nova.openstack.common import cfg from nova import utils +from nova import exception +from nova import compute LOG = logging.getLogger(__name__) @@ -49,6 +51,7 @@ class ConsoleAuthManager(manager.Manager): def __init__(self, scheduler_driver=None, *args, **kwargs): super(ConsoleAuthManager, self).__init__(*args, **kwargs) + self.compute_api = compute.API() self.tokens = {} utils.LoopingCall(self._delete_expired_tokens).start(1) @@ -64,18 +67,43 @@ def _delete_expired_tokens(self): del self.tokens[k] def authorize_console(self, context, token, console_type, host, port, - internal_access_path): + instance_id, internal_access_path): self.tokens[token] = {'token': token, 'console_type': console_type, 'host': host, 'port': port, + 'instance_id': instance_id, 'internal_access_path': internal_access_path, 'last_activity_at': time.time()} token_dict = self.tokens[token] + if instance_id is not None: + tokens = self.tokens[instance_id] + tokens.append(token) + self.tokens[instance_id] = tokens + LOG.audit(_("Received Token: %(token)s, %(token_dict)s)"), locals()) + def _validate_console(self, context, token): + console_valid = False + token_dict = self.tokens[token] + try: + console_valid = self.compute_api.validate_vnc_console(context, + token_dict['instance_id'], + token_dict['host'], + token_dict['port']) + except exception.InstanceNotFound: + pass + return console_valid + def check_token(self, context, token): token_valid = token in self.tokens LOG.audit(_("Checking Token: %(token)s, %(token_valid)s)"), locals()) - if token_valid: + if token_valid and _validate_console(token): return self.tokens[token] + + def delete_tokens_for_instance(self, context, instance_id): + for token in self.tokens[instance_id]: + token_dict = self.tokens[token] + token_dict['last_activity_at'] = 0 + self.tokens[token] = token_dict + del self.tokens[instance_id]
nova/tests/test_compute.py+41 −0 modified@@ -754,6 +754,47 @@ def test_novnc_vnc_console(self): self.assert_(console) self.compute.terminate_instance(self.context, instance['uuid']) + def test_validate_vnc_console(self): + """Check if a vnc console is really for the instance""" + def fake(*args, **kwargs): + vnc = {'host': 'myhost', 'port': '5900'} + return vnc + self.stubs.Set(self.compute_api, '_call_compute_message', fake) + instance = self._create_instance_full() + self.compute.run_instance(self.context, instance['id']) + + console_valid = self.compute_api.validate_vnc_console(self.context, + instance['id'], + 'myhost', + '5900') + self.assertTrue(console_valid) + self.compute.terminate_instance(self.context, instance['id']) + + def test_validate_vnc_console_wrong_port(self): + """Check if a vnc console is really for the instance""" + def fake(*args, **kwargs): + vnc = {'host': 'myhost', 'port': '5901'} + return vnc + self.stubs.Set(self.compute_api, '_call_compute_message', fake) + instance = self._create_instance_full() + self.compute.run_instance(self.context, instance['id']) + + console_valid = self.compute_api.validate_vnc_console(self.context, + instance['id'], + 'myhost', + '5900') + self.assertFalse(console_valid) + self.compute.terminate_instance(self.context, instance['id']) + + def test_validate_vnc_console_deleted_instance(self): + """Check if a vnc console is really for the instance""" + instance = self._create_instance_full() + self.compute.run_instance(self.context, instance['id']) + self.assertRaises(exception.InstanceNotFound, + self.compute_api.validate_vnc_console, + self.context, 5555, 'myhost', '5900') + self.compute.terminate_instance(self.context, instance['id']) + def test_xvpvnc_vnc_console(self): """Make sure we can a vnc console for an instance.""" instance = self._create_fake_instance()
nova/tests/test_consoleauth.py+7 −0 modified@@ -47,6 +47,13 @@ def test_tokens_expire(self): """Test that tokens expire correctly.""" token = 'mytok' self.flags(console_token_ttl=1) + + def fake_validate_console(*args, **kwargs): + return True + self.stubs.Set(self.manager, + "_validate_console", + fake_validate_console) + self.manager.authorize_console(self.context, token, 'novnc', '127.0.0.1', 'host', '') self.assertTrue(self.manager.check_token(self.context, token))
10 files changed · +200 −8
nova/common/memorycache.py+5 −0 modified@@ -62,3 +62,8 @@ def incr(self, key, delta=1): new_value = int(value) + delta self.cache[key] = (self.cache[key][0], str(new_value)) return new_value + + def delete(self, key, time=0): + """Deletes the value associated with a key.""" + if key in self.cache: + del self.cache[key]
nova/compute/api.py+2 −1 modified@@ -1853,7 +1853,8 @@ def get_vnc_console(self, context, instance, console_type): self.consoleauth_rpcapi.authorize_console(context, connect_info['token'], console_type, connect_info['host'], - connect_info['port'], connect_info['internal_access_path']) + connect_info['port'], connect_info['internal_access_path'], + instance["uuid"]) return {'url': connect_info['access_url']}
nova/compute/manager.py+12 −0 modified@@ -52,6 +52,7 @@ from nova.compute import task_states from nova.compute import utils as compute_utils from nova.compute import vm_states +from nova import consoleauth import nova.context from nova import exception from nova import flags @@ -235,6 +236,7 @@ def __init__(self, compute_driver=None, *args, **kwargs): self.compute_api = compute.API() self.compute_rpcapi = compute_rpcapi.ComputeAPI() self.scheduler_rpcapi = scheduler_rpcapi.SchedulerAPI() + self.consoleauth_rpcapi = consoleauth.rpcapi.ConsoleAuthAPI() super(ComputeManager, self).__init__(service_name="compute", *args, **kwargs) @@ -926,6 +928,10 @@ def _delete_instance(self, context, instance): self._notify_about_instance_usage(context, instance, "delete.end", system_metadata=system_meta) + if FLAGS.vnc_enabled: + self.consoleauth_rpcapi.delete_tokens_for_instance(context, + instance["uuid"]) + @exception.wrap_exception(notifier=notifier, publisher_id=publisher_id()) @wrap_instance_fault def terminate_instance(self, context, instance): @@ -1988,6 +1994,12 @@ def _attach_volume_boot(self, context, instance, volume, mountpoint): self.volume_api.attach(context, volume, instance_uuid, mountpoint) return connection_info + @exception.wrap_exception(notifier=notifier, publisher_id=publisher_id()) + @wrap_instance_fault + def validate_console_port(self, ctxt, instance, port, console_type): + console_info = self.driver.get_vnc_console(instance) + return console_info['port'] == port + @exception.wrap_exception(notifier=notifier, publisher_id=publisher_id()) @reverts_task_state @wrap_instance_fault
nova/compute/rpcapi.py+7 −0 modified@@ -259,6 +259,13 @@ def get_vnc_console(self, ctxt, instance, console_type): instance=instance_p, console_type=console_type), topic=_compute_topic(self.topic, ctxt, None, instance)) + def validate_console_port(self, ctxt, instance, port, console_type): + instance_p = jsonutils.to_primitive(instance) + return self.call(ctxt, self.make_msg('validate_console_port', + instance=instance_p, port=port, console_type=console_type), + topic=_compute_topic(self.topic, ctxt, + None, instance)) + def host_maintenance_mode(self, ctxt, host_param, mode, host): '''Set host maintenance mode
nova/consoleauth/manager.py+39 −2 modified@@ -20,6 +20,8 @@ import time +from nova.compute import rpcapi as compute_rpcapi +from nova.db import api as db from nova import flags from nova import manager from nova.openstack.common import cfg @@ -56,22 +58,57 @@ def __init__(self, scheduler_driver=None, *args, **kwargs): from nova.common import memorycache as memcache self.mc = memcache.Client(FLAGS.memcached_servers, debug=0) + self.compute_rpcapi = compute_rpcapi.ComputeAPI() + + def _get_tokens_for_instance(self, instance_uuid): + tokens_str = self.mc.get(instance_uuid.encode('UTF-8')) + if not tokens_str: + tokens = [] + else: + tokens = jsonutils.loads(tokens_str) + return tokens def authorize_console(self, context, token, console_type, host, port, - internal_access_path): + internal_access_path, instance_uuid=None): + token_dict = {'token': token, + 'instance_uuid': instance_uuid, 'console_type': console_type, 'host': host, 'port': port, 'internal_access_path': internal_access_path, 'last_activity_at': time.time()} data = jsonutils.dumps(token_dict) self.mc.set(token.encode('UTF-8'), data, FLAGS.console_token_ttl) + if instance_uuid is not None: + tokens = self._get_tokens_for_instance(instance_uuid) + tokens.append(token) + self.mc.set(instance_uuid.encode('UTF-8'), + jsonutils.dumps(tokens)) + LOG.audit(_("Received Token: %(token)s, %(token_dict)s)"), locals()) + def _validate_token(self, context, token): + instance_uuid = token['instance_uuid'] + if instance_uuid is None: + return False + instance = db.instance_get_by_uuid(context, instance_uuid) + return self.compute_rpcapi.validate_console_port(context, + instance, + token['port'], + token['console_type']) + def check_token(self, context, token): token_str = self.mc.get(token.encode('UTF-8')) token_valid = (token_str is not None) LOG.audit(_("Checking Token: %(token)s, %(token_valid)s)"), locals()) if token_valid: - return jsonutils.loads(token_str) + token = jsonutils.loads(token_str) + if self._validate_token(context, token): + return token + + def delete_tokens_for_instance(self, context, instance_uuid): + tokens = self._get_tokens_for_instance(instance_uuid) + for token in tokens: + self.mc.delete(token) + self.mc.delete(instance_uuid.encode('UTF-8'))
nova/consoleauth/rpcapi.py+8 −2 modified@@ -49,14 +49,20 @@ def __init__(self): default_version=self.BASE_RPC_API_VERSION) def authorize_console(self, ctxt, token, console_type, host, port, - internal_access_path): + internal_access_path, instance_uuid=None): # The remote side doesn't return anything, but we want to block # until it completes. return self.call(ctxt, self.make_msg('authorize_console', token=token, console_type=console_type, host=host, port=port, - internal_access_path=internal_access_path)) + internal_access_path=internal_access_path, + instance_uuid=instance_uuid)) def check_token(self, ctxt, token): return self.call(ctxt, self.make_msg('check_token', token=token)) + + def delete_tokens_for_instance(self, ctxt, instance_uuid): + return self.call(ctxt, + self.make_msg('delete_tokens_for_instance', + instance_uuid=instance_uuid))
nova/tests/compute/test_compute.py+51 −1 modified@@ -1372,6 +1372,24 @@ def fake_cleanup_volumes(context, instance): self.compute._delete_instance(self.context, instance=jsonutils.to_primitive(instance)) + def test_delete_instance_deletes_console_auth_tokens(self): + instance = self._create_fake_instance() + self.flags(vnc_enabled=True) + + self.tokens_deleted = False + + def fake_delete_tokens(*args, **kwargs): + self.tokens_deleted = True + + cauth_rpcapi = self.compute.consoleauth_rpcapi + self.stubs.Set(cauth_rpcapi, 'delete_tokens_for_instance', + fake_delete_tokens) + + self.compute._delete_instance(self.context, + instance=jsonutils.to_primitive(instance)) + + self.assertTrue(self.tokens_deleted) + def test_instance_termination_exception_sets_error(self): """Test that we handle InstanceTerminationFailure which is propagated up from the underlying driver. @@ -4505,7 +4523,9 @@ def test_vnc_console(self): 'console_type': fake_console_type, 'host': 'fake_console_host', 'port': 'fake_console_port', - 'internal_access_path': 'fake_access_path'} + 'internal_access_path': 'fake_access_path', + 'instance_uuid': fake_instance["uuid"]} + fake_connect_info2 = copy.deepcopy(fake_connect_info) fake_connect_info2['access_url'] = 'fake_console_url' @@ -4539,6 +4559,36 @@ def test_get_vnc_console_no_host(self): db.instance_destroy(self.context, instance['uuid']) + def test_validate_console_port(self): + self.flags(vnc_enabled=True) + instance = jsonutils.to_primitive(self._create_fake_instance()) + + def fake_driver_get_console(*args, **kwargs): + return {'host': "fake_host", 'port': "5900", + 'internal_access_path': None} + self.stubs.Set(self.compute.driver, "get_vnc_console", + fake_driver_get_console) + + self.assertTrue(self.compute.validate_console_port(self.context, + instance, + "5900", + "novnc")) + + def test_validate_console_port_wrong_port(self): + self.flags(vnc_enabled=True) + instance = jsonutils.to_primitive(self._create_fake_instance()) + + def fake_driver_get_console(*args, **kwargs): + return {'host': "fake_host", 'port': "5900", + 'internal_access_path': None} + self.stubs.Set(self.compute.driver, "get_vnc_console", + fake_driver_get_console) + + self.assertFalse(self.compute.validate_console_port(self.context, + instance, + "wrongport", + "novnc")) + def test_console_output(self): fake_instance = {'uuid': 'fake_uuid', 'host': 'fake_compute_host'}
nova/tests/compute/test_rpcapi.py+5 −0 modified@@ -168,6 +168,11 @@ def test_get_vnc_console(self): self._test_compute_api('get_vnc_console', 'call', instance=self.fake_instance, console_type='type') + def test_validate_console_port(self): + self._test_compute_api('validate_console_port', 'call', + instance=self.fake_instance, port="5900", + console_type="novnc") + def test_host_maintenance_mode(self): self._test_compute_api('host_maintenance_mode', 'call', host_param='param', mode='mode', host='host')
nova/tests/consoleauth/test_consoleauth.py+66 −1 modified@@ -45,8 +45,73 @@ def test_tokens_expire(self): """Test that tokens expire correctly.""" token = 'mytok' self.flags(console_token_ttl=1) + + def fake_validate_token(*args, **kwargs): + return True + self.stubs.Set(self.manager, + "_validate_token", + fake_validate_token) + self.manager.authorize_console(self.context, token, 'novnc', - '127.0.0.1', 'host', '') + '127.0.0.1', '8080', 'host', "1234") self.assertTrue(self.manager.check_token(self.context, token)) time.sleep(1.1) self.assertFalse(self.manager.check_token(self.context, token)) + + def test_multiple_tokens_for_instance(self): + tokens = ["token" + str(i) for i in xrange(10)] + instance = "12345" + + def fake_validate_token(*args, **kwargs): + return True + + self.stubs.Set(self.manager, "_validate_token", + fake_validate_token) + for token in tokens: + self.manager.authorize_console(self.context, token, 'novnc', + '127.0.0.1', '8080', 'host', + instance) + + for token in tokens: + self.assertTrue(self.manager.check_token(self.context, token)) + + def test_delete_tokens_for_instance(self): + instance = "12345" + tokens = ["token" + str(i) for i in xrange(10)] + + def fake_validate_token(*args, **kwargs): + return True + self.stubs.Set(self.manager, "_validate_token", + fake_validate_token) + + for token in tokens: + self.manager.authorize_console(self.context, token, 'novnc', + '127.0.0.1', '8080', 'host', + instance) + self.manager.delete_tokens_for_instance(self.context, instance) + stored_tokens = self.manager._get_tokens_for_instance(instance) + + self.assertEqual(len(stored_tokens), 0) + + for token in tokens: + self.assertFalse(self.manager.check_token(self.context, token)) + + def test_wrong_token_has_port(self): + token = 'mytok' + + def fake_validate_token(*args, **kwargs): + return False + + self.stubs.Set(self.manager, "_validate_token", + fake_validate_token) + + self.manager.authorize_console(self.context, token, 'novnc', + '127.0.0.1', '8080', 'host', + instance_uuid='instance') + self.assertFalse(self.manager.check_token(self.context, token)) + + def test_console_no_instance_uuid(self): + self.manager.authorize_console(self.context, "token", 'novnc', + '127.0.0.1', '8080', 'host', + instance_uuid=None) + self.assertFalse(self.manager.check_token(self.context, "token"))
nova/tests/consoleauth/test_rpcapi.py+5 −1 modified@@ -68,7 +68,11 @@ def _fake_call(_ctxt, _topic, _msg, _timeout): def test_authorize_console(self): self._test_consoleauth_api('authorize_console', token='token', console_type='ctype', host='h', port='p', - internal_access_path='iap') + internal_access_path='iap', instance_uuid="1234") def test_check_token(self): self._test_consoleauth_api('check_token', token='t') + + def test_delete_tokens_for_instnace(self): + self._test_consoleauth_api('delete_tokens_for_instance', + instance_uuid="instance")
ad94a9020219Flush tokens on instance delete
11 files changed · +247 −19
nova/common/memorycache.py+5 −0 modified@@ -83,3 +83,8 @@ def incr(self, key, delta=1): new_value = int(value) + delta self.cache[key] = (self.cache[key][0], str(new_value)) return new_value + + def delete(self, key, time=0): + """Deletes the value associated with a key.""" + if key in self.cache: + del self.cache[key]
nova/compute/api.py+7 −5 modified@@ -2189,8 +2189,9 @@ def get_vnc_console(self, context, instance, console_type): instance=instance, console_type=console_type) self.consoleauth_rpcapi.authorize_console(context, - connect_info['token'], console_type, connect_info['host'], - connect_info['port'], connect_info['internal_access_path']) + connect_info['token'], console_type, + connect_info['host'], connect_info['port'], + connect_info['internal_access_path'], instance['uuid']) return {'url': connect_info['access_url']} @@ -2207,10 +2208,11 @@ def get_spice_console(self, context, instance, console_type): """Get a url to an instance Console.""" connect_info = self.compute_rpcapi.get_spice_console(context, instance=instance, console_type=console_type) - + print connect_info self.consoleauth_rpcapi.authorize_console(context, - connect_info['token'], console_type, connect_info['host'], - connect_info['port'], connect_info['internal_access_path']) + connect_info['token'], console_type, + connect_info['host'], connect_info['port'], + connect_info['internal_access_path'], instance['uuid']) return {'url': connect_info['access_url']}
nova/compute/cells_api.py+4 −2 modified@@ -465,7 +465,8 @@ def get_vnc_console(self, context, instance, console_type): self.consoleauth_rpcapi.authorize_console(context, connect_info['token'], console_type, connect_info['host'], - connect_info['port'], connect_info['internal_access_path']) + connect_info['port'], connect_info['internal_access_path'], + instance_uuid=instance['uuid']) return {'url': connect_info['access_url']} @wrap_check_policy @@ -480,7 +481,8 @@ def get_spice_console(self, context, instance, console_type): self.consoleauth_rpcapi.authorize_console(context, connect_info['token'], console_type, connect_info['host'], - connect_info['port'], connect_info['internal_access_path']) + connect_info['port'], connect_info['internal_access_path'], + instance_uuid=instance['uuid']) return {'url': connect_info['access_url']} @validate_cell
nova/compute/manager.py+18 −1 modified@@ -50,6 +50,7 @@ from nova.compute import utils as compute_utils from nova.compute import vm_states from nova import conductor +from nova import consoleauth import nova.context from nova import exception from nova import hooks @@ -317,7 +318,7 @@ def agent_build_get_by_triple(self, context, hypervisor, os, architecture): class ComputeManager(manager.SchedulerDependentManager): """Manages the running instances from creation to destruction.""" - RPC_API_VERSION = '2.25' + RPC_API_VERSION = '2.26' def __init__(self, compute_driver=None, *args, **kwargs): """Load configuration options and connect to the hypervisor.""" @@ -335,6 +336,8 @@ def __init__(self, compute_driver=None, *args, **kwargs): self.conductor_api = conductor.API() self.is_quantum_security_groups = ( openstack_driver.is_quantum_security_groups()) + self.consoleauth_rpcapi = consoleauth.rpcapi.ConsoleAuthAPI() + super(ComputeManager, self).__init__(service_name="compute", *args, **kwargs) @@ -1223,6 +1226,10 @@ def _delete_instance(self, context, instance, bdms): self._notify_about_instance_usage(context, instance, "delete.end", system_metadata=system_meta) + if CONF.vnc_enabled or CONF.spice.enabled: + self.consoleauth_rpcapi.delete_tokens_for_instance(context, + instance['uuid']) + @exception.wrap_exception(notifier=notifier, publisher_id=publisher_id()) @wrap_instance_event @wrap_instance_fault @@ -2555,6 +2562,16 @@ def get_spice_console(self, context, console_type, instance): return connect_info + @exception.wrap_exception(notifier=notifier, publisher_id=publisher_id()) + @wrap_instance_fault + def validate_console_port(self, ctxt, instance, port, console_type): + if console_type == "spice-html5": + console_info = self.driver.get_spice_console(instance) + else: + console_info = self.driver.get_vnc_console(instance) + + return console_info['port'] == port + def _attach_volume_boot(self, context, instance, volume, mountpoint): """Attach a volume to an instance at boot time. So actual attach is done by instance creation"""
nova/compute/rpcapi.py+10 −0 modified@@ -161,6 +161,8 @@ class ComputeAPI(nova.openstack.common.rpc.proxy.RpcProxy): 2.23 - Remove network_info from reboot_instance 2.24 - Added get_spice_console method 2.25 - Add attach_interface() and detach_interface() + 2.26 - Add validate_console_token to ensure the service connects to + vnc on the correct port ''' # @@ -321,6 +323,14 @@ def get_spice_console(self, ctxt, instance, console_type): topic=_compute_topic(self.topic, ctxt, None, instance), version='2.24') + def validate_console_port(self, ctxt, instance, port, console_type): + instance_p = jsonutils.to_primitive(instance) + return self.call(ctxt, self.make_msg('validate_console_port', + instance=instance_p, port=port, console_type=console_type), + topic=_compute_topic(self.topic, ctxt, + None, instance), + version='2.26') + def host_maintenance_mode(self, ctxt, host_param, mode, host): '''Set host maintenance mode
nova/consoleauth/manager.py+42 −3 modified@@ -23,6 +23,8 @@ from oslo.config import cfg from nova.common import memorycache +from nova.compute import rpcapi as compute_rpcapi +from nova.conductor import api as conductor_api from nova import manager from nova.openstack.common import jsonutils from nova.openstack.common import log as logging @@ -46,30 +48,67 @@ class ConsoleAuthManager(manager.Manager): """Manages token based authentication.""" - RPC_API_VERSION = '1.1' + RPC_API_VERSION = '1.2' def __init__(self, scheduler_driver=None, *args, **kwargs): super(ConsoleAuthManager, self).__init__(*args, **kwargs) self.mc = memorycache.get_client() + self.compute_rpcapi = compute_rpcapi.ComputeAPI() + self.conductor_api = conductor_api.API() + + def _get_tokens_for_instance(self, instance_uuid): + tokens_str = self.mc.get(instance_uuid.encode('UTF-8')) + if not tokens_str: + tokens = [] + else: + tokens = jsonutils.loads(tokens_str) + return tokens def authorize_console(self, context, token, console_type, host, port, - internal_access_path): + internal_access_path, instance_uuid=None): + token_dict = {'token': token, + 'instance_uuid': instance_uuid, 'console_type': console_type, 'host': host, 'port': port, 'internal_access_path': internal_access_path, 'last_activity_at': time.time()} data = jsonutils.dumps(token_dict) self.mc.set(token.encode('UTF-8'), data, CONF.console_token_ttl) + if instance_uuid is not None: + tokens = self._get_tokens_for_instance(instance_uuid) + tokens.append(token) + self.mc.set(instance_uuid.encode('UTF-8'), + jsonutils.dumps(tokens)) + LOG.audit(_("Received Token: %(token)s, %(token_dict)s)"), locals()) + def _validate_token(self, context, token): + instance_uuid = token['instance_uuid'] + if instance_uuid is None: + return False + instance = self.conductor_api.instance_get_by_uuid(context, + instance_uuid) + return self.compute_rpcapi.validate_console_port(context, + instance, + token['port'], + token['console_type']) + def check_token(self, context, token): token_str = self.mc.get(token.encode('UTF-8')) token_valid = (token_str is not None) LOG.audit(_("Checking Token: %(token)s, %(token_valid)s)"), locals()) if token_valid: - return jsonutils.loads(token_str) + token = jsonutils.loads(token_str) + if self._validate_token(context, token): + return token + + def delete_tokens_for_instance(self, context, instance_uuid): + tokens = self._get_tokens_for_instance(instance_uuid) + for token in tokens: + self.mc.delete(token) + self.mc.delete(instance_uuid.encode('UTF-8')) def get_backdoor_port(self, context): return self.backdoor_port
nova/consoleauth/rpcapi.py+12 −2 modified@@ -32,6 +32,8 @@ class ConsoleAuthAPI(nova.openstack.common.rpc.proxy.RpcProxy): 1.0 - Initial version. 1.1 - Added get_backdoor_port() + 1.2 - Added instance_uuid to authorize_console, and + delete_tokens_for_instance ''' # @@ -50,18 +52,26 @@ def __init__(self): default_version=self.BASE_RPC_API_VERSION) def authorize_console(self, ctxt, token, console_type, host, port, - internal_access_path): + internal_access_path, instance_uuid=None): # The remote side doesn't return anything, but we want to block # until it completes. return self.call(ctxt, self.make_msg('authorize_console', token=token, console_type=console_type, host=host, port=port, - internal_access_path=internal_access_path)) + internal_access_path=internal_access_path, + instance_uuid=instance_uuid), + version="1.2") def check_token(self, ctxt, token): return self.call(ctxt, self.make_msg('check_token', token=token)) + def delete_tokens_for_instance(self, ctxt, instance_uuid): + return self.call(ctxt, + self.make_msg('delete_tokens_for_instance', + instance_uuid=instance_uuid), + version="1.2") + def get_backdoor_port(self, ctxt, host): return self.call(ctxt, self.make_msg('get_backdoor_port'), version='1.1')
nova/tests/compute/test_compute.py+73 −4 modified@@ -1420,6 +1420,54 @@ def test_novnc_vnc_console(self): self.compute.terminate_instance(self.context, instance=instance) + def test_validate_console_port_vnc(self): + self.flags(vnc_enabled=True) + self.flags(enabled=True, group='spice') + instance = jsonutils.to_primitive(self._create_fake_instance()) + + def fake_driver_get_console(*args, **kwargs): + return {'host': "fake_host", 'port': "5900", + 'internal_access_path': None} + self.stubs.Set(self.compute.driver, "get_vnc_console", + fake_driver_get_console) + + self.assertTrue(self.compute.validate_console_port(self.context, + instance, + "5900", + "novnc")) + + def test_validate_console_port_spice(self): + self.flags(vnc_enabled=True) + self.flags(enabled=True, group='spice') + instance = jsonutils.to_primitive(self._create_fake_instance()) + + def fake_driver_get_console(*args, **kwargs): + return {'host': "fake_host", 'port': "5900", + 'internal_access_path': None} + self.stubs.Set(self.compute.driver, "get_spice_console", + fake_driver_get_console) + + self.assertTrue(self.compute.validate_console_port(self.context, + instance, + "5900", + "spice-html5")) + + def test_validate_console_port_wrong_port(self): + self.flags(vnc_enabled=True) + self.flags(enabled=True, group='spice') + instance = jsonutils.to_primitive(self._create_fake_instance()) + + def fake_driver_get_console(*args, **kwargs): + return {'host': "fake_host", 'port': "5900", + 'internal_access_path': None} + self.stubs.Set(self.compute.driver, "get_vnc_console", + fake_driver_get_console) + + self.assertFalse(self.compute.validate_console_port(self.context, + instance, + "wrongport", + "spice-html5")) + def test_xvpvnc_vnc_console(self): # Make sure we can a vnc console for an instance. self.flags(vnc_enabled=True) @@ -1715,6 +1763,25 @@ def fake_cleanup_volumes(context, instance): instance=jsonutils.to_primitive(instance), bdms={}) + def test_delete_instance_deletes_console_auth_tokens(self): + instance = self._create_fake_instance() + self.flags(vnc_enabled=True) + + self.tokens_deleted = False + + def fake_delete_tokens(*args, **kwargs): + self.tokens_deleted = True + + cauth_rpcapi = self.compute.consoleauth_rpcapi + self.stubs.Set(cauth_rpcapi, 'delete_tokens_for_instance', + fake_delete_tokens) + + self.compute._delete_instance(self.context, + instance=jsonutils.to_primitive(instance), + bdms={}) + + self.assertTrue(self.tokens_deleted) + def test_instance_termination_exception_sets_error(self): """Test that we handle InstanceTerminationFailure which is propagated up from the underlying driver. @@ -5735,7 +5802,8 @@ def test_vnc_console(self): 'console_type': fake_console_type, 'host': 'fake_console_host', 'port': 'fake_console_port', - 'internal_access_path': 'fake_access_path'} + 'internal_access_path': 'fake_access_path', + 'instance_uuid': fake_instance['uuid']} fake_connect_info2 = copy.deepcopy(fake_connect_info) fake_connect_info2['access_url'] = 'fake_console_url' @@ -5747,7 +5815,7 @@ def test_vnc_console(self): 'version': compute_rpcapi.ComputeAPI.BASE_RPC_API_VERSION} rpc_msg2 = {'method': 'authorize_console', 'args': fake_connect_info, - 'version': '1.0'} + 'version': '1.2'} rpc.call(self.context, 'compute.%s' % fake_instance['host'], rpc_msg1, None).AndReturn(fake_connect_info2) @@ -5779,7 +5847,8 @@ def test_spice_console(self): 'console_type': fake_console_type, 'host': 'fake_console_host', 'port': 'fake_console_port', - 'internal_access_path': 'fake_access_path'} + 'internal_access_path': 'fake_access_path', + 'instance_uuid': fake_instance['uuid']} fake_connect_info2 = copy.deepcopy(fake_connect_info) fake_connect_info2['access_url'] = 'fake_console_url' @@ -5791,7 +5860,7 @@ def test_spice_console(self): 'version': '2.24'} rpc_msg2 = {'method': 'authorize_console', 'args': fake_connect_info, - 'version': '1.0'} + 'version': '1.2'} rpc.call(self.context, 'compute.%s' % fake_instance['host'], rpc_msg1, None).AndReturn(fake_connect_info2)
nova/tests/compute/test_rpcapi.py+6 −0 modified@@ -171,6 +171,12 @@ def test_get_spice_console(self): instance=self.fake_instance, console_type='type', version='2.24') + def test_validate_console_port(self): + self._test_compute_api('validate_console_port', 'call', + instance=self.fake_instance, port="5900", + console_type="novnc", + version="2.26") + def test_host_maintenance_mode(self): self._test_compute_api('host_maintenance_mode', 'call', host_param='param', mode='mode', host='host')
nova/tests/consoleauth/test_consoleauth.py+63 −1 modified@@ -42,12 +42,74 @@ def test_tokens_expire(self): self.useFixture(test.TimeOverride()) token = 'mytok' self.flags(console_token_ttl=1) + + def fake_validate_console_port(*args, **kwargs): + return True + self.stubs.Set(self.manager.compute_rpcapi, + "validate_console_port", + fake_validate_console_port) + self.manager.authorize_console(self.context, token, 'novnc', - '127.0.0.1', 'host', '') + '127.0.0.1', '8080', 'host', + 'instance') self.assertTrue(self.manager.check_token(self.context, token)) timeutils.advance_time_seconds(1) self.assertFalse(self.manager.check_token(self.context, token)) + def test_multiple_tokens_for_instance(self): + tokens = ["token" + str(i) for i in xrange(10)] + instance = "12345" + + def fake_validate_console_port(*args, **kwargs): + return True + + self.stubs.Set(self.manager.compute_rpcapi, + "validate_console_port", + fake_validate_console_port) + for token in tokens: + self.manager.authorize_console(self.context, token, 'novnc', + '127.0.0.1', '8080', 'host', + instance) + + for token in tokens: + self.assertTrue(self.manager.check_token(self.context, token)) + + def test_delete_tokens_for_instance(self): + instance = "12345" + tokens = ["token" + str(i) for i in xrange(10)] + for token in tokens: + self.manager.authorize_console(self.context, token, 'novnc', + '127.0.0.1', '8080', 'host', + instance) + self.manager.delete_tokens_for_instance(self.context, instance) + stored_tokens = self.manager._get_tokens_for_instance(instance) + + self.assertEqual(len(stored_tokens), 0) + + for token in tokens: + self.assertFalse(self.manager.check_token(self.context, token)) + + def test_wrong_token_has_port(self): + token = 'mytok' + + def fake_validate_console_port(*args, **kwargs): + return False + + self.stubs.Set(self.manager.compute_rpcapi, + "validate_console_port", + fake_validate_console_port) + + self.manager.authorize_console(self.context, token, 'novnc', + '127.0.0.1', '8080', 'host', + instance_uuid='instance') + self.assertFalse(self.manager.check_token(self.context, token)) + + def test_console_no_instance_uuid(self): + self.manager.authorize_console(self.context, "token", 'novnc', + '127.0.0.1', '8080', 'host', + instance_uuid=None) + self.assertFalse(self.manager.check_token(self.context, "token")) + def test_get_backdoor_port(self): self.manager.backdoor_port = 59697 port = self.manager.get_backdoor_port(self.context)
nova/tests/consoleauth/test_rpcapi.py+7 −1 modified@@ -65,11 +65,17 @@ def _fake_call(_ctxt, _topic, _msg, _timeout): def test_authorize_console(self): self._test_consoleauth_api('authorize_console', token='token', console_type='ctype', host='h', port='p', - internal_access_path='iap') + internal_access_path='iap', instance_uuid="instance", + version="1.2") def test_check_token(self): self._test_consoleauth_api('check_token', token='t') + def test_delete_tokens_for_instnace(self): + self._test_consoleauth_api('delete_tokens_for_instance', + instance_uuid="instance", + version='1.2') + def test_get_backdoor_port(self): self._test_consoleauth_api('get_backdoor_port', host='fake_host', version='1.1')
3b0f4cf6bea3Flush tokens on instance delete
11 files changed · +247 −19
nova/common/memorycache.py+5 −0 modified@@ -83,3 +83,8 @@ def incr(self, key, delta=1): new_value = int(value) + delta self.cache[key] = (self.cache[key][0], str(new_value)) return new_value + + def delete(self, key, time=0): + """Deletes the value associated with a key.""" + if key in self.cache: + del self.cache[key]
nova/compute/api.py+7 −5 modified@@ -2189,8 +2189,9 @@ def get_vnc_console(self, context, instance, console_type): instance=instance, console_type=console_type) self.consoleauth_rpcapi.authorize_console(context, - connect_info['token'], console_type, connect_info['host'], - connect_info['port'], connect_info['internal_access_path']) + connect_info['token'], console_type, + connect_info['host'], connect_info['port'], + connect_info['internal_access_path'], instance['uuid']) return {'url': connect_info['access_url']} @@ -2207,10 +2208,11 @@ def get_spice_console(self, context, instance, console_type): """Get a url to an instance Console.""" connect_info = self.compute_rpcapi.get_spice_console(context, instance=instance, console_type=console_type) - + print connect_info self.consoleauth_rpcapi.authorize_console(context, - connect_info['token'], console_type, connect_info['host'], - connect_info['port'], connect_info['internal_access_path']) + connect_info['token'], console_type, + connect_info['host'], connect_info['port'], + connect_info['internal_access_path'], instance['uuid']) return {'url': connect_info['access_url']}
nova/compute/cells_api.py+4 −2 modified@@ -465,7 +465,8 @@ def get_vnc_console(self, context, instance, console_type): self.consoleauth_rpcapi.authorize_console(context, connect_info['token'], console_type, connect_info['host'], - connect_info['port'], connect_info['internal_access_path']) + connect_info['port'], connect_info['internal_access_path'], + instance_uuid=instance['uuid']) return {'url': connect_info['access_url']} @wrap_check_policy @@ -480,7 +481,8 @@ def get_spice_console(self, context, instance, console_type): self.consoleauth_rpcapi.authorize_console(context, connect_info['token'], console_type, connect_info['host'], - connect_info['port'], connect_info['internal_access_path']) + connect_info['port'], connect_info['internal_access_path'], + instance_uuid=instance['uuid']) return {'url': connect_info['access_url']} @validate_cell
nova/compute/manager.py+18 −1 modified@@ -50,6 +50,7 @@ from nova.compute import utils as compute_utils from nova.compute import vm_states from nova import conductor +from nova import consoleauth import nova.context from nova import exception from nova import hooks @@ -317,7 +318,7 @@ def agent_build_get_by_triple(self, context, hypervisor, os, architecture): class ComputeManager(manager.SchedulerDependentManager): """Manages the running instances from creation to destruction.""" - RPC_API_VERSION = '2.25' + RPC_API_VERSION = '2.26' def __init__(self, compute_driver=None, *args, **kwargs): """Load configuration options and connect to the hypervisor.""" @@ -335,6 +336,8 @@ def __init__(self, compute_driver=None, *args, **kwargs): self.conductor_api = conductor.API() self.is_quantum_security_groups = ( openstack_driver.is_quantum_security_groups()) + self.consoleauth_rpcapi = consoleauth.rpcapi.ConsoleAuthAPI() + super(ComputeManager, self).__init__(service_name="compute", *args, **kwargs) @@ -1223,6 +1226,10 @@ def _delete_instance(self, context, instance, bdms): self._notify_about_instance_usage(context, instance, "delete.end", system_metadata=system_meta) + if CONF.vnc_enabled or CONF.spice.enabled: + self.consoleauth_rpcapi.delete_tokens_for_instance(context, + instance['uuid']) + @exception.wrap_exception(notifier=notifier, publisher_id=publisher_id()) @wrap_instance_event @wrap_instance_fault @@ -2555,6 +2562,16 @@ def get_spice_console(self, context, console_type, instance): return connect_info + @exception.wrap_exception(notifier=notifier, publisher_id=publisher_id()) + @wrap_instance_fault + def validate_console_port(self, ctxt, instance, port, console_type): + if console_type == "spice-html5": + console_info = self.driver.get_spice_console(instance) + else: + console_info = self.driver.get_vnc_console(instance) + + return console_info['port'] == port + def _attach_volume_boot(self, context, instance, volume, mountpoint): """Attach a volume to an instance at boot time. So actual attach is done by instance creation"""
nova/compute/rpcapi.py+10 −0 modified@@ -161,6 +161,8 @@ class ComputeAPI(nova.openstack.common.rpc.proxy.RpcProxy): 2.23 - Remove network_info from reboot_instance 2.24 - Added get_spice_console method 2.25 - Add attach_interface() and detach_interface() + 2.26 - Add validate_console_token to ensure the service connects to + vnc on the correct port ''' # @@ -321,6 +323,14 @@ def get_spice_console(self, ctxt, instance, console_type): topic=_compute_topic(self.topic, ctxt, None, instance), version='2.24') + def validate_console_port(self, ctxt, instance, port, console_type): + instance_p = jsonutils.to_primitive(instance) + return self.call(ctxt, self.make_msg('validate_console_port', + instance=instance_p, port=port, console_type=console_type), + topic=_compute_topic(self.topic, ctxt, + None, instance), + version='2.26') + def host_maintenance_mode(self, ctxt, host_param, mode, host): '''Set host maintenance mode
nova/consoleauth/manager.py+42 −3 modified@@ -23,6 +23,8 @@ from oslo.config import cfg from nova.common import memorycache +from nova.compute import rpcapi as compute_rpcapi +from nova.conductor import api as conductor_api from nova import manager from nova.openstack.common import jsonutils from nova.openstack.common import log as logging @@ -46,30 +48,67 @@ class ConsoleAuthManager(manager.Manager): """Manages token based authentication.""" - RPC_API_VERSION = '1.1' + RPC_API_VERSION = '1.2' def __init__(self, scheduler_driver=None, *args, **kwargs): super(ConsoleAuthManager, self).__init__(*args, **kwargs) self.mc = memorycache.get_client() + self.compute_rpcapi = compute_rpcapi.ComputeAPI() + self.conductor_api = conductor_api.API() + + def _get_tokens_for_instance(self, instance_uuid): + tokens_str = self.mc.get(instance_uuid.encode('UTF-8')) + if not tokens_str: + tokens = [] + else: + tokens = jsonutils.loads(tokens_str) + return tokens def authorize_console(self, context, token, console_type, host, port, - internal_access_path): + internal_access_path, instance_uuid=None): + token_dict = {'token': token, + 'instance_uuid': instance_uuid, 'console_type': console_type, 'host': host, 'port': port, 'internal_access_path': internal_access_path, 'last_activity_at': time.time()} data = jsonutils.dumps(token_dict) self.mc.set(token.encode('UTF-8'), data, CONF.console_token_ttl) + if instance_uuid is not None: + tokens = self._get_tokens_for_instance(instance_uuid) + tokens.append(token) + self.mc.set(instance_uuid.encode('UTF-8'), + jsonutils.dumps(tokens)) + LOG.audit(_("Received Token: %(token)s, %(token_dict)s)"), locals()) + def _validate_token(self, context, token): + instance_uuid = token['instance_uuid'] + if instance_uuid is None: + return False + instance = self.conductor_api.instance_get_by_uuid(context, + instance_uuid) + return self.compute_rpcapi.validate_console_port(context, + instance, + token['port'], + token['console_type']) + def check_token(self, context, token): token_str = self.mc.get(token.encode('UTF-8')) token_valid = (token_str is not None) LOG.audit(_("Checking Token: %(token)s, %(token_valid)s)"), locals()) if token_valid: - return jsonutils.loads(token_str) + token = jsonutils.loads(token_str) + if self._validate_token(context, token): + return token + + def delete_tokens_for_instance(self, context, instance_uuid): + tokens = self._get_tokens_for_instance(instance_uuid) + for token in tokens: + self.mc.delete(token) + self.mc.delete(instance_uuid.encode('UTF-8')) def get_backdoor_port(self, context): return self.backdoor_port
nova/consoleauth/rpcapi.py+12 −2 modified@@ -32,6 +32,8 @@ class ConsoleAuthAPI(nova.openstack.common.rpc.proxy.RpcProxy): 1.0 - Initial version. 1.1 - Added get_backdoor_port() + 1.2 - Added instance_uuid to authorize_console, and + delete_tokens_for_instance ''' # @@ -50,18 +52,26 @@ def __init__(self): default_version=self.BASE_RPC_API_VERSION) def authorize_console(self, ctxt, token, console_type, host, port, - internal_access_path): + internal_access_path, instance_uuid=None): # The remote side doesn't return anything, but we want to block # until it completes. return self.call(ctxt, self.make_msg('authorize_console', token=token, console_type=console_type, host=host, port=port, - internal_access_path=internal_access_path)) + internal_access_path=internal_access_path, + instance_uuid=instance_uuid), + version="1.2") def check_token(self, ctxt, token): return self.call(ctxt, self.make_msg('check_token', token=token)) + def delete_tokens_for_instance(self, ctxt, instance_uuid): + return self.call(ctxt, + self.make_msg('delete_tokens_for_instance', + instance_uuid=instance_uuid), + version="1.2") + def get_backdoor_port(self, ctxt, host): return self.call(ctxt, self.make_msg('get_backdoor_port'), version='1.1')
nova/tests/compute/test_compute.py+73 −4 modified@@ -1420,6 +1420,54 @@ def test_novnc_vnc_console(self): self.compute.terminate_instance(self.context, instance=instance) + def test_validate_console_port_vnc(self): + self.flags(vnc_enabled=True) + self.flags(enabled=True, group='spice') + instance = jsonutils.to_primitive(self._create_fake_instance()) + + def fake_driver_get_console(*args, **kwargs): + return {'host': "fake_host", 'port': "5900", + 'internal_access_path': None} + self.stubs.Set(self.compute.driver, "get_vnc_console", + fake_driver_get_console) + + self.assertTrue(self.compute.validate_console_port(self.context, + instance, + "5900", + "novnc")) + + def test_validate_console_port_spice(self): + self.flags(vnc_enabled=True) + self.flags(enabled=True, group='spice') + instance = jsonutils.to_primitive(self._create_fake_instance()) + + def fake_driver_get_console(*args, **kwargs): + return {'host': "fake_host", 'port': "5900", + 'internal_access_path': None} + self.stubs.Set(self.compute.driver, "get_spice_console", + fake_driver_get_console) + + self.assertTrue(self.compute.validate_console_port(self.context, + instance, + "5900", + "spice-html5")) + + def test_validate_console_port_wrong_port(self): + self.flags(vnc_enabled=True) + self.flags(enabled=True, group='spice') + instance = jsonutils.to_primitive(self._create_fake_instance()) + + def fake_driver_get_console(*args, **kwargs): + return {'host': "fake_host", 'port': "5900", + 'internal_access_path': None} + self.stubs.Set(self.compute.driver, "get_vnc_console", + fake_driver_get_console) + + self.assertFalse(self.compute.validate_console_port(self.context, + instance, + "wrongport", + "spice-html5")) + def test_xvpvnc_vnc_console(self): # Make sure we can a vnc console for an instance. self.flags(vnc_enabled=True) @@ -1715,6 +1763,25 @@ def fake_cleanup_volumes(context, instance): instance=jsonutils.to_primitive(instance), bdms={}) + def test_delete_instance_deletes_console_auth_tokens(self): + instance = self._create_fake_instance() + self.flags(vnc_enabled=True) + + self.tokens_deleted = False + + def fake_delete_tokens(*args, **kwargs): + self.tokens_deleted = True + + cauth_rpcapi = self.compute.consoleauth_rpcapi + self.stubs.Set(cauth_rpcapi, 'delete_tokens_for_instance', + fake_delete_tokens) + + self.compute._delete_instance(self.context, + instance=jsonutils.to_primitive(instance), + bdms={}) + + self.assertTrue(self.tokens_deleted) + def test_instance_termination_exception_sets_error(self): """Test that we handle InstanceTerminationFailure which is propagated up from the underlying driver. @@ -5735,7 +5802,8 @@ def test_vnc_console(self): 'console_type': fake_console_type, 'host': 'fake_console_host', 'port': 'fake_console_port', - 'internal_access_path': 'fake_access_path'} + 'internal_access_path': 'fake_access_path', + 'instance_uuid': fake_instance['uuid']} fake_connect_info2 = copy.deepcopy(fake_connect_info) fake_connect_info2['access_url'] = 'fake_console_url' @@ -5747,7 +5815,7 @@ def test_vnc_console(self): 'version': compute_rpcapi.ComputeAPI.BASE_RPC_API_VERSION} rpc_msg2 = {'method': 'authorize_console', 'args': fake_connect_info, - 'version': '1.0'} + 'version': '1.2'} rpc.call(self.context, 'compute.%s' % fake_instance['host'], rpc_msg1, None).AndReturn(fake_connect_info2) @@ -5779,7 +5847,8 @@ def test_spice_console(self): 'console_type': fake_console_type, 'host': 'fake_console_host', 'port': 'fake_console_port', - 'internal_access_path': 'fake_access_path'} + 'internal_access_path': 'fake_access_path', + 'instance_uuid': fake_instance['uuid']} fake_connect_info2 = copy.deepcopy(fake_connect_info) fake_connect_info2['access_url'] = 'fake_console_url' @@ -5791,7 +5860,7 @@ def test_spice_console(self): 'version': '2.24'} rpc_msg2 = {'method': 'authorize_console', 'args': fake_connect_info, - 'version': '1.0'} + 'version': '1.2'} rpc.call(self.context, 'compute.%s' % fake_instance['host'], rpc_msg1, None).AndReturn(fake_connect_info2)
nova/tests/compute/test_rpcapi.py+6 −0 modified@@ -171,6 +171,12 @@ def test_get_spice_console(self): instance=self.fake_instance, console_type='type', version='2.24') + def test_validate_console_port(self): + self._test_compute_api('validate_console_port', 'call', + instance=self.fake_instance, port="5900", + console_type="novnc", + version="2.26") + def test_host_maintenance_mode(self): self._test_compute_api('host_maintenance_mode', 'call', host_param='param', mode='mode', host='host')
nova/tests/consoleauth/test_consoleauth.py+63 −1 modified@@ -42,12 +42,74 @@ def test_tokens_expire(self): self.useFixture(test.TimeOverride()) token = 'mytok' self.flags(console_token_ttl=1) + + def fake_validate_console_port(*args, **kwargs): + return True + self.stubs.Set(self.manager.compute_rpcapi, + "validate_console_port", + fake_validate_console_port) + self.manager.authorize_console(self.context, token, 'novnc', - '127.0.0.1', 'host', '') + '127.0.0.1', '8080', 'host', + 'instance') self.assertTrue(self.manager.check_token(self.context, token)) timeutils.advance_time_seconds(1) self.assertFalse(self.manager.check_token(self.context, token)) + def test_multiple_tokens_for_instance(self): + tokens = ["token" + str(i) for i in xrange(10)] + instance = "12345" + + def fake_validate_console_port(*args, **kwargs): + return True + + self.stubs.Set(self.manager.compute_rpcapi, + "validate_console_port", + fake_validate_console_port) + for token in tokens: + self.manager.authorize_console(self.context, token, 'novnc', + '127.0.0.1', '8080', 'host', + instance) + + for token in tokens: + self.assertTrue(self.manager.check_token(self.context, token)) + + def test_delete_tokens_for_instance(self): + instance = "12345" + tokens = ["token" + str(i) for i in xrange(10)] + for token in tokens: + self.manager.authorize_console(self.context, token, 'novnc', + '127.0.0.1', '8080', 'host', + instance) + self.manager.delete_tokens_for_instance(self.context, instance) + stored_tokens = self.manager._get_tokens_for_instance(instance) + + self.assertEqual(len(stored_tokens), 0) + + for token in tokens: + self.assertFalse(self.manager.check_token(self.context, token)) + + def test_wrong_token_has_port(self): + token = 'mytok' + + def fake_validate_console_port(*args, **kwargs): + return False + + self.stubs.Set(self.manager.compute_rpcapi, + "validate_console_port", + fake_validate_console_port) + + self.manager.authorize_console(self.context, token, 'novnc', + '127.0.0.1', '8080', 'host', + instance_uuid='instance') + self.assertFalse(self.manager.check_token(self.context, token)) + + def test_console_no_instance_uuid(self): + self.manager.authorize_console(self.context, "token", 'novnc', + '127.0.0.1', '8080', 'host', + instance_uuid=None) + self.assertFalse(self.manager.check_token(self.context, "token")) + def test_get_backdoor_port(self): self.manager.backdoor_port = 59697 port = self.manager.get_backdoor_port(self.context)
nova/tests/consoleauth/test_rpcapi.py+7 −1 modified@@ -65,11 +65,17 @@ def _fake_call(_ctxt, _topic, _msg, _timeout): def test_authorize_console(self): self._test_consoleauth_api('authorize_console', token='token', console_type='ctype', host='h', port='p', - internal_access_path='iap') + internal_access_path='iap', instance_uuid="instance", + version="1.2") def test_check_token(self): self._test_consoleauth_api('check_token', token='t') + def test_delete_tokens_for_instnace(self): + self._test_consoleauth_api('delete_tokens_for_instance', + instance_uuid="instance", + version='1.2') + def test_get_backdoor_port(self): self._test_consoleauth_api('get_backdoor_port', host='fake_host', version='1.1')
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
20- secunia.com/advisories/52337nvdVendor Advisory
- secunia.com/advisories/52728nvdVendor Advisory
- github.com/advisories/GHSA-qfp8-hfqx-c79cghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2013-0335ghsaADVISORY
- github.com/openstack/nova/commit/05a3374992bc8ba53ddc9c491b51c4b59eed0a72ghsaWEB
- github.com/openstack/nova/commit/3b0f4cf6bea33e6ee1893f6e872d968b0c309f88ghsaWEB
- github.com/openstack/nova/commit/48e81f1554ce41c3d4f7445421d19f4a8128e98dghsaWEB
- github.com/openstack/nova/commit/ad94a90202193335f011888db017e557b07faf8aghsaWEB
- github.com/openstack/nova/commit/e98928cf77645fdc309da894f3bd332e99482e0dghsaWEB
- rhn.redhat.com/errata/RHSA-2013-0709.htmlnvdWEB
- www.openwall.com/lists/oss-security/2013/02/26/7nvdWEB
- www.ubuntu.com/usn/USN-1771-1nvdWEB
- bugs.launchpad.net/nova/+bug/1125378nvdWEB
- github.com/pypa/advisory-database/tree/main/vulns/nova/PYSEC-2013-43.yamlghsaWEB
- review.openstack.orgghsaWEB
- review.openstack.orgnvdWEB
- review.openstack.orgghsaWEB
- www.osvdb.org/90657nvd
- review.openstack.orgnvd
- review.openstack.orgnvd
News mentions
0No linked articles in our index yet.