1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195
|
Description: Flush tokens on instance delete
Force console auth service to flush all tokens associated with an instance
when it is deleted. This will fix bug 1125378, where the console for the wrong
instance can be connected to via theconsole if the correct circumstances
occur. This change also adds a call to validate the token when it is used.
This check will ensure that all tokens are valid for their target instances.
Tokens can become scrambled when a compute node is restarted, because the virt
driver may not assign ports in the same way.
Bug-Debian: http://bugs.debian.org/701773
Bug-Ubuntu: https://launchpad.net/bugs/1125378
Author: Loganathan Parthipan <parthipan@hp.com>
Date: Sat, 23 Feb 2013 05:42:10 +0000
diff --git a/nova/compute/api.py b/nova/compute/api.py
index 633e282..a317c44 100644
--- a/nova/compute/api.py
+++ b/nova/compute/api.py
@@ -1554,12 +1554,22 @@ class API(BaseAPI):
'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."""
params = {'tail_length': tail_length}
diff --git a/nova/compute/manager.py b/nova/compute/manager.py
index ca51fc5..67c6bb1 100644
--- a/nova/compute/manager.py
+++ b/nova/compute/manager.py
@@ -749,6 +749,10 @@ class ComputeManager(manager.SchedulerDependentManager):
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())
diff --git a/nova/consoleauth/manager.py b/nova/consoleauth/manager.py
index f43dbfb..ae929c4 100644
--- a/nova/consoleauth/manager.py
+++ b/nova/consoleauth/manager.py
@@ -27,6 +27,8 @@ from nova import log as logging
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 @@ class ConsoleAuthManager(manager.Manager):
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]
diff --git a/nova/tests/test_compute.py b/nova/tests/test_compute.py
index 2e7e7de..b0a8b79 100644
--- a/nova/tests/test_compute.py
+++ b/nova/tests/test_compute.py
@@ -754,6 +754,47 @@ class ComputeTestCase(BaseTestCase):
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()
diff --git a/nova/tests/test_consoleauth.py b/nova/tests/test_consoleauth.py
index 6370a4f..13d0e74 100644
--- a/nova/tests/test_consoleauth.py
+++ b/nova/tests/test_consoleauth.py
@@ -47,6 +47,13 @@ class ConsoleauthTestCase(test.TestCase):
"""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))
--
1.7.9.5
|