Example #1
0
def is_ip_whitelisted_machine():
    """Returns True if the call is made from IP whitelisted machine."""
    # TODO(vadimsh): Get rid of this. It's blocked on fixing /bot_code calls in
    # bootstrap code everywhere to use service accounts and switching all Swarming
    # Tasks API calls made from bots to use proper authentication.
    return auth.is_in_ip_whitelist(auth.bots_ip_whitelist(),
                                   auth.get_peer_ip(), False)
Example #2
0
def validate_bot_id_and_fetch_config(bot_id):
    """Verifies ID reported by a bot matches the credentials being used.

  Expected to be called in a context of some bot API request handler. Uses
  bots.cfg config to look up what credentials are expected to be used by the bot
  with given ID.

  Raises auth.AuthorizationError if bot_id is unknown or bot is using invalid
  credentials.

  On success returns the configuration for this bot (BotGroupConfig tuple), as
  defined in bots.cfg
  """
    cfg = bot_groups_config.get_bot_group_config(bot_id)
    if not cfg:
        logging.error(
            'bot_auth: unknown bot_id, not in the config\nbot_id: "%s"',
            bot_id)
        raise auth.AuthorizationError('Unknown bot ID, not in config')

    peer_ident = auth.get_peer_identity()
    if cfg.require_luci_machine_token:
        if not _is_valid_ident_for_bot(peer_ident, bot_id):
            logging.error(
                'bot_auth: bot ID doesn\'t match the machine token used\n'
                'bot_id: "%s", peer_ident: "%s"', bot_id,
                peer_ident.to_bytes())
            raise auth.AuthorizationError(
                'Bot ID doesn\'t match the token used')
    elif cfg.require_service_account:
        expected_id = auth.Identity(auth.IDENTITY_USER,
                                    cfg.require_service_account)
        if peer_ident != expected_id:
            logging.error(
                'bot_auth: bot is not using expected service account\n'
                'bot_id: "%s", expected_id: "%s", peer_ident: "%s"', bot_id,
                expected_id.to_bytes(), peer_ident.to_bytes())
            raise auth.AuthorizationError(
                'bot is not using expected service account')
    elif not cfg.ip_whitelist:
        # This branch should not be hit for validated configs.
        logging.error(
            'bot_auth: invalid bot group config, no auth method defined\n'
            'bot_id: "%s"', bot_id)
        raise auth.AuthorizationError('Invalid bot group config')

    # Check that IP whitelist applies (in addition to credentials).
    if cfg.ip_whitelist:
        ip = auth.get_peer_ip()
        if not auth.is_in_ip_whitelist(cfg.ip_whitelist, ip):
            logging.error(
                'bot_auth: bot IP is not whitelisted\n'
                'bot_id: "%s", peer_ip: "%s", ip_whitelist: "%s"', bot_id,
                ipaddr.ip_to_string(ip), cfg.ip_whitelist)
            raise auth.AuthorizationError('Not IP whitelisted')

    return cfg
Example #3
0
    def post(self):
        # Forbid usage of delegation tokens for this particular call. Using
        # delegation when creating delegation tokens is too deep. Redelegation will
        # be done as separate explicit API call that accept existing delegation
        # token via request body, not via headers.
        if auth.get_current_identity() != auth.get_peer_identity():
            raise auth.AuthorizationError(
                'This API call must not be used with active delegation token')

        # Convert request body to proto (with validation). Verify IP format.
        try:
            body = self.parse_body()
            subtoken = subtoken_from_jsonish(body)
            intent = body.get('intent') or ''
            if not isinstance(intent, basestring):
                raise TypeError('"intent" must be string')
        except (TypeError, ValueError) as exc:
            self.abort_with_error(400, text=str(exc))

        # Fill in defaults.
        assert not subtoken.requestor_identity
        user_id = auth.get_current_identity().to_bytes()
        subtoken.requestor_identity = user_id
        if not subtoken.delegated_identity:
            subtoken.delegated_identity = user_id
        subtoken.creation_time = int(utils.time_time())
        if not subtoken.validity_duration:
            subtoken.validity_duration = DEF_VALIDITY_DURATION_SEC
        if '*' in subtoken.services:
            subtoken.services[:] = get_default_allowed_services(user_id)

        # Check ACL (raises auth.AuthorizationError on errors).
        rule = check_can_create_token(user_id, subtoken)

        # Register the token in the datastore, generate its ID.
        subtoken.subtoken_id = register_subtoken(subtoken, rule, intent,
                                                 auth.get_peer_ip())

        # Create and sign the token.
        try:
            token = delegation.serialize_token(delegation.seal_token(subtoken))
        except delegation.BadTokenError as exc:
            # This happens if resulting token is too large.
            self.abort_with_error(400, text=str(exc))

        self.send_response(response={
            'delegation_token': token,
            'subtoken_id': str(subtoken.subtoken_id),
            'validity_duration': subtoken.validity_duration,
        },
                           http_code=201)
Example #4
0
def file_size(size):
    """Reports the size of a file fetched from GCS by whitelisted clients.

  If the client's requests are not whitelisted for monitoring, does nothing.

  Args:
    size: Size of the file in bytes.
  """
    ip = auth.get_peer_ip()
    for cfg in config.settings().client_monitoring_config:
        if auth.is_in_ip_whitelist(cfg.ip_whitelist, ip):
            _bytes_requested.increment_by(
                size,
                fields={
                    'client_name': cfg.label,
                    'client_email': auth.get_peer_identity().to_bytes(),
                    'download_source': 'GCS'
                })
            return
Example #5
0
 def who(self, _request):
   return WhoResponse(
       host=auth.get_peer_host(),
       identity=auth.get_current_identity().to_bytes(),
       ip=auth.ip_to_string(auth.get_peer_ip()))
Example #6
0
 def who(self, _request):
     return WhoResponse(host=auth.get_peer_host(),
                        identity=auth.get_current_identity().to_bytes(),
                        ip=auth.ip_to_string(auth.get_peer_ip()))
Example #7
0
def validate_bot_id_and_fetch_config(bot_id):
    """Verifies ID reported by a bot matches the credentials being used.

  Expected to be called in a context of some bot API request handler. Uses
  bots.cfg config to look up what credentials are expected to be used by the bot
  with given ID.

  Raises auth.AuthorizationError if bot_id is unknown or bot is using invalid
  credentials.

  On success returns the configuration for this bot (BotGroupConfig tuple), as
  defined in bots.cfg.
  """
    bot_id = _extract_primary_hostname(bot_id)
    cfg = bot_groups_config.get_bot_group_config(bot_id)
    if not cfg:
        logging.error(
            'bot_auth: unknown bot_id, not in the config\nbot_id: "%s"',
            bot_id)
        raise auth.AuthorizationError('Unknown bot ID, not in config')

    # This should not really happen for validated configs.
    if not cfg.auth:
        logging.error('bot_auth: no auth configured in bots.cfg')
        raise auth.AuthorizationError('No auth configured in bots.cfg')

    ip = auth.get_peer_ip()
    peer_ident = auth.get_peer_identity()

    # Errors from all auth methods.
    auth_errs = []
    # Logs to emit if all methods fail. Omitted if some method succeeds.
    delayed_logs = []

    # Try all auth methods sequentially until a first success. When migrating
    # between different methods it may be important to know when a method is
    # skipped. Logs from such methods are always emitted at 'error' level. Other
    # logs are buffered and emitted only if all methods fail.
    for bot_auth in cfg.auth:
        err, details = _check_bot_auth(bot_auth, bot_id, peer_ident, ip)
        if not err:
            logging.debug('Using auth method: %s', bot_auth)
            return cfg
        auth_errs.append(err)
        if bot_auth.log_if_failed:
            logging.error('Preferred auth method failed: %s', err)
            logging.error('Failed auth method: %s', bot_auth)
            for msg in details:
                logging.error('%s', msg)
        else:
            delayed_logs.append('Auth method failed: %s' % (err, ))
            delayed_logs.append('Failed auth method: %s' % (bot_auth, ))
            delayed_logs.extend(details)

    # All fallback methods failed. Need their logs to investigate.
    for msg in delayed_logs:
        logging.error('%s', msg)

    # In most cases there's only one auth method used, so we can simplify the
    # error message to be less confusing.
    if len(auth_errs) == 1:
        raise auth.AuthorizationError(auth_errs[0])
    raise auth.AuthorizationError('All auth methods failed: %s' %
                                  '; '.join(auth_errs))
Example #8
0
def is_ip_whitelisted_machine():
  """Returns True if the call is made from IP whitelisted machine."""
  return auth.is_in_ip_whitelist(auth.bots_ip_whitelist(), auth.get_peer_ip(),
                                 False)