def main(): """Script main function. Does logging initialization and command line arguments parsing. """ # Initialize script logger log_file = os.path.join(LOG_DIR, SCRIPT_NAME + ".log") formatter = logging.Formatter( "%(asctime)s - %(name)s - %(levelname)s - %(message)s") logger_file_handler = logging.FileHandler(log_file) logger_file_handler.setLevel(logging.DEBUG) logger_file_handler.setFormatter(formatter) logger = logging.getLogger(SCRIPT_NAME) logger.setLevel(LOG_LEVEL) logger.addHandler(logger_file_handler) logger.info("====== %s initialization ======", SCRIPT_NAME) logger.debug("sys.path: %s", str(sys.path)) # Parse command line arguments parser = argparse.ArgumentParser(description="ifttt proxy for lan pc") parser.add_argument( "-t", "--target", required=True, help="mac address of the target machine on the lan (00:00:00:00:00:00") parser.add_argument( "-p", "--port", default=33000, type=int, help="listening port for the http server (default: 33000)") parser.add_argument("-V", "--version", action="version", version='%(prog)s ' + __version__ + ' - ' + __date__, help="show current version of the script") args = parser.parse_args() logger.info("Starting with port: %i and target: %s", args.port, args.target) # Initialize the api api = FlaskApi(__name__, args.port, args.target, logger) # Notify systemd that we are ready n = SystemdNotifier() n.notify("READY=1") # Run the api api.run()
def run(): """ Runs the Peekaboo daemon. """ arg_parser = ArgumentParser( description= 'Peekaboo Extended Email Attachment Behavior Observation Owl') arg_parser.add_argument('-c', '--config', action='store', help='The configuration file for Peekaboo.') arg_parser.add_argument( '-d', '--debug', action='store_true', help= "Run Peekaboo in debug mode regardless of what's specified in the configuration." ) arg_parser.add_argument( '-D', '--daemon', action='store_true', help= 'Run Peekaboo in daemon mode (suppresses the logo to be written to STDOUT).' ) args = arg_parser.parse_args() print('Starting Peekaboo %s.' % __version__) if not args.daemon: print(PEEKABOO_OWL) # Check if CLI arguments override the configuration log_level = None if args.debug: log_level = logging.DEBUG try: config = PeekabooConfig(config_file=args.config, log_level=log_level) logger.debug(config) except PeekabooConfigException as error: logging.critical(error) sys.exit(1) # find localisation in our package directory locale_domain = 'peekaboo' locale_dir = os.path.join(os.path.dirname(__file__), 'locale') languages = None if config.report_locale: logger.debug('Looking for translations for preconfigured locale "%s"', config.report_locale) languages = [config.report_locale] if not gettext.find(locale_domain, locale_dir, languages): logger.warning('Translation file not found - falling back to ' 'system configuration.') languages = None logger.debug('Installing report message translations') translation = gettext.translation(locale_domain, locale_dir, languages, fallback=True) # python2's gettext needs to be told explicitly to return unicode strings loc_kwargs = {} if sys.version_info[0] < 3: loc_kwargs = {'unicode': True} translation.install(loc_kwargs) # establish a connection to the database try: db_con = PeekabooDatabase( db_url=config.db_url, instance_id=config.cluster_instance_id, stale_in_flight_threshold=config.cluster_stale_in_flight_threshold, log_level=config.db_log_level) except PeekabooDatabaseError as error: logging.critical(error) sys.exit(1) except SQLAlchemyError as dberr: logger.critical( 'Failed to establish a connection to the database ' 'at %s: %s', config.db_url, dberr) sys.exit(1) # Import debug module if we are in debug mode debugger = None if config.use_debug_module: from peekaboo.debug import PeekabooDebugger debugger = PeekabooDebugger() debugger.start() # initialize the daemon infrastructure such as PID file and dropping # privileges, automatically cleans up after itself when going out of scope daemon_infrastructure = PeekabooDaemonInfrastructure( config.pid_file, config.sock_file, config.user, config.group) daemon_infrastructure.init() systemd = SystemdNotifier() # clear all our in flight samples and all instances' stale in flight # samples db_con.clear_in_flight_samples() db_con.clear_stale_in_flight_samples() # a cluster duplicate interval of 0 disables the handler thread which is # what we want if we don't have an instance_id and therefore are alone cldup_check_interval = 0 if config.cluster_instance_id > 0: cldup_check_interval = config.cluster_duplicate_check_interval if cldup_check_interval < 5: cldup_check_interval = 5 logger.warning( "Raising excessively low cluster duplicate check " "interval to %d seconds.", cldup_check_interval) # workers of the job queue need the ruleset configuration to create the # ruleset engine with it try: ruleset_config = PeekabooConfigParser(config.ruleset_config) except PeekabooConfigException as error: logging.critical(error) sys.exit(1) # verify the ruleset configuration by spawning a ruleset engine and having # it verify it try: engine = RulesetEngine(ruleset_config, db_con) except (KeyError, ValueError, PeekabooConfigException) as error: logging.critical('Ruleset configuration error: %s', error) sys.exit(1) except PeekabooRulesetConfigError as error: logging.critical(error) sys.exit(1) job_queue = JobQueue(worker_count=config.worker_count, ruleset_config=ruleset_config, db_con=db_con, cluster_duplicate_check_interval=cldup_check_interval) if config.cuckoo_mode == "embed": cuckoo = CuckooEmbed(job_queue, config.cuckoo_exec, config.cuckoo_submit, config.cuckoo_storage, config.interpreter) # otherwise it's the new API method and default else: cuckoo = CuckooApi(job_queue, config.cuckoo_url, config.cuckoo_api_token, config.cuckoo_poll_interval) sig_handler = SignalHandler() sig_handler.register_listener(cuckoo) # Factory producing almost identical samples providing them with global # config values and references to other objects they need, such as cuckoo, # database connection and connection map. sample_factory = SampleFactory(cuckoo, config.sample_base_dir, config.job_hash_regex, config.keep_mail_data, config.processing_info_dir) # We only want to accept 2 * worker_count connections. try: server = PeekabooServer(sock_file=config.sock_file, job_queue=job_queue, sample_factory=sample_factory, request_queue_size=config.worker_count * 2) except Exception as error: logger.critical('Failed to start Peekaboo Server: %s', error) job_queue.shut_down() if debugger is not None: debugger.shut_down() sys.exit(1) exit_code = 1 try: systemd.notify("READY=1") # If this dies Peekaboo dies, since this is the main thread. (legacy) exit_code = cuckoo.do() except Exception as error: logger.critical('Main thread aborted: %s', error) finally: server.shutdown() job_queue.shut_down() try: db_con.clear_in_flight_samples() db_con.clear_stale_in_flight_samples() except PeekabooDatabaseError as dberr: logger.error(dberr) if debugger is not None: debugger.shut_down() sys.exit(exit_code)
def run(): """ Runs the Peekaboo daemon. """ arg_parser = ArgumentParser( description= 'Peekaboo Extended Email Attachment Behavior Observation Owl') arg_parser.add_argument('-c', '--config', action='store', required=False, default=os.path.join('./peekaboo.conf'), help='The configuration file for Peekaboo.') arg_parser.add_argument( '-d', '--debug', action='store_true', required=False, default=False, help= "Run Peekaboo in debug mode regardless of what's specified in the configuration." ) arg_parser.add_argument( '-D', '--daemon', action='store_true', required=False, default=False, help= 'Run Peekaboo in daemon mode (suppresses the logo to be written to STDOUT).' ) args = arg_parser.parse_args() if not args.daemon: print(_owl) else: print('Starting Peekaboo %s.' % __version__) # read configuration if not os.path.isfile(args.config): print('Failed to read config, files does not exist.' ) # logger doesn't exist here sys.exit(1) config = parse_config(args.config) # Check if CLI arguments override the configuration if args.debug: config.change_log_level('DEBUG') # Log the configuration options if we are in debug mode if config.log_level == logging.DEBUG: logger.debug(config.__str__()) # establish a connection to the database try: db_con = PeekabooDatabase(config.db_url) config.add_db_con(db_con) except PeekabooDatabaseError as e: logging.exception(e) sys.exit(1) except Exception as e: logger.critical('Failed to establish a connection to the database.') logger.exception(e) sys.exit(1) # Import debug module if we are in debug mode if config.use_debug_module: from peekaboo.debug import peekaboo_debugger peekaboo_debugger() if os.getuid() == 0: logger.warning('Peekaboo should not run as root.') # drop privileges to user os.setgid(grp.getgrnam(config.group)[2]) os.setuid(pwd.getpwnam(config.user)[2]) # set $HOME to the users home directory # (VirtualBox must access the configs) os.environ['HOME'] = pwd.getpwnam(config.user)[5] logger.info("Dropped privileges to user %s and group %s" % (config.user, config.group)) logger.debug('$HOME is ' + os.environ['HOME']) # write PID file pid = str(os.getpid()) with open(config.pid_file, "w") as pidfile: pidfile.write("%s\n" % pid) systemd = SystemdNotifier() server = PeekabooStreamServer(config.sock_file, PeekabooStreamRequestHandler) runner = Thread(target=server.serve_forever) runner.daemon = True try: runner.start() logger.info('Peekaboo server is listening on %s' % server.server_address) os.chmod( config.sock_file, stat.S_IWOTH | stat.S_IREAD | stat.S_IWRITE | stat.S_IRGRP | stat.S_IWGRP | stat.S_IWOTH) # Run Cuckoo sandbox, parse log output, and report back of Peekaboo. # If this dies Peekaboo dies, since this is the main thread. srv = CuckooServer() reactor.spawnProcess(srv, config.interpreter, [config.interpreter, '-u', config.cuckoo_exec]) systemd.notify("READY=1") reactor.run() except Exception as e: logger.exception(e) finally: server.shutdown()