コード例 #1
0
ファイル: websocketproxy.py プロジェクト: mba811/nova
    def new_websocket_client(self):
        """Called after a new WebSocket connection has been established."""
        # Reopen the eventlet hub to make sure we don't share an epoll
        # fd with parent and/or siblings, which would be bad
        from eventlet import hubs
        hubs.use_hub()

        # The nova expected behavior is to have token
        # passed to the method GET of the request
        query = urlparse.urlparse(self.path).query
        token = urlparse.parse_qs(query).get("token", [""]).pop()
        if not token:
            # NoVNC uses it's own convention that forward token
            # from the request to a cookie header, we should check
            # also for this behavior
            hcookie = self.headers.getheader('cookie')
            if hcookie:
                cookie = Cookie.SimpleCookie()
                cookie.load(hcookie)
                if 'token' in cookie:
                    token = cookie['token'].value

        ctxt = context.get_admin_context()
        rpcapi = consoleauth_rpcapi.ConsoleAuthAPI()
        connect_info = rpcapi.check_token(ctxt, token=token)

        if not connect_info:
            raise exception.InvalidToken(token=token)

        self.msg(_('connect info: %s'), str(connect_info))
        host = connect_info['host']
        port = int(connect_info['port'])

        # Connect to the target
        self.msg(
            _("connecting to: %(host)s:%(port)s") % {
                'host': host,
                'port': port
            })
        tsock = self.socket(host, port, connect=True)

        # Handshake as necessary
        if connect_info.get('internal_access_path'):
            tsock.send("CONNECT %s HTTP/1.1\r\n\r\n" %
                       connect_info['internal_access_path'])
            while True:
                data = tsock.recv(4096, socket.MSG_PEEK)
                if data.find("\r\n\r\n") != -1:
                    if data.split("\r\n")[0].find("200") == -1:
                        raise Exception(_("Invalid Connection Info"))
                    tsock.recv(len(data))
                    break

        # Start proxying
        try:
            self.do_proxy(tsock)
        except Exception:
            if tsock:
                tsock.shutdown(socket.SHUT_RDWR)
                tsock.close()
                self.vmsg(
                    _("%(host)s:%(port)s: Target closed") % {
                        'host': host,
                        'port': port
                    })
            raise
コード例 #2
0
    def new_websocket_client(self):
        """Called after a new WebSocket connection has been established."""
        # Reopen the eventlet hub to make sure we don't share an epoll
        # fd with parent and/or siblings, which would be bad
        from eventlet import hubs
        hubs.use_hub()

        # The nova expected behavior is to have token
        # passed to the method GET of the request
        parse = urlparse.urlparse(self.path)
        if parse.scheme not in ('http', 'https'):
            # From a bug in urlparse in Python < 2.7.4 we cannot support
            # special schemes (cf: http://bugs.python.org/issue9374)
            if sys.version_info < (2, 7, 4):
                raise exception.NovaException(
                    _("We do not support scheme '%s' under Python < 2.7.4, "
                      "please use http or https") % parse.scheme)

        query = parse.query
        token = urlparse.parse_qs(query).get("token", [""]).pop()
        if not token:
            # NoVNC uses it's own convention that forward token
            # from the request to a cookie header, we should check
            # also for this behavior
            hcookie = self.headers.getheader('cookie')
            if hcookie:
                cookie = Cookie.SimpleCookie()
                cookie.load(hcookie)
                if 'token' in cookie:
                    token = cookie['token'].value

        ctxt = context.get_admin_context()
        rpcapi = consoleauth_rpcapi.ConsoleAuthAPI()
        connect_info = rpcapi.check_token(ctxt, token=token)

        if not connect_info:
            raise exception.InvalidToken(token=token)

        # Verify Origin
        expected_origin_hostname = self.headers.getheader('Host')
        if ':' in expected_origin_hostname:
            e = expected_origin_hostname
            if '[' in e and ']' in e:
                expected_origin_hostname = e.split(']')[0][1:]
            else:
                expected_origin_hostname = e.split(':')[0]
        origin_url = self.headers.getheader('Origin')
        # missing origin header indicates non-browser client which is OK
        if origin_url is not None:
            origin = urlparse.urlparse(origin_url)
            origin_hostname = origin.hostname
            origin_scheme = origin.scheme
            if origin_hostname == '' or origin_scheme == '':
                detail = _("Origin header not valid.")
                raise exception.ValidationError(detail=detail)
            if expected_origin_hostname != origin_hostname:
                detail = _("Origin header does not match this host.")
                raise exception.ValidationError(detail=detail)
            if not self.verify_origin_proto(connect_info, origin_scheme):
                detail = _("Origin header protocol does not match this host.")
                raise exception.ValidationError(detail=detail)

        self.msg(_('connect info: %s'), str(connect_info))
        host = connect_info['host']
        port = int(connect_info['port'])

        # Connect to the target
        self.msg(_("connecting to: %(host)s:%(port)s") % {'host': host,
                                                          'port': port})
        tsock = self.socket(host, port, connect=True)

        # Handshake as necessary
        if connect_info.get('internal_access_path'):
            tsock.send("CONNECT %s HTTP/1.1\r\n\r\n" %
                        connect_info['internal_access_path'])
            while True:
                data = tsock.recv(4096, socket.MSG_PEEK)
                if data.find("\r\n\r\n") != -1:
                    if data.split("\r\n")[0].find("200") == -1:
                        raise exception.InvalidConnectionInfo()
                    tsock.recv(len(data))
                    break

        # Start proxying
        try:
            self.do_proxy(tsock)
        except Exception:
            if tsock:
                tsock.shutdown(socket.SHUT_RDWR)
                tsock.close()
                self.vmsg(_("%(host)s:%(port)s: Target closed") %
                          {'host': host, 'port': port})
            raise
コード例 #3
0
class ConsoleAuthTokensExtensionTestV21(test.NoDBTestCase):
    controller_class = console_auth_tokens_v21

    _EXPECTED_OUTPUT = {
        'console': {
            'instance_uuid': fakes.FAKE_UUID,
            'host': 'fake_host',
            'port': '1234',
            'internal_access_path': 'fake_access_path'
        }
    }

    # The database backend returns a ConsoleAuthToken.to_dict() and o.vo
    # StringField are unicode. And the port is an IntegerField.
    _EXPECTED_OUTPUT_DB = copy.deepcopy(_EXPECTED_OUTPUT)
    _EXPECTED_OUTPUT_DB['console'].update({
        'host':
        u'fake_host',
        'port':
        1234,
        'internal_access_path':
        u'fake_access_path'
    })

    def setUp(self):
        super(ConsoleAuthTokensExtensionTestV21, self).setUp()
        self.controller = self.controller_class.ConsoleAuthTokensController()
        self.req = fakes.HTTPRequest.blank('', use_admin_context=True)
        self.context = self.req.environ['nova.context']

    @ddt.data(True, False)
    @mock.patch('nova.objects.ConsoleAuthToken.validate',
                return_value=objects.ConsoleAuthToken(
                    instance_uuid=fakes.FAKE_UUID,
                    host='fake_host',
                    port='1234',
                    internal_access_path='fake_access_path',
                    console_type='rdp-html5',
                    token=fakes.FAKE_UUID))
    @mock.patch.object(consoleauth_rpcapi.ConsoleAuthAPI,
                       'check_token',
                       return_value={
                           'instance_uuid': fakes.FAKE_UUID,
                           'host': 'fake_host',
                           'port': '1234',
                           'internal_access_path': 'fake_access_path',
                           'console_type': 'rdp-html5'
                       })
    def test_get_console_connect_info(self, enable_consoleauth,
                                      mock_check_token, mock_validate):
        self.flags(enable_consoleauth=enable_consoleauth, group='workarounds')
        output = self.controller.show(self.req, fakes.FAKE_UUID)
        if enable_consoleauth:
            self.assertEqual(self._EXPECTED_OUTPUT, output)
            mock_check_token.assert_called_once_with(self.context,
                                                     fakes.FAKE_UUID)
            mock_validate.assert_not_called()
        else:
            self.assertEqual(self._EXPECTED_OUTPUT_DB, output)
            mock_validate.assert_called_once_with(self.context,
                                                  fakes.FAKE_UUID)
            mock_check_token.assert_not_called()

    @ddt.data(True, False)
    @mock.patch('nova.objects.ConsoleAuthToken.validate',
                side_effect=exception.InvalidToken(token='***'))
    @mock.patch.object(consoleauth_rpcapi.ConsoleAuthAPI,
                       'check_token',
                       return_value=None)
    def test_get_console_connect_info_token_not_found(self, enable_consoleauth,
                                                      mock_check_token,
                                                      mock_validate):
        self.flags(enable_consoleauth=enable_consoleauth, group='workarounds')
        self.assertRaises(webob.exc.HTTPNotFound, self.controller.show,
                          self.req, fakes.FAKE_UUID)
        if enable_consoleauth:
            mock_check_token.assert_called_once_with(self.context,
                                                     fakes.FAKE_UUID)
            mock_validate.assert_not_called()
        else:
            mock_validate.assert_called_once_with(self.context,
                                                  fakes.FAKE_UUID)
            mock_check_token.assert_not_called()

    @ddt.data(True, False)
    @mock.patch('nova.objects.ConsoleAuthToken.validate',
                return_value=objects.ConsoleAuthToken(
                    instance_uuid=fakes.FAKE_UUID,
                    host='fake_host',
                    port='1234',
                    internal_access_path='fake_access_path',
                    console_type='unauthorized_console_type',
                    token=fakes.FAKE_UUID))
    @mock.patch.object(consoleauth_rpcapi.ConsoleAuthAPI,
                       'check_token',
                       return_value={
                           'instance_uuid': fakes.FAKE_UUID,
                           'host': 'fake_host',
                           'port': '1234',
                           'internal_access_path': 'fake_access_path',
                           'console_type': 'unauthorized_console_type'
                       })
    def test_get_console_connect_info_nonrdp_console_type(
            self, enable_consoleauth, mock_check_token, mock_validate):
        self.flags(enable_consoleauth=enable_consoleauth, group='workarounds')
        self.assertRaises(webob.exc.HTTPUnauthorized, self.controller.show,
                          self.req, fakes.FAKE_UUID)
        if enable_consoleauth:
            mock_check_token.assert_called_once_with(self.context,
                                                     fakes.FAKE_UUID)
            mock_validate.assert_not_called()
        else:
            mock_validate.assert_called_once_with(self.context,
                                                  fakes.FAKE_UUID)
            mock_check_token.assert_not_called()