Esempio n. 1
0
    def config_subscriber(self):
        ctx = zmq.Context()
        subscriber_config = ctx.socket(zmq.SUB)
        subscriber_config.connect('ipc://configPublisher')
        subscriber_config.setsockopt(zmq.SUBSCRIBE, '')
        send_zmq_request('ipc://configCommands', Messages.PUBLISH_CONFIG)

        while True:
            poller = zmq.Poller()
            poller.register(subscriber_config, zmq.POLLIN)
            while True:
                socks = dict(poller.poll())
                if subscriber_config in socks and socks[subscriber_config] == zmq.POLLIN:
                    topic, msg = subscriber_config.recv().split(' ', 1)
                    self.config = json.loads(msg)
                    self.first_cfg_received.set()
                    logger.debug('Config received')
Esempio n. 2
0
def drone_key(key):
    global drone_keys
    if key not in drone_keys:
        logger.warn('Attempt to add new drone, but using wrong key from: {0}'.format(request.remote_addr))
        abort(401)
    else:
        drone_id = str(uuid.uuid4())
        server_zmq_url = 'tcp://{0}:{1}'.format(config['network']['zmq_host'], config['network']['zmq_port'])
        server_zmq_command_url = 'tcp://{0}:{1}'.format(config['network']['zmq_host'], config['network']['zmq_command_port'])
        result = send_zmq_request('ipc://configCommands', '{0} {1}'.format(Messages.GEN_ZMQ_KEYS, drone_id))
        zmq_public = result['public_key']
        zmq_private = result['private_key']
        db_session = database_setup.get_session()
        # this could be done even simpler by only using the http api to provide the ZMQ keys
        # everything else could be done using zmq
        drone_config = {
            'general': {
                'mode': '',
                'id': drone_id,
                'fetch_ip': False
            },
            'beeswarm_server': {
                'enabled': True,
                'zmq_url': server_zmq_url,
                'zmq_command_url' : server_zmq_command_url,
                'zmq_server_public': config['network']['zmq_server_public_key'],
                'zmq_own_public': zmq_public,
                'zmq_own_private': zmq_private,
            }
        }

        config_json = json.dumps(drone_config, indent=4)
        drone = Drone(id=drone_id, configuration=config_json)
        db_session.add(drone)
        db_session.commit()
        logger.debug('Generated drone config for {0} on request from {1}'.format(drone_id, request.remote_addr))
        return config_json
Esempio n. 3
0
def create_client():
    form = NewClientConfigForm()
    client_id = str(uuid.uuid4())
    if form.validate_on_submit():
        with open(app.config['CERT_PATH']) as cert:
            cert_str = cert.read()
        server_zmq_url = 'tcp://{0}:{1}'.format(config['network']['zmq_host'], config['network']['zmq_port'])
        result = send_zmq_request('ipc://configCommands', '{0} {1}'.format(Messages.GEN_ZMQ_KEYS, client_id))
        zmq_public = result['public_key']
        zmq_private = result['private_key']

        client_config = {
            'general': {
                'mode': 'client',
                'client_id': client_id,
                'honeypot_id': None
            },
            'public_ip': {
                'fetch_ip': True
            },
            'bait_sessions': {
                'http': {
                    'enabled': form.http_enabled.data,
                    'server': form.http_server.data,
                    'port': form.http_port.data,
                    'timing': {
                        'active_range': form.http_active_range.data,
                        'sleep_interval': form.http_sleep_interval.data,
                        'activation_probability': form.http_activation_probability.data
                    },
                    'username': form.http_login.data,
                    'password': form.http_password.data
                },
                'ftp': {
                    'enabled': form.ftp_enabled.data,
                    'server': form.ftp_server.data,
                    'port': form.ftp_port.data,
                    'timing': {
                        'active_range': form.ftp_active_range.data,
                        'sleep_interval': form.ftp_sleep_interval.data,
                        'activation_probability': form.ftp_activation_probability.data
                    },
                    'username': form.ftp_login.data,
                    'password': form.ftp_password.data
                },
                'https': {
                    'enabled': form.https_enabled.data,
                    'server': form.https_server.data,
                    'port': form.https_port.data,
                    'timing': {
                        'active_range': form.https_active_range.data,
                        'sleep_interval': form.https_sleep_interval.data,
                        'activation_probability': form.https_activation_probability.data
                    },
                    'username': form.https_login.data,
                    'password': form.https_password.data
                },
                'pop3': {
                    'enabled': form.pop3_enabled.data,
                    'server': form.pop3_server.data,
                    'port': form.pop3_port.data,
                    'timing': {
                        'active_range': form.pop3_active_range.data,
                        'sleep_interval': form.pop3_sleep_interval.data,
                        'activation_probability': form.pop3_activation_probability.data
                    },
                    'username': form.pop3_login.data,
                    'password': form.pop3_password.data
                },
                'ssh': {
                    'enabled': form.ssh_enabled.data,
                    'server': form.ssh_server.data,
                    'port': form.ssh_port.data,
                    'timing': {
                        'active_range': form.ssh_active_range.data,
                        'sleep_interval': form.ssh_sleep_interval.data,
                        'activation_probability': form.ssh_activation_probability.data
                    },
                    'username': form.ssh_login.data,
                    'password': form.ssh_password.data
                },
                'pop3s': {
                    'enabled': form.pop3s_enabled.data,
                    'server': form.pop3s_server.data,
                    'port': form.pop3s_port.data,
                    'timing': {
                        'active_range': form.pop3s_active_range.data,
                        'sleep_interval': form.pop3s_sleep_interval.data,
                        'activation_probability': form.pop3s_activation_probability.data
                    },
                    'username': form.pop3s_login.data,
                    'password': form.pop3s_password.data
                },
                'smtp': {
                    'enabled': form.smtp_enabled.data,
                    'server': form.smtp_server.data,
                    'port': form.smtp_port.data,
                    'timing': {
                        'active_range': form.smtp_active_range.data,
                        'sleep_interval': form.smtp_sleep_interval.data,
                        'activation_probability': form.smtp_activation_probability.data
                    },
                    'username': form.smtp_login.data,
                    'local_hostname': form.smtp_local_hostname.data,
                    'password': form.smtp_password.data
                },
                'vnc': {
                    'enabled': form.vnc_enabled.data,
                    'server': form.vnc_server.data,
                    'port': form.vnc_port.data,
                    'timing': {
                        'active_range': form.vnc_active_range.data,
                        'sleep_interval': form.vnc_sleep_interval.data,
                        'activation_probability': form.vnc_activation_probability.data
                    },
                    'username': form.vnc_login.data,
                    'password': form.vnc_password.data
                },
                'telnet': {
                    'enabled': form.telnet_enabled.data,
                    'server': form.telnet_server.data,
                    'port': form.telnet_port.data,
                    'timing': {
                        'active_range': form.telnet_active_range.data,
                        'sleep_interval': form.telnet_sleep_interval.data,
                        'activation_probability': form.telnet_activation_probability.data
                    },
                    'username': form.telnet_login.data,
                    'password': form.telnet_password.data
                }
            },
            'beeswarm_server': {
                'enabled': True,
                'zmq_url' : server_zmq_url,
                'zmq_server_public': config['network']['zmq_server_public_key'],
                'zmq_own_public': zmq_public,
                'zmq_own_private': zmq_private,
            },
        }
        config_json = json.dumps(client_config, indent=4)

        db_session = database_setup.get_session()
        f = Client(id=client_id, configuration=config_json)
        db_session.add(f)
        db_session.commit()
        return render_template('finish-config.html', mode_name='Client', user=current_user)

    return render_template('create-client.html', form=form, mode_name='Client', user=current_user)
Esempio n. 4
0
def configure_drone(id):
    db_session = database_setup.get_session()
    drone = db_session.query(Drone).filter(Drone.id == id).one()
    if drone.discriminator == 'honeypot':
        if drone.configuration is not None:
            config_obj = DictWrapper(json.loads(drone.configuration))
        else:
            # virgin drone
            config_obj = None
        form = NewHoneypotConfigForm(obj=config_obj)
        if not form.validate_on_submit():
            return render_template('create-honeypot.html', form=form, mode_name='Honeypot', user=current_user)
        else:
            server_zmq_command_url = 'tcp://{0}:{1}'.format(config['network']['zmq_host'], config['network']['zmq_command_port'])
            server_zmq_url = 'tcp://{0}:{1}'.format(config['network']['zmq_host'], config['network']['zmq_port'])
            # TODO: Check if key pair exists
            result = send_zmq_request('ipc://configCommands', '{0} {1}'.format(Messages.GEN_ZMQ_KEYS, str(drone.id)))
            zmq_public = result['public_key']
            zmq_private = result['private_key']

            drone_config = {
            'general': {
                'name': form.general__name.data,
                'mode': 'honeypot',
                'id': drone.id,
                'ip': '192.168.1.1',
                'fetch_ip': False
            },
            'beeswarm_server': {
                'enabled': True,
                'zmq_url' : server_zmq_url,
                'zmq_server_public': config['network']['zmq_server_public_key'],
                'zmq_own_public': zmq_public,
                'zmq_own_private': zmq_private,
                'zmq_command_url': server_zmq_command_url,
            },
            'log_syslog': {
                'enabled': False,
                'socket': '/dev/log'
            },
            'certificate_info': {
                'common_name': form.certificate_info__common_name.data,
                'country': form.certificate_info__country.data,
                'state': form.certificate_info__state.data,
                'locality': form.certificate_info__locality.data,
                'organization': form.certificate_info__organization.data,
                'organization_unit': form.certificate_info__organization_unit.data
            },
            'capabilities': {
                'ftp': {
                    'enabled': form.capabilities__ftp__enabled.data,
                    'port': form.capabilities__ftp__port.data,
                    'max_attempts': form.capabilities__ftp__max_attempts.data,
                    'banner': form.capabilities__ftp__banner.data,
                    'syst_type': form.capabilities__ftp__syst_type.data
                },
                'telnet': {
                    'enabled': form.capabilities__telnet__enabled.data,
                    'port': form.capabilities__telnet__port.data,
                    'max_attempts': form.capabilities__telnet__max_attempts.data
                },
                'pop3': {
                    'enabled': form.capabilities__pop3__enabled.data,
                    'port': form.capabilities__pop3__port.data,
                    'max_attempts': form.capabilities__pop3__max_attempts.data,
                },
                'pop3s': {
                    'enabled': form.capabilities__pop3s__enabled.data,
                    'port': form.capabilities__pop3s__port.data,
                    'max_attempts': form.capabilities__pop3s__max_attempts.data,
                },
                'ssh': {
                    'enabled': form.capabilities__ssh__enabled.data,
                    'port': form.capabilities__ssh__port.data,
                },
                'http': {
                    'enabled': form.capabilities__http__enabled.data,
                    'port': form.capabilities__http__port.data,
                    'banner': form.capabilities__http__banner.data
                },
                'https': {
                    'enabled': form.capabilities__https__enabled.data,
                    'port': form.capabilities__https__port.data,
                    'banner': form.capabilities__https__banner.data
                },
                'smtp': {
                    'enabled': form.capabilities__smtp__enabled.data,
                    'port': form.capabilities__smtp__port.data,
                    'banner': form.capabilities__smtp__banner.data
                },
                'vnc': {
                    'enabled': form.capabilities__vnc__enabled.data,
                    'port': form.capabilities__vnc__port.data
                }
            },
            'users': {},
            'timecheck': {
                'enabled': True,
                'poll': 5,
                'ntp_pool': 'pool.ntp.org'
            },
        }
            config_json = json.dumps(drone_config, indent=4)
            drone.name = form.general__name.data
            drone.configuration = config_json
            db_session.add(drone)
            db_session.commit()

            # everything good, push config to drone if it is listening
            send_zmq_push('ipc://droneCommandReceiver', '{0} {1} {2}'.format(drone.id, Messages.CONFIG, config_json))
            return render_template('finish-config.html', drone_id=drone.id, user=current_user)
    elif drone.discriminator == 'client':
        form = NewClientConfigForm()
        if not form.validate_on_submit():
            return render_template('create-client.html', form=form, mode_name='Honeypot', user=current_user)
        else:
            server_zmq_url = 'tcp://{0}:{1}'.format(config['network']['zmq_host'], config['network']['zmq_port'])
            server_zmq_command_url = 'tcp://{0}:{1}'.format(config['network']['zmq_host'], config['network']['zmq_command_port'])
            # TODO: Check if key pair exists
            result = send_zmq_request('ipc://configCommands', '{0} {1}'.format(Messages.GEN_ZMQ_KEYS, str(drone.id)))
            zmq_public = result['public_key']
            zmq_private = result['private_key']

            drone_config = {
                'general': {
                    'mode': 'client',
                    'id': drone.id,
                    'honeypot_id': None
                },
                'public_ip': {
                    'fetch_ip': True
                },
                'bait_sessions': {
                    'http': {
                        'enabled': form.http_enabled.data,
                        'server': form.http_server.data,
                        'port': form.http_port.data,
                        'timing': {
                            'active_range': form.http_active_range.data,
                            'sleep_interval': form.http_sleep_interval.data,
                            'activation_probability': form.http_activation_probability.data
                        },
                        'username': form.http_login.data,
                        'password': form.http_password.data
                    },
                    'ftp': {
                        'enabled': form.ftp_enabled.data,
                        'server': form.ftp_server.data,
                        'port': form.ftp_port.data,
                        'timing': {
                            'active_range': form.ftp_active_range.data,
                            'sleep_interval': form.ftp_sleep_interval.data,
                            'activation_probability': form.ftp_activation_probability.data
                        },
                        'username': form.ftp_login.data,
                        'password': form.ftp_password.data
                    },
                    'https': {
                        'enabled': form.https_enabled.data,
                        'server': form.https_server.data,
                        'port': form.https_port.data,
                        'timing': {
                            'active_range': form.https_active_range.data,
                            'sleep_interval': form.https_sleep_interval.data,
                            'activation_probability': form.https_activation_probability.data
                        },
                        'username': form.https_login.data,
                        'password': form.https_password.data
                    },
                    'pop3': {
                        'enabled': form.pop3_enabled.data,
                        'server': form.pop3_server.data,
                        'port': form.pop3_port.data,
                        'timing': {
                            'active_range': form.pop3_active_range.data,
                            'sleep_interval': form.pop3_sleep_interval.data,
                            'activation_probability': form.pop3_activation_probability.data
                        },
                        'username': form.pop3_login.data,
                        'password': form.pop3_password.data
                    },
                    'ssh': {
                        'enabled': form.ssh_enabled.data,
                        'server': form.ssh_server.data,
                        'port': form.ssh_port.data,
                        'timing': {
                            'active_range': form.ssh_active_range.data,
                            'sleep_interval': form.ssh_sleep_interval.data,
                            'activation_probability': form.ssh_activation_probability.data
                        },
                        'username': form.ssh_login.data,
                        'password': form.ssh_password.data
                    },
                    'pop3s': {
                        'enabled': form.pop3s_enabled.data,
                        'server': form.pop3s_server.data,
                        'port': form.pop3s_port.data,
                        'timing': {
                            'active_range': form.pop3s_active_range.data,
                            'sleep_interval': form.pop3s_sleep_interval.data,
                            'activation_probability': form.pop3s_activation_probability.data
                        },
                        'username': form.pop3s_login.data,
                        'password': form.pop3s_password.data
                    },
                    'smtp': {
                        'enabled': form.smtp_enabled.data,
                        'server': form.smtp_server.data,
                        'port': form.smtp_port.data,
                        'timing': {
                            'active_range': form.smtp_active_range.data,
                            'sleep_interval': form.smtp_sleep_interval.data,
                            'activation_probability': form.smtp_activation_probability.data
                        },
                        'username': form.smtp_login.data,
                        'local_hostname': form.smtp_local_hostname.data,
                        'password': form.smtp_password.data
                    },
                    'vnc': {
                        'enabled': form.vnc_enabled.data,
                        'server': form.vnc_server.data,
                        'port': form.vnc_port.data,
                        'timing': {
                            'active_range': form.vnc_active_range.data,
                            'sleep_interval': form.vnc_sleep_interval.data,
                            'activation_probability': form.vnc_activation_probability.data
                        },
                        'username': form.vnc_login.data,
                        'password': form.vnc_password.data
                    },
                    'telnet': {
                        'enabled': form.telnet_enabled.data,
                        'server': form.telnet_server.data,
                        'port': form.telnet_port.data,
                        'timing': {
                            'active_range': form.telnet_active_range.data,
                            'sleep_interval': form.telnet_sleep_interval.data,
                            'activation_probability': form.telnet_activation_probability.data
                        },
                        'username': form.telnet_login.data,
                        'password': form.telnet_password.data
                    }
                },
                'beeswarm_server': {
                    'enabled': True,
                    'zmq_url' : server_zmq_url,
                    'zmq_server_public': config['network']['zmq_server_public_key'],
                    'zmq_own_public': zmq_public,
                    'zmq_own_private': zmq_private,
                    'zmq_command_url': server_zmq_command_url,
                },
            }

            config_json = json.dumps(drone_config, indent=4)

            drone.configuration = config_json
            db_session.add(drone)
            db_session.commit()

            # everything good, push config to drone if it is listening
            send_zmq_push('ipc://droneCommandReceiver', '{0} {1} {2}'.format(drone.id, Messages.CONFIG, config_json))
            return render_template('finish-config.html', drone_id=drone.id, user=current_user)
    elif drone.discriminator is None:
        return render_template('drone_mode.html', drone_id=drone.id, user=current_user)
    else:
        assert(drone is None)
        abort(404, 'Drone with that id could not be found.')
Esempio n. 5
0
    def message_proxy(self, work_dir):
        """
        drone_data_inboud   is for data comming from drones
        drone_data_outbound is for commands to the drones, topic must either be a drone ID or all for sending
                            a broadcast message to all drones
        """
        public_keys_dir = os.path.join(work_dir, 'certificates', 'public_keys')
        secret_keys_dir = os.path.join(work_dir, 'certificates',
                                       'private_keys')

        # start and configure auth worker
        auth = IOLoopAuthenticator()
        auth.start()
        auth.allow('127.0.0.1')
        auth.configure_curve(domain='*', location=public_keys_dir)

        # external interfaces for communicating with drones
        server_secret_file = os.path.join(secret_keys_dir,
                                          'beeswarm_server.pri')
        server_public, server_secret = load_certificate(server_secret_file)
        drone_data_inbound = beeswarm.shared.zmq_context.socket(zmq.PULL)
        drone_data_inbound.curve_secretkey = server_secret
        drone_data_inbound.curve_publickey = server_public
        drone_data_inbound.curve_server = True
        drone_data_inbound.bind('tcp://*:{0}'.format(
            self.config['network']['zmq_port']))

        drone_data_outbound = beeswarm.shared.zmq_context.socket(zmq.PUB)
        drone_data_outbound.curve_secretkey = server_secret
        drone_data_outbound.curve_publickey = server_public
        drone_data_outbound.curve_server = True
        drone_data_outbound.bind('tcp://*:{0}'.format(
            self.config['network']['zmq_command_port']))

        # internal interfaces
        # all inbound session data from drones will be replayed in this socket
        sessionPublisher = beeswarm.shared.zmq_context.socket(zmq.PUB)
        sessionPublisher.bind('inproc://sessionPublisher')

        # all commands received on this will be published on the external interface
        drone_command_receiver = beeswarm.shared.zmq_context.socket(zmq.PULL)
        drone_command_receiver.bind('inproc://droneCommandReceiver')

        poller = zmq.Poller()
        poller.register(drone_data_inbound, zmq.POLLIN)
        poller.register(drone_command_receiver, zmq.POLLIN)
        while True:
            # .recv() gives no context switch - why not? using poller with timeout instead
            socks = dict(poller.poll(100))
            gevent.sleep()

            if drone_command_receiver in socks and socks[
                    drone_command_receiver] == zmq.POLLIN:
                data = drone_command_receiver.recv()
                drone_id, _ = data.split(' ', 1)
                logger.debug("Sending drone command to: {0}".format(drone_id))
                # pub socket takes care of filtering
                drone_data_outbound.send(data)
            elif drone_data_inbound in socks and socks[
                    drone_data_inbound] == zmq.POLLIN:
                split_data = drone_data_inbound.recv().split(' ', 2)
                if len(split_data) == 3:
                    topic, drone_id, data = split_data
                else:
                    data = None
                    topic, drone_id, = split_data
                logger.debug("Received {0} message from {1}.".format(
                    topic, drone_id))
                db_session = database_setup.get_session()
                drone = db_session.query(Drone).filter(
                    Drone.id == drone_id).one()
                drone.last_activity = datetime.now()
                db_session.add(drone)
                db_session.commit()

                if topic == Messages.SESSION_HONEYPOT or topic == Messages.SESSION_CLIENT:
                    sessionPublisher.send('{0} {1}'.format(topic, data))
                elif topic == Messages.KEY or topic == Messages.CERT:
                    # for now we just store the fingerprint
                    # in the future it might be relevant to store the entire public key and private key
                    # for forensic purposes
                    if topic == Messages.CERT:
                        cert = data.split(' ', 1)[1]
                        digest = generate_cert_digest(cert)
                        logging.debug(
                            'Storing public key digest: {0} for drone {1}.'.
                            format(digest, drone_id))
                        db_session = database_setup.get_session()
                        drone = db_session.query(Drone).filter(
                            Drone.id == drone_id).one()
                        drone.cert_digest = digest
                        db_session.add(drone)
                        db_session.commit()
                elif topic == Messages.PING:
                    pass
                elif topic == Messages.IP:
                    ip_address = data
                    logging.debug('Drone {0} reported ip: {1}'.format(
                        drone_id, ip_address))
                    db_session = database_setup.get_session()
                    drone = db_session.query(Drone).filter(
                        Drone.id == drone_id).one()
                    if drone.ip_address != ip_address:
                        drone.ip_address = ip_address
                        db_session.add(drone)
                        db_session.commit()
                        send_zmq_request(
                            'inproc://configCommands',
                            '{0} {1}'.format(Messages.DRONE_CONFIG_CHANGED,
                                             drone_id))
                # drone want it's config transmitted
                elif topic == Messages.DRONE_CONFIG:
                    config_dict = send_zmq_request(
                        'inproc://configCommands',
                        '{0} {1}'.format(Messages.DRONE_CONFIG, drone_id))
                    drone_data_outbound.send('{0} {1} {2}'.format(
                        drone_id, Messages.CONFIG, json.dumps(config_dict)))
                else:
                    logger.warn(
                        'Message with unknown topic received: {0}'.format(
                            topic))
Esempio n. 6
0
    def message_proxy(self, work_dir):
        """
        drone_data_inboud   is for data comming from drones
        drone_data_outbound is for commands to the drones, topic must either be a drone ID or all for sending
                            a broadcast message to all drones
        """
        public_keys_dir = os.path.join(work_dir, 'certificates', 'public_keys')
        secret_keys_dir = os.path.join(work_dir, 'certificates', 'private_keys')

        # start and configure auth worker
        auth = IOLoopAuthenticator()
        auth.start()
        auth.allow('127.0.0.1')
        auth.configure_curve(domain='*', location=public_keys_dir)


        # external interfaces for communicating with drones
        server_secret_file = os.path.join(secret_keys_dir, 'beeswarm_server.pri')
        server_public, server_secret = load_certificate(server_secret_file)
        drone_data_inbound = beeswarm.shared.zmq_context.socket(zmq.PULL)
        drone_data_inbound.curve_secretkey = server_secret
        drone_data_inbound.curve_publickey = server_public
        drone_data_inbound.curve_server = True
        drone_data_inbound.bind('tcp://*:{0}'.format(self.config['network']['zmq_port']))

        drone_data_outbound = beeswarm.shared.zmq_context.socket(zmq.PUB)
        drone_data_outbound.curve_secretkey = server_secret
        drone_data_outbound.curve_publickey = server_public
        drone_data_outbound.curve_server = True
        drone_data_outbound.bind('tcp://*:{0}'.format(self.config['network']['zmq_command_port']))

        # internal interfaces
        # all inbound session data from drones will be replayed in this socket
        sessionPublisher = beeswarm.shared.zmq_context.socket(zmq.PUB)
        sessionPublisher.bind('inproc://sessionPublisher')

        # all commands received on this will be published on the external interface
        drone_command_receiver = beeswarm.shared.zmq_context.socket(zmq.PULL)
        drone_command_receiver.bind('inproc://droneCommandReceiver')

        poller = zmq.Poller()
        poller.register(drone_data_inbound, zmq.POLLIN)
        poller.register(drone_command_receiver, zmq.POLLIN)
        while True:
            # .recv() gives no context switch - why not? using poller with timeout instead
            socks = dict(poller.poll(100))
            gevent.sleep()

            if drone_command_receiver in socks and socks[drone_command_receiver] == zmq.POLLIN:
                data = drone_command_receiver.recv()
                drone_id, _ = data.split(' ', 1)
                logger.debug("Sending drone command to: {0}".format(drone_id))
                # pub socket takes care of filtering
                drone_data_outbound.send(data)
            elif drone_data_inbound in socks and socks[drone_data_inbound] == zmq.POLLIN:
                split_data = drone_data_inbound.recv().split(' ', 2)
                if len(split_data) == 3:
                    topic, drone_id, data = split_data
                else:
                    data = None
                    topic, drone_id, = split_data
                logger.debug("Received {0} message from {1}.".format(topic, drone_id))
                db_session = database_setup.get_session()
                drone = db_session.query(Drone).filter(Drone.id == drone_id).one()
                drone.last_activity = datetime.now()
                db_session.add(drone)
                db_session.commit()

                if topic == Messages.SESSION_HONEYPOT or topic == Messages.SESSION_CLIENT:
                    sessionPublisher.send('{0} {1}'.format(topic, data))
                elif topic == Messages.KEY or topic == Messages.CERT:
                    # for now we just store the fingerprint
                    # in the future it might be relevant to store the entire public key and private key
                    # for forensic purposes
                    if topic == Messages.CERT:
                        cert = data.split(' ', 1)[1]
                        digest = generate_cert_digest(cert)
                        logging.debug('Storing public key digest: {0} for drone {1}.'.format(digest, drone_id))
                        db_session = database_setup.get_session()
                        drone = db_session.query(Drone).filter(Drone.id == drone_id).one()
                        drone.cert_digest = digest
                        db_session.add(drone)
                        db_session.commit()
                elif topic == Messages.PING:
                    pass
                elif topic == Messages.IP:
                    ip_address = data
                    logging.debug('Drone {0} reported ip: {1}'.format(drone_id, ip_address))
                    db_session = database_setup.get_session()
                    drone = db_session.query(Drone).filter(Drone.id == drone_id).one()
                    if drone.ip_address != ip_address:
                        drone.ip_address = ip_address
                        db_session.add(drone)
                        db_session.commit()
                        send_zmq_request('inproc://configCommands', '{0} {1}'.format(Messages.DRONE_CONFIG_CHANGED,
                                                                                     drone_id))
                # drone want it's config transmitted
                elif topic == Messages.DRONE_CONFIG:
                    config_dict = send_zmq_request('inproc://configCommands', '{0} {1}'.format(Messages.DRONE_CONFIG,
                                                                                            drone_id))
                    drone_data_outbound.send('{0} {1} {2}'.format(drone_id, Messages.CONFIG, json.dumps(config_dict)))
                else:
                    logger.warn('Message with unknown topic received: {0}'.format(topic))