Source code for vent.core.network_tap.ncontrol.paths

import ast
import json
import socket

import docker
import falcon
import redis


[docs]class CreateR(object): """ This endpoint is for creating a new filter """
[docs] def on_post(self, req, resp): """ Send a POST request with id/nic/interval/filter/iters and it will start a container for collection with those specifications """ resp.content_type = falcon.MEDIA_TEXT resp.status = falcon.HTTP_200 # verify payload is in the correct format # default to no filter payload = {} if req.content_length: try: payload = json.load(req.stream) except Exception as e: # pragma: no cover resp.body = 'malformed payload' return else: resp.body = 'malformed payload' return if 'filter' not in payload: payload['filter'] = '' # payload should have the following fields: # - id # - nic # - interval # - filter # - iters # should spin up a tcpdump container that writes out pcap files based # on the filter needs to be attached to the nic specified, if iters is # -1 then loops until killed, otherwise completes iters number of # captures (and creates that many pcap files) should keep track of # container id, container name, and id of filter and filter + whatever # else is in payload in redis # verify payload has necessary information if 'nic' not in payload: resp.body = 'payload missing nic' return if 'id' not in payload: resp.body = 'payload missing id' return if 'interval' not in payload: resp.body = 'payload missing interval' return if 'iters' not in payload: resp.body = 'payload missing iters' return # connect to redis if 'metadata' in payload: r = None try: r = redis.StrictRedis(host='redis', port=6379, db=0) except Exception as e: # pragma: no cover try: r = redis.StrictRedis(host='localhost', port=6379, db=0) except Exception as e: # pragma: no cover resp.body = "(False, 'unable to connect to redis because: " + str(e) + "')" return if r: metadata = {} try: metadata = ast.literal_eval(payload['metadata']) except Exception as e: # pragma: no cover resp.body = "(False, 'unable to convert metadata [ " + str( payload['metadata']) + ' ] into a dict because: ' + str(e) + "')" return try: redis_metadata = {} for key in metadata: redis_metadata[key] = str(metadata[key]) r.hmset(payload['id'], redis_metadata) r.hmset(metadata['endpoint_data']['mac'], {'poseidon_hash': payload['id']}) r.sadd('mac_addresses', metadata['endpoint_data']['mac']) if metadata['endpoint_data']['ipv4'] != 'None' and metadata['endpoint_data']['ipv4']: r.hmset(metadata['endpoint_data']['ipv4'], {'poseidon_hash': payload['id']}) r.sadd('ip_addresses', metadata['endpoint_data']['ipv4']) if metadata['endpoint_data']['ipv6'] != 'None' and metadata['endpoint_data']['ipv6']: r.hmset(metadata['endpoint_data']['ipv6'], {'poseidon_hash': payload['id']}) r.sadd('ip_addresses', metadata['endpoint_data']['ipv6']) except Exception as e: # pragma: no cover resp.body = "(False, 'unable to store contents of the payload " + str( metadata) + ' in redis because: ' + str(e) + "')" return # connect to docker c = None try: c = docker.from_env() except Exception as e: # pragma: no cover resp.body = "(False, 'unable to connect to docker because: " + str(e) + "')" return # spin up container with payload specifications if c: tool_d = {'network_mode': 'host', 'volumes_from': [socket.gethostname()]} cmd = '/tmp/run.sh ' + payload['nic'] + ' ' + payload['interval'] cmd += ' ' + payload['id'] + ' ' + payload['iters'] + ' "' cmd += payload['filter'] + '"' try: container_id = c.containers.run(image='cyberreboot/vent-ncapture:master', command=cmd, remove=True, detach=True, **tool_d) except Exception as e: # pragma: no cover resp.body = "(False, 'unable to start container because: " + str(e) + "')" return resp.body = "(True, 'successfully created and started filter: " + \ str(payload['id']) + ' on container: ' + str(container_id) + "')" return
[docs]class DeleteR(object): """ This endpoint is for deleting a network tap filter container """
[docs] def on_post(self, req, resp): """ Send a POST request with a docker container ID and it will be deleted. Example input: {'id': "12345"}, {'id': ["123", "456"]} """ resp.content_type = falcon.MEDIA_TEXT resp.status = falcon.HTTP_200 # verify user input payload = {} if req.content_length: try: payload = json.load(req.stream) except Exception as e: # pragma: no cover resp.body = 'malformed payload' return else: resp.body = 'malformed payload' return # verify payload has a container ID if 'id' not in payload: resp.body = 'payload missing id' return # connect to docker and stop the given container c = None try: c = docker.from_env() except Exception as e: # pragma: no cover resp.body = "(False, 'unable to connect to docker because: " + str(e) + "')" return # delete containers chosen from CLI try: for container_id in payload['id']: c.containers.get(container_id).remove() except Exception as e: # pragma: no cover resp.body = "(False, 'unable to delete containers because: " + str(e) + "')" return resp.body = "(True, 'container successfully deleted: " + \ str(payload['id']) + "')" return
[docs]class InfoR(object): """ This endpoint is for returning info about this service """
[docs] def on_get(self, req, resp): resp.body = json.dumps({'version': 'v0.1.0'}) resp.content_type = falcon.MEDIA_TEXT resp.status = falcon.HTTP_200 return
[docs]class ListR(object): """ This endpoint is for listing all filter containers """
[docs] def on_get(self, req, resp): """ Send a GET request to get the list of all of the filter containers """ resp.content_type = falcon.MEDIA_TEXT resp.status = falcon.HTTP_200 # connect to docker try: containers = docker.from_env() except Exception as e: # pragma: no cover resp.body = "(False, 'unable to connect to docker because: " + str(e) + "')" return # search for all docker containers and grab ncapture containers container_list = [] try: for c in containers.containers.list(all=True): # TODO: maybe find a way to not have to hard code image name if c.attrs['Config']['Image'] == \ 'cyberreboot/vent-ncapture:master': # the core container is not what we want if 'core' not in c.attrs['Config']['Labels']['vent.groups']: lst = {} lst['id'] = c.attrs['Id'][:12] lst['status'] = c.attrs['State']['Status'] lst['args'] = c.attrs['Args'] container_list.append(lst) except Exception as e: # pragma: no cover resp.body = "(False, 'Failure because: " + str(e) + "')" return resp.body = json.dumps(container_list) return
[docs]class NICsR(object): """ This endpoint is for listing all available network interfaces """
[docs] def on_get(self, req, resp): """ Send a GET request to get the list of all available network interfaces """ resp.content_type = falcon.MEDIA_TEXT resp.status = falcon.HTTP_200 # connect to docker try: d_client = docker.from_env() except Exception as e: # pragma: no cover resp.body = "(False, 'unable to connect to docker because: " + str(e) + "')" return # start container to get network interfaces nics = '' try: nics = d_client.containers.run('cyberreboot/gonet', network_mode='host', remove=True) except Exception as e: # pragma: no cover resp.body = "(False, 'Failure because: " + str(e) + "')" return resp.body = nics return
[docs]class StartR(object): """ This endpoint is for starting a network tap filter container """
[docs] def on_post(self, req, resp): """ Send a POST request with a docker container ID and it will be started. Example input: {'id': "12345"}, {'id': ["123", "456"]} """ resp.content_type = falcon.MEDIA_TEXT resp.status = falcon.HTTP_200 # verify user input payload = {} if req.content_length: try: payload = json.load(req.stream) except Exception as e: # pragma: no cover resp.body = 'malformed payload' return else: resp.body = 'malformed payload' return # verify payload has a container ID if 'id' not in payload: resp.body = "(False, 'payload missing container id')" return # connect to docker and stop the given container c = None try: c = docker.from_env() except Exception as e: # pragma: no cover resp.body = "(False, 'unable to connect to docker because: " + str(e) + "')" return # start containers chosen from CLI try: for container_id in payload['id']: c.containers.get(container_id).start() except Exception as e: # pragma: no cover resp.body = "(False, 'unable to start list of containers because: " + str(e) + "')" return resp.body = "(True, 'container successfully started: " + \ str(payload['id']) + "')" return
[docs]class StopR(object): """ This endpoint is for stopping a network tap filter container """
[docs] def on_post(self, req, resp): """ Send a POST request with a docker container ID and it will be stopped. Example input: {'id': "12345"}, {'id': ["123", "456"] """ resp.content_type = falcon.MEDIA_TEXT resp.status = falcon.HTTP_200 # verify user input payload = {} if req.content_length: try: payload = json.load(req.stream) except Exception as e: # pragma: no cover resp.body = 'malformed payload' return else: resp.body = 'malformed payload' return # verify payload has a container ID if 'id' not in payload: resp.body = "(False, 'payload missing container id')" return # connect to docker and stop the given container c = None try: c = docker.from_env() except Exception as e: # pragma: no cover resp.body = "(False, 'unable to connect to docker because: " + str(e) + "')" return # stop containers chosen from CLI try: for container_id in payload['id']: c.containers.get(container_id).stop() except Exception as e: # pragma: no cover resp.body = "(False, 'unable to stop list of containers because: " + str(e) + "')" return resp.body = "(True, 'container successfully stopped: " + \ str(payload['id']) + "')" return
[docs]class UpdateR(object): """ This endpoint is for updating a filter """
[docs] def on_post(self, req, resp): """ Send a POST request with id and metadata and it will update the existing filter metadata with those specifications """ resp.content_type = falcon.MEDIA_TEXT resp.status = falcon.HTTP_200 # verify payload is in the correct format payload = {} if req.content_length: try: payload = json.load(req.stream) except Exception as e: # pragma: no cover resp.body = 'malformed payload' return else: resp.body = 'malformed payload' return # payload should have the following fields: # - id # - metadata # verify payload has necessary information if 'id' not in payload: resp.body = 'payload missing id' return if 'metadata' not in payload: resp.body = 'payload missing metadata' return # connect to redis r = None try: r = redis.StrictRedis(host='redis', port=6379, db=0) except Exception as e: # pragma: no cover try: r = redis.StrictRedis(host='localhost', port=6379, db=0) except Exception as e: # pragma: no cover resp.body = "(False, 'unable to connect to redis because: " + str(e) + "')" return if r: metadata = {} try: metadata = ast.literal_eval(payload['metadata']) except Exception as e: # pragma: no cover resp.body = "(False, 'unable to convert metadata [ " + str( payload['metadata']) + ' ] into a dict because: ' + str(e) + "')" return try: redis_metadata = {} for key in metadata: redis_metadata[key] = str(metadata[key]) r.hmset(payload['id'], redis_metadata) r.hmset(metadata['endpoint_data']['mac'], {'poseidon_hash': payload['id']}) r.sadd('mac_addresses', metadata['endpoint_data']['mac']) if metadata['endpoint_data']['ipv4'] != 'None' and metadata['endpoint_data']['ipv4']: r.hmset(metadata['endpoint_data']['ipv4'], {'poseidon_hash': payload['id']}) r.sadd('ip_addresses', metadata['endpoint_data']['ipv4']) if metadata['endpoint_data']['ipv6'] != 'None' and metadata['endpoint_data']['ipv6']: r.hmset(metadata['endpoint_data']['ipv6'], {'poseidon_hash': payload['id']}) r.sadd('ip_addresses', metadata['endpoint_data']['ipv6']) except Exception as e: # pragma: no cover resp.body = "(False, 'unable to store contents of the payload " + str( metadata) + ' in redis because: ' + str(e) + "')" return resp.body = "(True, 'successfully updated filter: " + \ str(payload['id']) + "')" return