Merge "Add console allowed origins setting"
This commit is contained in:
@@ -30,9 +30,19 @@ from nova.consoleauth import rpcapi as consoleauth_rpcapi
|
|||||||
from nova import context
|
from nova import context
|
||||||
from nova import exception
|
from nova import exception
|
||||||
from nova.i18n import _
|
from nova.i18n import _
|
||||||
|
from oslo_config import cfg
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
CONF = cfg.CONF
|
||||||
|
console_origin_opts = [
|
||||||
|
cfg.ListOpt('console_allowed_origins',
|
||||||
|
default=[],
|
||||||
|
help='Allowed Origin header hostnames for access to console '
|
||||||
|
'proxy servers'),
|
||||||
|
]
|
||||||
|
CONF.register_opts(console_origin_opts)
|
||||||
|
|
||||||
|
|
||||||
class NovaProxyRequestHandlerBase(object):
|
class NovaProxyRequestHandlerBase(object):
|
||||||
def address_string(self):
|
def address_string(self):
|
||||||
@@ -104,6 +114,8 @@ class NovaProxyRequestHandlerBase(object):
|
|||||||
expected_origin_hostname = e.split(']')[0][1:]
|
expected_origin_hostname = e.split(']')[0][1:]
|
||||||
else:
|
else:
|
||||||
expected_origin_hostname = e.split(':')[0]
|
expected_origin_hostname = e.split(':')[0]
|
||||||
|
expected_origin_hostnames = CONF.console_allowed_origins
|
||||||
|
expected_origin_hostnames.append(expected_origin_hostname)
|
||||||
origin_url = self.headers.getheader('Origin')
|
origin_url = self.headers.getheader('Origin')
|
||||||
# missing origin header indicates non-browser client which is OK
|
# missing origin header indicates non-browser client which is OK
|
||||||
if origin_url is not None:
|
if origin_url is not None:
|
||||||
@@ -113,7 +125,7 @@ class NovaProxyRequestHandlerBase(object):
|
|||||||
if origin_hostname == '' or origin_scheme == '':
|
if origin_hostname == '' or origin_scheme == '':
|
||||||
detail = _("Origin header not valid.")
|
detail = _("Origin header not valid.")
|
||||||
raise exception.ValidationError(detail=detail)
|
raise exception.ValidationError(detail=detail)
|
||||||
if expected_origin_hostname != origin_hostname:
|
if origin_hostname not in expected_origin_hostnames:
|
||||||
detail = _("Origin header does not match this host.")
|
detail = _("Origin header does not match this host.")
|
||||||
raise exception.ValidationError(detail=detail)
|
raise exception.ValidationError(detail=detail)
|
||||||
if not self.verify_origin_proto(connect_info, origin_scheme):
|
if not self.verify_origin_proto(connect_info, origin_scheme):
|
||||||
|
|||||||
@@ -30,6 +30,8 @@ class NovaProxyRequestHandlerBaseTestCase(test.NoDBTestCase):
|
|||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(NovaProxyRequestHandlerBaseTestCase, self).setUp()
|
super(NovaProxyRequestHandlerBaseTestCase, self).setUp()
|
||||||
|
|
||||||
|
self.flags(console_allowed_origins = ['allowed-origin-example-1.net',
|
||||||
|
'allowed-origin-example-2.net'])
|
||||||
self.wh = websocketproxy.NovaProxyRequestHandlerBase()
|
self.wh = websocketproxy.NovaProxyRequestHandlerBase()
|
||||||
self.wh.socket = mock.MagicMock()
|
self.wh.socket = mock.MagicMock()
|
||||||
self.wh.msg = mock.MagicMock()
|
self.wh.msg = mock.MagicMock()
|
||||||
@@ -76,6 +78,16 @@ class NovaProxyRequestHandlerBaseTestCase(test.NoDBTestCase):
|
|||||||
else:
|
else:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
def _fake_getheader_allowed_origin(self, header):
|
||||||
|
if header == 'cookie':
|
||||||
|
return 'token="123-456-789"'
|
||||||
|
elif header == 'Origin':
|
||||||
|
return 'https://allowed-origin-example-2.net:6080'
|
||||||
|
elif header == 'Host':
|
||||||
|
return 'example.net:6080'
|
||||||
|
else:
|
||||||
|
return
|
||||||
|
|
||||||
def _fake_getheader_blank_origin(self, header):
|
def _fake_getheader_blank_origin(self, header):
|
||||||
if header == 'cookie':
|
if header == 'cookie':
|
||||||
return 'token="123-456-789"'
|
return 'token="123-456-789"'
|
||||||
@@ -266,6 +278,25 @@ class NovaProxyRequestHandlerBaseTestCase(test.NoDBTestCase):
|
|||||||
self.assertRaises(exception.ValidationError,
|
self.assertRaises(exception.ValidationError,
|
||||||
self.wh.new_websocket_client)
|
self.wh.new_websocket_client)
|
||||||
|
|
||||||
|
@mock.patch('nova.consoleauth.rpcapi.ConsoleAuthAPI.check_token')
|
||||||
|
def test_new_websocket_client_novnc_allowed_origin_header(self,
|
||||||
|
check_token):
|
||||||
|
check_token.return_value = {
|
||||||
|
'host': 'node1',
|
||||||
|
'port': '10000',
|
||||||
|
'console_type': 'novnc',
|
||||||
|
'access_url': 'https://example.net:6080'
|
||||||
|
}
|
||||||
|
self.wh.socket.return_value = '<socket>'
|
||||||
|
self.wh.path = "http://127.0.0.1/"
|
||||||
|
self.wh.headers.getheader = self._fake_getheader_allowed_origin
|
||||||
|
|
||||||
|
self.wh.new_websocket_client()
|
||||||
|
|
||||||
|
check_token.assert_called_with(mock.ANY, token="123-456-789")
|
||||||
|
self.wh.socket.assert_called_with('node1', 10000, connect=True)
|
||||||
|
self.wh.do_proxy.assert_called_with('<socket>')
|
||||||
|
|
||||||
@mock.patch('nova.consoleauth.rpcapi.ConsoleAuthAPI.check_token')
|
@mock.patch('nova.consoleauth.rpcapi.ConsoleAuthAPI.check_token')
|
||||||
def test_new_websocket_client_novnc_blank_origin_header(self, check_token):
|
def test_new_websocket_client_novnc_blank_origin_header(self, check_token):
|
||||||
check_token.return_value = {
|
check_token.return_value = {
|
||||||
|
|||||||
Reference in New Issue
Block a user