def _handle_cert_message(self, topic, drone_id, data): # 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 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() if drone: drone.cert_digest = digest db_session.add(drone) db_session.commit() else: logger.warning('Trying to update cert on non-exting drone with id {0}'.format(drone_id))
def message_proxy(self, work_dir): """ drone_data_inboud is for data comming from drones drone_data_outbound is for commands to the drone, topic must either be a drone ID or all for sending a broadcast message to all drones """ ctx = zmq.Context() 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 = ctx.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 = ctx.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 = ctx.socket(zmq.PUB) sessionPublisher.bind('ipc://sessionPublisher') # all commands received on this will be published on the external interface drone_command_receiver = ctx.socket(zmq.PULL) drone_command_receiver.bind('ipc://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(1)) gevent.sleep() if drone_command_receiver in socks and socks[drone_command_receiver] == zmq.POLLIN: data = drone_command_receiver.recv() topic, _ = data.split(' ', 1) logger.debug("Sending drone command to: {0}".format(topic)) # pub socket takes care of filtering drone_data_outbound.send(data) elif drone_data_inbound in socks and socks[drone_data_inbound] == zmq.POLLIN: topic, data = drone_data_inbound.recv().split(' ', 1) logger.debug("Received {0} data.".format(topic)) 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: drone_id, cert = data.split(' ', 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: drone_id = data 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() else: logger.warn('Message with unknown topic received: {0}'.format(topic))
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))