def _verify_origin(self, access_url): # Verify Origin expected_origin_hostname = self.headers.get('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] expected_origin_hostnames = CONF.websocket_proxy.allowed_origins expected_origin_hostnames.append(expected_origin_hostname) origin_url = self.headers.get('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) if origin_hostname not in expected_origin_hostnames: detail = _("Origin header does not match this host.") raise exception.ValidationError(detail) if not self.verify_origin_proto(access_url, origin_scheme): detail = _("Origin header protocol does not match this host.") raise exception.ValidationError(detail)
def verify_origin_proto(self, access_url, origin_proto): if not access_url: detail = _("No access_url available." "Cannot validate protocol") raise exception.ValidationError(detail=detail) expected_protos = [urlparse.urlparse(access_url).scheme] # NOTE: For serial consoles the expected protocol could be ws or # wss which correspond to http and https respectively in terms of # security. if 'ws' in expected_protos: expected_protos.append('http') if 'wss' in expected_protos: expected_protos.append('https') return origin_proto in expected_protos
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 zun 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: https://bugs.python.org/issue9374) if sys.version_info < (2, 7, 4): raise exception.ZunException( _("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() uuid = urlparse.parse_qs(query).get("uuid", [""]).pop() dbapi = db_api._get_dbdriver_instance() ctx = context.get_admin_context(all_tenants=True) if uuidutils.is_uuid_like(uuid): container = dbapi.get_container_by_uuid(ctx, uuid) else: container = dbapi.get_container_by_name(ctx, uuid) if token != container.websocket_token: raise exception.InvalidWebsocketToken(token) access_url = '%s?token=%s&uuid=%s' % (CONF.websocket_proxy.base_url, token, uuid) # Verify Origin expected_origin_hostname = self.headers.get('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] expected_origin_hostnames = CONF.websocket_proxy.allowed_origins expected_origin_hostnames.append(expected_origin_hostname) origin_url = self.headers.get('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) if origin_hostname not in expected_origin_hostnames: detail = _("Origin header does not match this host.") raise exception.ValidationError(detail) if not self.verify_origin_proto(access_url, origin_scheme): detail = _("Origin header protocol does not match this host.") raise exception.ValidationError(detail) if container.websocket_url: target_url = container.websocket_url escape = "~" close_wait = 0.5 wscls = WebSocketClient(host_url=target_url, escape=escape, close_wait=close_wait) wscls.connect() self.target = wscls else: raise exception.InvalidWebsocketUrl() # Start proxying try: self.do_proxy(self.target.ws) except Exception as e: if self.target.ws: self.target.ws.close() self.vmsg(_("%Websocket client or target closed")) raise
def validate_image_driver(image_driver): if image_driver not in CONF.image_driver_list: detail = _("Invalid input for image_driver, " "it should be within the image drivers list") raise exception.ValidationError(detail=detail)