Ejemplo n.º 1
0
    def __call__(self, req):
        context = req.environ['ec2api.context']
        api_request = req.environ['ec2.request']
        try:
            result = api_request.invoke(context)
        except botocore.exceptions.ClientError as ex:
            error = ex.response.get('Error', {})
            code = ex.response.get('Code', error.get('Code'))
            message = ex.response.get('Message', error.get('Message'))
            # the early versions of botocore didn't provide HTTPStatusCode
            # for 400 errors
            status = ex.response.get('ResponseMetadata', {}).get(
                'HTTPStatusCode', 400)
            if status < 400 or status > 499:
                LOG.exception("Exception from remote server")
            return faults.ec2_error_response(
                context.request_id, code, message, status=status)
        except Exception as ex:
            return ec2_error_ex(
                ex, req, unexpected=not isinstance(ex, exception.EC2Exception))
        else:
            resp = webob.Response()
            resp.status = 200
            resp.headers['Content-Type'] = 'text/xml'
            resp.body = six.binary_type(result)

            return resp
Ejemplo n.º 2
0
    def __call__(self, req):
        context = req.environ['ec2api.context']
        api_request = req.environ['ec2.request']
        try:
            result = api_request.invoke(context)
        except botocore.exceptions.ClientError as ex:
            error = ex.response.get('Error', {})
            code = ex.response.get('Code', error.get('Code'))
            message = ex.response.get('Message', error.get('Message'))
            # the early versions of botocore didn't provide HTTPStatusCode
            # for 400 errors
            status = ex.response.get('ResponseMetadata',
                                     {}).get('HTTPStatusCode', 400)
            if status < 400 or status > 499:
                LOG.exception("Exception from remote server")
            return faults.ec2_error_response(context.request_id,
                                             code,
                                             message,
                                             status=status)
        except Exception as ex:
            return ec2_error_ex(
                ex, req, unexpected=not isinstance(ex, exception.EC2Exception))
        else:
            resp = webob.Response()
            resp.status = 200
            resp.headers['Content-Type'] = 'text/xml'
            resp.body = six.binary_type(result)

            return resp
Ejemplo n.º 3
0
def ec2_error_ex(ex, req, unexpected=False):
    """Return an EC2 error response.

    Return an EC2 error response based on passed exception and log
    the exception on an appropriate log level:

        * DEBUG: expected errors
        * ERROR: unexpected errors

    All expected errors are treated as client errors and 4xx HTTP
    status codes are always returned for them.

    Unexpected 5xx errors may contain sensitive information,
    suppress their messages for security.
    """
    code = exception_to_ec2code(ex)
    for status_name in ('code', 'status', 'status_code', 'http_status'):
        status = getattr(ex, status_name, None)
        if isinstance(status, int):
            break
    else:
        status = 500

    if unexpected:
        log_fun = LOG.error
        log_msg = _("Unexpected %(ex_name)s raised: %(ex_str)s")
        exc_info = sys.exc_info()
    else:
        log_fun = LOG.debug
        log_msg = _("%(ex_name)s raised: %(ex_str)s")
        exc_info = None

    context = req.environ['ec2api.context']
    request_id = context.request_id
    log_msg_args = {
        'ex_name': type(ex).__name__,
        'ex_str': ex
    }
    log_fun(log_msg % log_msg_args, context=context, exc_info=exc_info)

    if unexpected and status >= 500:
        message = _('Unknown error occurred.')
    elif getattr(ex, 'message', None):
        message = six.text_type(ex.message)
    elif ex.args and any(arg for arg in ex.args):
        message = " ".join(map(six.text_type, ex.args))
    else:
        message = six.text_type(ex)
    if unexpected:
        # Log filtered environment for unexpected errors.
        env = req.environ.copy()
        for k in list(env.keys()):
            if not isinstance(env[k], six.string_types):
                env.pop(k)
        log_fun(_('Environment: %s') % jsonutils.dumps(env))
    return faults.ec2_error_response(request_id, code, message, status=status)
Ejemplo n.º 4
0
def ec2_error_ex(ex, req, unexpected=False):
    """Return an EC2 error response.

    Return an EC2 error response based on passed exception and log
    the exception on an appropriate log level:

        * DEBUG: expected errors
        * ERROR: unexpected errors

    All expected errors are treated as client errors and 4xx HTTP
    status codes are always returned for them.

    Unexpected 5xx errors may contain sensitive information,
    suppress their messages for security.
    """
    code = exception_to_ec2code(ex)
    for status_name in ('code', 'status', 'status_code', 'http_status'):
        status = getattr(ex, status_name, None)
        if isinstance(status, int):
            break
    else:
        status = 500

    if unexpected:
        log_fun = LOG.error
        log_msg = _("Unexpected %(ex_name)s raised: %(ex_str)s")
        exc_info = sys.exc_info()
    else:
        log_fun = LOG.debug
        log_msg = _("%(ex_name)s raised: %(ex_str)s")
        exc_info = None

    context = req.environ['ec2api.context']
    request_id = context.request_id
    log_msg_args = {
        'ex_name': type(ex).__name__,
        'ex_str': ex
    }
    log_fun(log_msg % log_msg_args, context=context, exc_info=exc_info)

    if unexpected and status >= 500:
        message = _('Unknown error occurred.')
    elif getattr(ex, 'message', None):
        message = six.text_type(ex.message)
    elif ex.args and any(arg for arg in ex.args):
        message = " ".join(map(six.text_type, ex.args))
    else:
        message = six.text_type(ex)
    if unexpected:
        # Log filtered environment for unexpected errors.
        env = req.environ.copy()
        for k in list(env.keys()):
            if not isinstance(env[k], six.string_types):
                env.pop(k)
        log_fun(_('Environment: %s') % jsonutils.dumps(env))
    return faults.ec2_error_response(request_id, code, message, status=status)
Ejemplo n.º 5
0
def _get_ec2_credentials(context):
    # ec2_creds = clients.keystone(context).ec2.list(context.user_id)
    url = CONF.keystone_url + '/users/%s/credentials/OS-EC2' % context.user_id
    headers = {
        'Content-Type': 'application/json',
        'X-Auth-Token': context.auth_token
    }

    verify = CONF.ssl_ca_file or not CONF.ssl_insecure
    response = requests.request('GET', url, headers=headers, verify=verify)
    status_code = response.status_code
    if status_code != 200:
        msg = response.reason
        return faults.ec2_error_response(context.request_id,
                                         "AuthFailure",
                                         msg,
                                         status=status_code)
    result = response.json()
    return result['credentials']
Ejemplo n.º 6
0
    def __call__(self, req):
        request_id = common_context.generate_request_id()

        # NOTE(alevine) We need to calculate the hash here because
        # subsequent access to request modifies the req.body so the hash
        # calculation will yield invalid results.
        body_hash = hashlib.sha256(req.body).hexdigest()

        signature = self._get_signature(req)
        if not signature:
            msg = _("Signature not provided")
            return faults.ec2_error_response(request_id,
                                             "AuthFailure",
                                             msg,
                                             status=400)
        access = self._get_access(req)
        if not access:
            msg = _("Access key not provided")
            return faults.ec2_error_response(request_id,
                                             "AuthFailure",
                                             msg,
                                             status=400)

        if 'X-Amz-Signature' in req.params or 'Authorization' in req.headers:
            params = {}
        else:
            # Make a copy of args for authentication and signature verification
            params = dict(req.params)
            # Not part of authentication args
            params.pop('Signature', None)

        cred_dict = {
            'access': access,
            'signature': signature,
            'host': req.host,
            'verb': req.method,
            'path': req.path,
            'params': params,
            # python3 takes only keys for json from headers object
            'headers': {k: req.headers[k]
                        for k in req.headers},
            'body_hash': body_hash
        }

        token_url = CONF.keystone_ec2_tokens_url
        if "ec2" in token_url:
            creds = {'ec2Credentials': cred_dict}
        else:
            creds = {'auth': {'OS-KSEC2:ec2Credentials': cred_dict}}
        creds_json = jsonutils.dumps(creds)
        headers = {'Content-Type': 'application/json'}
        params = {'data': creds_json, 'headers': headers}
        clients.update_request_params_with_ssl(params)
        response = requests.request('POST', token_url, **params)
        status_code = response.status_code
        if status_code != 200:
            msg = response.reason
            return faults.ec2_error_response(request_id,
                                             "AuthFailure",
                                             msg,
                                             status=status_code)

        try:
            auth_ref = keystone_access.AccessInfo.factory(resp=response,
                                                          body=response.json())
        except (NotImplementedError, KeyError):
            LOG.exception("Keystone failure")
            msg = _("Failure communicating with keystone")
            return faults.ec2_error_response(request_id,
                                             "AuthFailure",
                                             msg,
                                             status=400)
        auth = keystone_identity_access.AccessInfoPlugin(auth_ref)
        params = {'auth': auth}
        clients.update_request_params_with_ssl(params)
        session = keystone_session.Session(**params)
        remote_address = req.remote_addr
        if CONF.use_forwarded_for:
            remote_address = req.headers.get('X-Forwarded-For', remote_address)

        ctxt = context.RequestContext(auth_ref.user_id,
                                      auth_ref.project_id,
                                      request_id=request_id,
                                      user_name=auth_ref.username,
                                      project_name=auth_ref.project_name,
                                      remote_address=remote_address,
                                      session=session,
                                      api_version=req.params.get('Version'))

        req.environ['ec2api.context'] = ctxt

        return self.application
Ejemplo n.º 7
0
    def __call__(self, req):
        request_id = common_context.generate_request_id()

        # NOTE(alevine) We need to calculate the hash here because
        # subsequent access to request modifies the req.body so the hash
        # calculation will yield invalid results.
        body_hash = hashlib.sha256(req.body).hexdigest()

        signature = self._get_signature(req)
        if not signature:
            msg = _("Signature not provided")
            return faults.ec2_error_response(request_id, "AuthFailure", msg,
                                             status=400)
        access = self._get_access(req)
        if not access:
            msg = _("Access key not provided")
            return faults.ec2_error_response(request_id, "AuthFailure", msg,
                                             status=400)

        if 'X-Amz-Signature' in req.params or 'Authorization' in req.headers:
            params = {}
        else:
            # Make a copy of args for authentication and signature verification
            params = dict(req.params)
            # Not part of authentication args
            params.pop('Signature', None)

        cred_dict = {
            'access': access,
            'signature': signature,
            'host': req.host,
            'verb': req.method,
            'path': req.path,
            'params': params,
            # python3 takes only keys for json from headers object
            'headers': {k: req.headers[k] for k in req.headers},
            'body_hash': body_hash
        }

        token_url = CONF.keystone_ec2_tokens_url
        if "ec2" in token_url:
            creds = {'ec2Credentials': cred_dict}
        else:
            creds = {'auth': {'OS-KSEC2:ec2Credentials': cred_dict}}
        creds_json = jsonutils.dumps(creds)
        headers = {'Content-Type': 'application/json'}
        params = {'data': creds_json, 'headers': headers}
        clients.update_request_params_with_ssl(params)
        response = requests.request('POST', token_url, **params)
        status_code = response.status_code
        if status_code != 200:
            msg = response.reason
            return faults.ec2_error_response(request_id, "AuthFailure", msg,
                                             status=status_code)

        try:
            auth_ref = keystone_access.AccessInfo.factory(resp=response,
                                                          body=response.json())
        except (NotImplementedError, KeyError):
            LOG.exception("Keystone failure")
            msg = _("Failure communicating with keystone")
            return faults.ec2_error_response(request_id, "AuthFailure", msg,
                                             status=400)
        auth = keystone_identity_access.AccessInfoPlugin(auth_ref)
        params = {'auth': auth}
        clients.update_request_params_with_ssl(params)
        session = keystone_session.Session(**params)
        remote_address = req.remote_addr
        if CONF.use_forwarded_for:
            remote_address = req.headers.get('X-Forwarded-For',
                                             remote_address)

        ctxt = context.RequestContext(auth_ref.user_id, auth_ref.project_id,
                                      request_id=request_id,
                                      user_name=auth_ref.username,
                                      project_name=auth_ref.project_name,
                                      remote_address=remote_address,
                                      session=session,
                                      api_version=req.params.get('Version'))

        req.environ['ec2api.context'] = ctxt

        return self.application
    def __call__(self, req):
        metric_logger.startTime()
        request_id = context.generate_request_id()

        # NOTE(alevine) We need to calculate the hash here because
        # subsequent access to request modifies the req.body so the hash
        # calculation will yield invalid results.
        body_hash = hashlib.sha256(req.body).hexdigest()

        # Verify the version is as expected from config file
        req_version = req.params.get('Version')
        if not req_version or req_version not in CONF.supported_api_versions:
            _msg = ("Unsupported Version used in the request.")
            return faults.ec2_error_response(request_id,
                                             'BadRequest',
                                             _msg,
                                             status=400)

        if CONF.enable_policy_engine:
            try:
                rsrc_action_list = self.policy_engine.handle_params(
                    dict(req.params))
            except Exception as e:
                exc_type, exc_obj, exc_tb = sys.exc_info()
                LOG.exception(str(e))
                if isinstance(exc_obj, webob.exc.HTTPUnauthorized):
                    return faults.ec2_error_response(request_id,
                                                     'AuthFailure',
                                                     str(e),
                                                     status=403)
                elif isinstance(exc_obj, webob.exc.HTTPInternalServerError):
                    return faults.ec2_error_response(request_id,
                                                     'InternalError',
                                                     str(e),
                                                     status=500)
                else:
                    return faults.ec2_error_response(request_id,
                                                     'BadRequest',
                                                     str(e),
                                                     status=400)

        keystone_validation_url = ""
        data = {}
        headers = {'Content-Type': 'application/json'}
        auth_token = self._get_auth_token(req)
        if auth_token:
            headers['X-Auth-Token'] = auth_token
            data['action_resource_list'] = rsrc_action_list
            data = jsonutils.dumps(data)
            keystone_validation_url = CONF.keystone_token_url
        else:
            signature = self._get_signature(req)
            if not signature:
                msg = _("Signature not provided")
                return faults.ec2_error_response(request_id,
                                                 "AuthFailure",
                                                 msg,
                                                 status=400)
            access = self._get_access(req)
            if not access:
                msg = _("Access key not provided")
                return faults.ec2_error_response(request_id,
                                                 "AuthFailure",
                                                 msg,
                                                 status=400)

            if 'X-Amz-Signature' in req.params or 'Authorization' in req.headers:
                params = {}
            else:
                # Make a copy of args for authentication and signature verification
                params = dict(req.params)
                # Not part of authentication args
                params.pop('Signature', None)

            cred_dict = {
                'access': access,
                'signature': signature,
                'host': req.host,
                'verb': req.method,
                'path': req.path,
                'params': params,
                'headers': req.headers,
                'body_hash': body_hash
            }

            if CONF.enable_policy_engine:
                cred_dict['action_resource_list'] = rsrc_action_list

            keystone_validation_url = CONF.keystone_sig_url
            if "ec2" in keystone_validation_url:
                creds = {'ec2Credentials': cred_dict}
            else:
                creds = {'auth': {'OS-KSEC2:ec2Credentials': cred_dict}}
            data = jsonutils.dumps(creds)

        verify = CONF.ssl_ca_file or not CONF.ssl_insecure
        response = requests.request('POST',
                                    keystone_validation_url,
                                    verify=verify,
                                    data=data,
                                    headers=headers)

        status_code = response.status_code
        if status_code != 200:
            msg = response.text
            return faults.ec2_error_response(request_id,
                                             "AuthFailure",
                                             msg,
                                             status=status_code)
        result = response.json()
        try:
            user_id = result.get('user_id')
            project_id = result.get('account_id')
            if auth_token:
                token_id = auth_token
            else:
                token_id = result.get('token_id')
            if not user_id or not project_id:
                raise KeyError
        except (AttributeError, KeyError):
            LOG.exception(_("Keystone failure"))
            msg = _("Failure communicating with keystone")
            return faults.ec2_error_response(request_id,
                                             "AuthFailure",
                                             msg,
                                             status=400)

        remote_address = req.remote_addr
        if CONF.use_forwarded_for:
            remote_address = req.headers.get('X-Forwarded-For', remote_address)

        # Fill in default values
        user_name = project_name = 'default'
        roles = catalog = []
        ctxt = context.RequestContext(user_id,
                                      project_id,
                                      user_name=user_name,
                                      project_name=project_name,
                                      roles=roles,
                                      auth_token=token_id,
                                      remote_address=remote_address,
                                      service_catalog=catalog,
                                      api_version=req.params.get('Version'))

        req.environ['ec2api.context'] = ctxt
        compute_request_id = ctxt.request_id
        appendRequestDict = {'requestid': compute_request_id}
        actionName = ec2utils.camelcase_to_underscore(req.params.get('Action'))
        actionName = actionName + "-auth"
        metric_logger.reportTime(actionName, addOnInfoPairs=appendRequestDict)
        return self.application
Ejemplo n.º 9
0
    def __call__(self, req):
        request_id = context.generate_request_id()

        # NOTE(alevine) We need to calculate the hash here because
        # subsequent access to request modifies the req.body so the hash
        # calculation will yield invalid results.
        body_hash = hashlib.sha256(req.body).hexdigest()

        signature = self._get_signature(req)
        if not signature:
            msg = _("Signature not provided")
            return faults.ec2_error_response(request_id,
                                             "AuthFailure",
                                             msg,
                                             status=400)
        access = self._get_access(req)
        if not access:
            msg = _("Access key not provided")
            return faults.ec2_error_response(request_id,
                                             "AuthFailure",
                                             msg,
                                             status=400)

        if 'X-Amz-Signature' in req.params or 'Authorization' in req.headers:
            params = {}
        else:
            # Make a copy of args for authentication and signature verification
            params = dict(req.params)
            # Not part of authentication args
            params.pop('Signature', None)

        cred_dict = {
            'access': access,
            'signature': signature,
            'host': req.host,
            'verb': req.method,
            'path': req.path,
            'params': params,
            'headers': req.headers,
            'body_hash': body_hash
        }

        token_url = CONF.keystone_ec2_tokens_url
        if "ec2" in token_url:
            creds = {'ec2Credentials': cred_dict}
        else:
            creds = {'auth': {'OS-KSEC2:ec2Credentials': cred_dict}}
        creds_json = jsonutils.dumps(creds)
        headers = {'Content-Type': 'application/json'}

        response = requests.request('POST',
                                    token_url,
                                    data=creds_json,
                                    headers=headers)
        status_code = response.status_code
        if status_code != 200:
            msg = response.reason
            return faults.ec2_error_response(request_id,
                                             "AuthFailure",
                                             msg,
                                             status=status_code)
        result = response.json()

        try:
            if 'token' in result:
                # NOTE(andrey-mp): response from keystone v3
                token_id = response.headers['x-subject-token']
                user_id = result['token']['user']['id']
                project_id = result['token']['project']['id']
                user_name = result['token']['user'].get('name')
                project_name = result['token']['project'].get('name')
                catalog = result['token']['catalog']
            else:
                token_id = result['access']['token']['id']
                user_id = result['access']['user']['id']
                project_id = result['access']['token']['tenant']['id']
                user_name = result['access']['user'].get('name')
                project_name = result['access']['token']['tenant'].get('name')
                catalog = result['access']['serviceCatalog']
        except (AttributeError, KeyError):
            LOG.exception(_("Keystone failure"))
            msg = _("Failure communicating with keystone")
            return faults.ec2_error_response(request_id,
                                             "AuthFailure",
                                             msg,
                                             status=400)

        remote_address = req.remote_addr
        if CONF.use_forwarded_for:
            remote_address = req.headers.get('X-Forwarded-For', remote_address)

        ctxt = context.RequestContext(user_id,
                                      project_id,
                                      request_id=request_id,
                                      user_name=user_name,
                                      project_name=project_name,
                                      auth_token=token_id,
                                      remote_address=remote_address,
                                      service_catalog=catalog,
                                      api_version=req.params.get('Version'))

        req.environ['ec2api.context'] = ctxt

        return self.application
Ejemplo n.º 10
0
    def __call__(self, req):
        request_id = context.generate_request_id()

        # NOTE(alevine) We need to calculate the hash here because
        # subsequent access to request modifies the req.body so the hash
        # calculation will yield invalid results.
        body_hash = hashlib.sha256(req.body).hexdigest()

        signature = self._get_signature(req)
        if not signature:
            msg = _("Signature not provided")
            return faults.ec2_error_response(request_id, "AuthFailure", msg,
                                             status=400)
        access = self._get_access(req)
        if not access:
            msg = _("Access key not provided")
            return faults.ec2_error_response(request_id, "AuthFailure", msg,
                                             status=400)

        if 'X-Amz-Signature' in req.params or 'Authorization' in req.headers:
            params = {}
        else:
            # Make a copy of args for authentication and signature verification
            params = dict(req.params)
            # Not part of authentication args
            params.pop('Signature', None)

        cred_dict = {
            'access': access,
            'signature': signature,
            'host': req.host,
            'verb': req.method,
            'path': req.path,
            'params': params,
            'headers': req.headers,
            'body_hash': body_hash
        }

        token_url = CONF.keystone_ec2_tokens_url
        if "ec2" in token_url:
            creds = {'ec2Credentials': cred_dict}
        else:
            creds = {'auth': {'OS-KSEC2:ec2Credentials': cred_dict}}
        creds_json = jsonutils.dumps(creds)
        headers = {'Content-Type': 'application/json'}

        response = requests.request('POST', token_url,
                                    data=creds_json, headers=headers)
        status_code = response.status_code
        if status_code != 200:
            msg = response.reason
            return faults.ec2_error_response(request_id, "AuthFailure", msg,
                                             status=status_code)
        result = response.json()

        try:
            if 'token' in result:
                # NOTE(andrey-mp): response from keystone v3
                token_id = response.headers['x-subject-token']
                user_id = result['token']['user']['id']
                project_id = result['token']['project']['id']
                user_name = result['token']['user'].get('name')
                project_name = result['token']['project'].get('name')
                catalog = result['token']['catalog']
            else:
                token_id = result['access']['token']['id']
                user_id = result['access']['user']['id']
                project_id = result['access']['token']['tenant']['id']
                user_name = result['access']['user'].get('name')
                project_name = result['access']['token']['tenant'].get('name')
                catalog = result['access']['serviceCatalog']
        except (AttributeError, KeyError):
            LOG.exception(_("Keystone failure"))
            msg = _("Failure communicating with keystone")
            return faults.ec2_error_response(request_id, "AuthFailure", msg,
                                             status=400)

        remote_address = req.remote_addr
        if CONF.use_forwarded_for:
            remote_address = req.headers.get('X-Forwarded-For',
                                             remote_address)

        ctxt = context.RequestContext(user_id, project_id,
                                      request_id=request_id,
                                      user_name=user_name,
                                      project_name=project_name,
                                      auth_token=token_id,
                                      remote_address=remote_address,
                                      service_catalog=catalog,
                                      api_version=req.params.get('Version'))

        req.environ['ec2api.context'] = ctxt

        return self.application