Example #1
0
def learn_from_result(result, node):
    if not result['data']:
        return

    capture_columns = set(
        map(itemgetter(0), current_app.config['POLYLOGYX_CAPTURE_NODE_INFO']))

    if not capture_columns:
        return

    node_info = node.get('node_info', {})
    orig_node_info = node_info.copy()

    for _, action, columns, _, _, in extract_results(result):
        # only update columns common to both sets
        for column in capture_columns & set(columns):

            cvalue = node_info.get(column)  # current value
            value = columns.get(column)

            if action == 'removed' and (cvalue is None or cvalue != value):
                pass
            elif action == 'removed' and cvalue == value:
                node_info.pop(column)
            elif action == 'added' and (cvalue is None or cvalue != value):
                node_info[column] = value

    # only update node_info if there's actually a change

    if orig_node_info == node_info:
        return

    node = Node.get_by_id(node['id'])
    node.update(node_info=node_info)
    return
Example #2
0
def enroll():
    '''
    Enroll an endpoint with osquery.
    :returns: a `node_key` unique id. Additionally `node_invalid` will
        be true if the node failed to enroll.
    '''
    remote_addr = get_ip()
    request_json = request.get_json()
    if not request_json:
        current_app.logger.error(
            "%s - Request did not contain valid JSON data. This could "
            "be an attempt to gather information about this endpoint "
            "or an automated scanner.", remote_addr)
        # Return nothing
        return ""

    enroll_secret = request_json.get(
        current_app.config.get('POLYLOGYX_ENROLL_OVERRIDE', 'enroll_secret'))

    if not enroll_secret:
        current_app.logger.error(
            "%s - No enroll_secret provided by remote host", remote_addr)
        return jsonify(node_invalid=True)

    # If we pre-populate node table with a per-node enroll_secret,
    # let's query it now.

    if current_app.config.get('POLYLOGYX_ENROLL_SECRET_TAG_DELIMITER'):
        delimiter = current_app.config.get(
            'POLYLOGYX_ENROLL_SECRET_TAG_DELIMITER')
        enroll_secret, _, enroll_tags = enroll_secret.partition(delimiter)
        enroll_tags = set(
            [tag.strip() for tag in enroll_tags.split(delimiter)[:10]])
    else:
        enroll_secret, enroll_tags = enroll_secret, set()

    node = Node.query.filter(Node.enroll_secret == enroll_secret).first()

    if not node and enroll_secret not in current_app.config[
            'POLYLOGYX_ENROLL_SECRET']:
        current_app.logger.error("%s - Invalid enroll_secret %s", remote_addr,
                                 enroll_secret)
        return jsonify(node_invalid=True)

    host_identifier = request_json.get('host_identifier')

    if node and node.enrolled_on:
        current_app.logger.warn(
            "%s - %s already enrolled on %s, returning existing node_key",
            remote_addr, node, node.enrolled_on)

        if node.host_identifier != host_identifier:
            current_app.logger.info(
                "%s - %s changed their host_identifier to %s", remote_addr,
                node, host_identifier)
            node.host_identifier = host_identifier

        node.update(last_checkin=dt.datetime.utcnow(), last_ip=remote_addr)
        send_checkin_queries(node)
        return jsonify(node_key=node.node_key, node_invalid=False)

    existing_node = None
    if host_identifier:
        existing_node = Node.query.filter(
            Node.host_identifier == host_identifier).first()

    if existing_node and not existing_node.enroll_secret:
        current_app.logger.warning(
            "%s - Duplicate host_identifier %s, already enrolled %s",
            remote_addr, host_identifier, existing_node.enrolled_on)

        if current_app.config['POLYLOGYX_EXPECTS_UNIQUE_HOST_ID'] is True:
            current_app.logger.info(
                "%s - Unique host identification is true, %s already enrolled "
                "returning existing node key %s", remote_addr, host_identifier,
                existing_node.node_key)
            existing_node.update(last_checkin=dt.datetime.utcnow(),
                                 last_ip=remote_addr)
            send_checkin_queries(existing_node)
            return jsonify(node_key=existing_node.node_key, node_invalid=False)

    now = dt.datetime.utcnow()

    if node:
        node.update(host_identifier=host_identifier,
                    last_checkin=now,
                    enrolled_on=now,
                    last_ip=remote_addr)
    else:
        node = Node(host_identifier=host_identifier,
                    last_checkin=now,
                    enrolled_on=now,
                    last_ip=remote_addr)

        enroll_tags.update(
            current_app.config.get('POLYLOGYX_ENROLL_DEFAULT_TAGS', []))

        for value in sorted((t.strip() for t in enroll_tags if t)):
            tag = Tag.query.filter_by(value=value).first()
            if tag and tag not in node.tags:
                node.tags.append(tag)
            elif not tag:
                node.tags.append(Tag(value=value))

        node.save()

    current_app.logger.info("%s - Enrolled new node %s", remote_addr, node)
    notify_of_node_enrollment.apply_async(queue='default_queue_tasks',
                                          args=[node.to_dict()])
    # notify_of_node_enrollment.delay(node.to_dict())
    send_checkin_queries(node)
    update_system_details(request_json, node)

    return jsonify(node_key=node.node_key, node_invalid=False)