Skip to content

Commit 186aff9

Browse files
mnaserlyarwood
authored andcommitted
Add 'path' query parameter to console access url
Starting in noVNC v1.1.0, the token query parameter is no longer forwarded via cookie [1]. We must instead use the 'path' query parameter to pass the token through to the websocketproxy [2]. This means that if someone deploys noVNC v1.1.0, VNC consoles will break in nova because the code is relying on the cookie functionality that v1.1.0 removed. This modifies the ConsoleAuthToken.access_url property to include the 'path' query parameter as part of the returned access_url that the client will use to call the console proxy service. This change is backward compatible with noVNC < v1.1.0. The 'path' query parameter is a long supported feature in noVNC. Co-Authored-By: melanie witt <[email protected]> Closes-Bug: #1822676 [1] novnc/noVNC@51f9f00 [2] novnc/noVNC#1220 Change-Id: I2ddf0f4d768b698e980594dd67206464a9cea37b (cherry picked from commit 9606c80)
1 parent 950e044 commit 186aff9

17 files changed

+53
-36
lines changed
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"console": {
33
"type": "rdp-html5",
4-
"url": "http://127.0.0.1:6083/?token=191996c3-7b0f-42f3-95a7-f1839f2da6ed"
4+
"url": "http://127.0.0.1:6083/?path=%3Ftoken%3D21efbb20-b84c-4d1f-807d-4e14f6884b7f"
55
}
6-
}
6+
}
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"console": {
33
"type": "serial",
4-
"url":"ws://127.0.0.1:6083/?token=f9906a48-b71e-4f18-baca-c987da3ebdb3"
4+
"url": "ws://127.0.0.1:6083/?path=%3Ftoken%3D6ac46b4c-2705-4d8b-baa3-1b6f1b0c7dd3"
55
}
6-
}
6+
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"console": {
33
"type": "spice-html5",
4-
"url": "http://127.0.0.1:6082/spice_auto.html?token=a30e5d08-6a20-4043-958f-0852440c6af4"
4+
"url": "http://127.0.0.1:6082/spice_auto.html?path=%3Ftoken%3Da7bd9607-421c-44b9-8689-18e87ada2f78"
55
}
66
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"console": {
33
"type": "novnc",
4-
"url": "http://127.0.0.1:6080/vnc_auto.html?token=191996c3-7b0f-42f3-95a7-f1839f2da6ed"
4+
"url": "http://127.0.0.1:6080/vnc_auto.html?path=%3Ftoken%3Ddaae261f-474d-4cae-8f6a-1865278ed8c9"
55
}
66
}

doc/api_samples/os-remote-consoles/v2.6/create-vnc-console-resp.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@
22
"remote_console": {
33
"protocol": "vnc",
44
"type": "novnc",
5-
"url": "http://example.com:6080/vnc_auto.html?token=b60bcfc3-5fd4-4d21-986c-e83379107819"
5+
"url": "http://example.com:6080/vnc_auto.html?path=%3Ftoken%3Db60bcfc3-5fd4-4d21-986c-e83379107819"
66
}
77
}

doc/source/admin/figures/SCH_5009_V00_NUAC-VNC_OpenStack.svg

Lines changed: 2 additions & 2 deletions
Loading

doc/source/admin/remote-console-access.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ nova provides services to handle this proxying. Consider a noVNC-based VNC
3030
console connection for example:
3131

3232
#. A user connects to the API and gets an ``access_url`` such as,
33-
``http://ip:port/?token=xyz``.
33+
``http://ip:port/?path=%3Ftoken%3Dxyz``.
3434

3535
#. The user pastes the URL in a browser or uses it as a client parameter.
3636

nova/api/openstack/compute/rest_api_version_history.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ Example response::
8484
"remote_console": {
8585
"protocol": "vnc",
8686
"type": "novnc",
87-
"url": "http://example.com:6080/vnc_auto.html?token=XYZ"
87+
"url": "http://example.com:6080/vnc_auto.html?path=%3Ftoken%3DXYZ"
8888
}
8989
}
9090

nova/objects/console_auth_token.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
from oslo_utils import strutils
1919
from oslo_utils import timeutils
2020
from oslo_utils import uuidutils
21+
import six.moves.urllib.parse as urlparse
2122

2223
from nova.db import api as db
2324
from nova import exception
@@ -60,7 +61,9 @@ def access_url(self):
6061
specific to this authorization.
6162
"""
6263
if self.obj_attr_is_set('id'):
63-
return '%s?token=%s' % (self.access_url_base, self.token)
64+
qparams = {'path': '?token=%s' % self.token}
65+
return '%s?%s' % (self.access_url_base,
66+
urlparse.urlencode(qparams))
6467

6568
@staticmethod
6669
def _from_db_object(context, obj, db_obj):
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"console": {
33
"type": "rdp-html5",
4-
"url": "http://127.0.0.1:6083/?token=%(uuid)s"
4+
"url": "http://127.0.0.1:6083/?path=%%3Ftoken%%3D%(uuid)s"
55
}
66
}
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"console": {
33
"type": "serial",
4-
"url": "ws://127.0.0.1:6083/?token=%(uuid)s"
4+
"url": "ws://127.0.0.1:6083/?path=%%3Ftoken%%3D%(uuid)s"
55
}
66
}
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"console": {
33
"type": "spice-html5",
4-
"url": "http://127.0.0.1:6082/spice_auto.html?token=%(uuid)s"
4+
"url": "http://127.0.0.1:6082/spice_auto.html?path=%%3Ftoken%%3D%(uuid)s"
55
}
66
}
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"console": {
33
"type": "novnc",
4-
"url": "http://127.0.0.1:6080/vnc_auto.html?token=%(uuid)s"
4+
"url": "http://127.0.0.1:6080/vnc_auto.html?path=%%3Ftoken%%3D%(uuid)s"
55
}
66
}

nova/tests/functional/api_sample_tests/test_console_auth_tokens.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import re
1616

1717
from oslo_serialization import jsonutils
18+
import six.moves.urllib.parse as urlparse
1819

1920
from nova.tests.functional.api_sample_tests import test_servers
2021

@@ -32,7 +33,8 @@ def _get_console_token(self, uuid):
3233
{'action': 'os-getRDPConsole'})
3334

3435
url = self._get_console_url(response.content)
35-
return re.match('.+?token=([^&]+)', url).groups()[0]
36+
path = urlparse.urlencode({'path': '?token='})
37+
return re.match('.+?%s([^&]+)' % path, url).groups()[0]
3638

3739
def test_get_console_connect_info(self):
3840
self.flags(enabled=True, group='rdp')

nova/tests/unit/console/test_websocketproxy.py

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
import mock
2121
from oslo_utils.fixture import uuidsentinel as uuids
22+
import six.moves.urllib.parse as urlparse
2223

2324
import nova.conf
2425
from nova.console.securityproxy import base
@@ -47,6 +48,7 @@ def setUp(self):
4748
self.wh.msg = mock.MagicMock()
4849
self.wh.do_proxy = mock.MagicMock()
4950
self.wh.headers = mock.MagicMock()
51+
self.path = urlparse.urlencode({'path': '?token=123-456-789'})
5052

5153
def _fake_console_db(self, **updates):
5254
console_db = copy.deepcopy(fake_ca.fake_token_dict)
@@ -96,7 +98,7 @@ def test_new_websocket_client_db(
9698
tsock.recv.return_value = "HTTP/1.1 200 OK\r\n\r\n"
9799
self.wh.socket.return_value = tsock
98100

99-
self.wh.path = "http://127.0.0.1/?token=123-456-789"
101+
self.wh.path = "http://127.0.0.1/?%s" % self.path
100102
self.wh.headers = self.fake_header
101103

102104
if instance_not_found:
@@ -143,6 +145,8 @@ def setUp(self):
143145
self.wh.msg = mock.MagicMock()
144146
self.wh.do_proxy = mock.MagicMock()
145147
self.wh.headers = mock.MagicMock()
148+
self.path = urlparse.urlencode({'path': '?token=123-456-789'})
149+
self.path_invalid = urlparse.urlencode({'path': '?token=XXX'})
146150

147151
fake_header = {
148152
'cookie': 'token="123-456-789"',
@@ -228,7 +232,7 @@ def test_new_websocket_client_enable_consoleauth(self, check_token):
228232
'access_url': 'https://example.net:6080'
229233
}
230234
self.wh.socket.return_value = '<socket>'
231-
self.wh.path = "http://127.0.0.1/?token=123-456-789"
235+
self.wh.path = "http://127.0.0.1/?%s" % self.path
232236
self.wh.headers = self.fake_header
233237

234238
self.wh.new_websocket_client()
@@ -261,7 +265,7 @@ def test_new_websocket_client_enable_consoleauth_fallback(self, validate,
261265
validate.return_value = objects.ConsoleAuthToken(**params)
262266

263267
self.wh.socket.return_value = '<socket>'
264-
self.wh.path = "http://127.0.0.1/?token=123-456-789"
268+
self.wh.path = "http://127.0.0.1/?%s" % self.path
265269
self.wh.headers = self.fake_header
266270

267271
self.wh.new_websocket_client()
@@ -287,7 +291,7 @@ def test_new_websocket_client(self, validate, check_port):
287291
validate.return_value = objects.ConsoleAuthToken(**params)
288292

289293
self.wh.socket.return_value = '<socket>'
290-
self.wh.path = "http://127.0.0.1/?token=123-456-789"
294+
self.wh.path = "http://127.0.0.1/?%s" % self.path
291295
self.wh.headers = self.fake_header
292296

293297
self.wh.new_websocket_client()
@@ -312,7 +316,7 @@ def test_new_websocket_client_ipv6_url(self, validate, check_port):
312316
validate.return_value = objects.ConsoleAuthToken(**params)
313317

314318
self.wh.socket.return_value = '<socket>'
315-
self.wh.path = "http://[2001:db8::1]/?token=123-456-789"
319+
self.wh.path = "http://[2001:db8::1]/?%s" % self.path
316320
self.wh.headers = self.fake_header_ipv6
317321

318322
self.wh.new_websocket_client()
@@ -325,7 +329,7 @@ def test_new_websocket_client_ipv6_url(self, validate, check_port):
325329
def test_new_websocket_client_token_invalid(self, validate):
326330
validate.side_effect = exception.InvalidToken(token='XXX')
327331

328-
self.wh.path = "http://127.0.0.1/?token=XXX"
332+
self.wh.path = "http://127.0.0.1/?%s" % self.path_invalid
329333
self.wh.headers = self.fake_header_bad_token
330334

331335
self.assertRaises(exception.InvalidToken,
@@ -353,7 +357,7 @@ def test_new_websocket_client_internal_access_path(self, validate,
353357
tsock.recv.return_value = "HTTP/1.1 200 OK\r\n\r\n"
354358

355359
self.wh.socket.return_value = tsock
356-
self.wh.path = "http://127.0.0.1/?token=123-456-789"
360+
self.wh.path = "http://127.0.0.1/?%s" % self.path
357361
self.wh.headers = self.fake_header
358362

359363
self.wh.new_websocket_client()
@@ -386,7 +390,7 @@ def test_new_websocket_client_internal_access_path_err(self, validate,
386390
tsock.recv.return_value = "HTTP/1.1 500 Internal Server Error\r\n\r\n"
387391

388392
self.wh.socket.return_value = tsock
389-
self.wh.path = "http://127.0.0.1/?token=123-456-789"
393+
self.wh.path = "http://127.0.0.1/?%s" % self.path
390394
self.wh.headers = self.fake_header
391395

392396
self.assertRaises(exception.InvalidConnectionInfo,
@@ -418,7 +422,7 @@ def test_new_websocket_client_internal_access_path_rfb(self, validate,
418422
HTTP_RESP]
419423

420424
self.wh.socket.return_value = tsock
421-
self.wh.path = "http://127.0.0.1/?token=123-456-789"
425+
self.wh.path = "http://127.0.0.1/?%s" % self.path
422426
self.wh.headers = self.fake_header
423427

424428
self.wh.new_websocket_client()
@@ -448,7 +452,7 @@ def test_new_websocket_client_py273_good_scheme(
448452
validate.return_value = objects.ConsoleAuthToken(**params)
449453

450454
self.wh.socket.return_value = '<socket>'
451-
self.wh.path = "http://127.0.0.1/?token=123-456-789"
455+
self.wh.path = "http://127.0.0.1/?%s" % self.path
452456
self.wh.headers = self.fake_header
453457

454458
self.wh.new_websocket_client()
@@ -468,7 +472,7 @@ def test_new_websocket_client_py273_special_scheme(
468472
'console_type': 'novnc'
469473
}
470474
self.wh.socket.return_value = '<socket>'
471-
self.wh.path = "ws://127.0.0.1/?token=123-456-789"
475+
self.wh.path = "ws://127.0.0.1/?%s" % self.path
472476
self.wh.headers = self.fake_header
473477

474478
self.assertRaises(exception.NovaException,
@@ -477,10 +481,9 @@ def test_new_websocket_client_py273_special_scheme(
477481
@mock.patch('socket.getfqdn')
478482
def test_address_string_doesnt_do_reverse_dns_lookup(self, getfqdn):
479483
request_mock = mock.MagicMock()
480-
request_mock.makefile().readline.side_effect = [
481-
b'GET /vnc.html?token=123-456-789 HTTP/1.1\r\n',
482-
b''
483-
]
484+
msg = 'GET /vnc.html?%s HTTP/1.1\r\n' % self.path
485+
request_mock.makefile().readline.side_effect = [msg.encode('utf-8'),
486+
b'']
484487
server_mock = mock.MagicMock()
485488
client_address = ('8.8.8.8', 54321)
486489

@@ -734,7 +737,8 @@ def setUp(self):
734737
with mock.patch('websockify.ProxyRequestHandler'):
735738
self.wh = websocketproxy.NovaProxyRequestHandler()
736739
self.wh.server = self.server
737-
self.wh.path = "http://127.0.0.1/?token=123-456-789"
740+
path = urlparse.urlencode({'path': '?token=123-456-789'})
741+
self.wh.path = "http://127.0.0.1/?%s" % path
738742
self.wh.socket = mock.MagicMock()
739743
self.wh.msg = mock.MagicMock()
740744
self.wh.do_proxy = mock.MagicMock()

nova/tests/unit/objects/test_console_auth_token.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
from oslo_db.exception import DBDuplicateEntry
2020
from oslo_utils.fixture import uuidsentinel
2121
from oslo_utils import timeutils
22+
import six.moves.urllib.parse as urlparse
2223

2324
from nova import exception
2425
from nova.objects import console_auth_token as token_obj
@@ -70,9 +71,9 @@ def test_authorize(self, mock_create):
7071
self.compare_obj(obj, expected)
7172

7273
url = obj.access_url
73-
expected_url = '%s?token=%s' % (
74-
fakes.fake_token_dict['access_url_base'],
75-
fakes.fake_token)
74+
path = urlparse.urlencode({'path': '?token=%s' % fakes.fake_token})
75+
expected_url = '%s?%s' % (
76+
fakes.fake_token_dict['access_url_base'], path)
7677
self.assertEqual(expected_url, url)
7778

7879
@mock.patch('nova.db.api.console_auth_token_create')
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
fixes:
3+
- |
4+
Add support for noVNC >= v1.1.0 for VNC consoles. Prior to this fix, VNC
5+
console token validation always failed regardless of actual token validity
6+
with noVNC >= v1.1.0. See
7+
https://bugs.launchpad.net/nova/+bug/1822676 for more details.

0 commit comments

Comments
 (0)