Use X-Forwarded-Proto as origin protocol if present

When using a haproxy with SSL termination to provide secure console
connections, haproxy will change the Origin header scheme to 'http'
and add the 'X-Forwarded-Proto: https' header. This causes a failure
in the Nova console proxy code which verifies that the Origin header
scheme matches the access_url scheme for the connection, because the
Origin header coming from haproxy is 'http' while the access_url
scheme is 'https' or 'wss'.

This looks for the X-Forwarded-Proto header and uses its scheme for
the verification instead, if it is present.

Closes-Bug: #1788180

Change-Id: I43401dc8368853654bf443273a0a1b5b9b63e3f0
This commit is contained in:
melanie witt
2019-01-16 00:27:42 +00:00
parent 89c2d1056b
commit 890e2d320e
2 changed files with 39 additions and 0 deletions

View File

@@ -231,6 +231,13 @@ class NovaProxyRequestHandlerBase(object):
origin = urlparse.urlparse(origin_url)
origin_hostname = origin.hostname
origin_scheme = origin.scheme
# If the console connection was forwarded by a proxy (example:
# haproxy), the original protocol could be contained in the
# X-Forwarded-Proto header instead of the Origin header. Prefer the
# forwarded protocol if it is present.
forwarded_proto = self.headers.get('X-Forwarded-Proto')
if forwarded_proto is not None:
origin_scheme = forwarded_proto
if origin_hostname == '' or origin_scheme == '':
detail = _("Origin header not valid.")
raise exception.ValidationError(detail=detail)

View File

@@ -628,6 +628,38 @@ class NovaProxyRequestHandlerBaseTestCase(test.NoDBTestCase):
self.assertRaises(exception.ValidationError,
self.wh.new_websocket_client)
@mock.patch('nova.console.websocketproxy.NovaProxyRequestHandlerBase.'
'_check_console_port')
@mock.patch('nova.objects.ConsoleAuthToken.validate')
def test_new_websocket_client_http_forwarded_proto_https(self, validate,
check_port):
params = {
'id': 1,
'token': '123-456-789',
'instance_uuid': uuids.instance,
'host': 'node1',
'port': '10000',
'console_type': 'serial',
'access_url_base': 'wss://example.net:6080'
}
validate.return_value = objects.ConsoleAuthToken(**params)
header = {
'cookie': 'token="123-456-789"',
'Origin': 'http://example.net:6080',
'Host': 'example.net:6080',
'X-Forwarded-Proto': 'https'
}
self.wh.socket.return_value = '<socket>'
self.wh.path = "https://127.0.0.1/"
self.wh.headers = header
self.wh.new_websocket_client()
validate.assert_called_with(mock.ANY, "123-456-789")
self.wh.socket.assert_called_with('node1', 10000, connect=True)
self.wh.do_proxy.assert_called_with('<socket>')
@mock.patch('nova.console.websocketproxy.NovaProxyRequestHandlerBase.'
'_check_console_port')
@mock.patch('nova.objects.ConsoleAuthToken.validate')