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
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)