ok
Direktori : /opt/imunify360/venv/bin/ |
Current File : //opt/imunify360/venv/bin/pam_imunify_daemon_watchdog.py |
#!/opt/imunify360/venv/bin/python3 from contextlib import suppress import gc from pathlib import Path import signal import subprocess import sys import time import psutil from pam_i360.internals import (CONFIG, getLogger, logger_init, pam_imunify_config) # to disable PAM ftp integration MAX_FAILURES_PER_HOUR = 10 FAILURES_PERIOD = 60*60 TRANSIENT_SIGLIST =(signal.SIGABRT, signal.SIGCONT, signal.SIGHUP, signal.SIGINT, signal.SIGPIPE, signal.SIGQUIT, signal.SIGUSR1, signal.SIGUSR2, signal.SIGTERM) PIDFILE = Path('/var/run/pam_imunify_daemon-watchdog.pid') logger = getLogger() def read_panic_watchdog_config(): try: failures = int(pam_imunify_config()['watchdog_limit']) except KeyError: pass except ValueError as e: logger.error("%s error 'parsing watchdog_limit=...' %s", CONFIG, e) else: global MAX_FAILURES_PER_HOUR MAX_FAILURES_PER_HOUR = failures try: period = float(pam_imunify_config()['watchdog_period']) except KeyError: pass except ValueError as e: logger.error("%s error 'parsing watchdog_period=...' %s", CONFIG, e) else: global FAILURES_PERIOD FAILURES_PERIOD = period return def disable_pamftp_intergation(): cmd = ['/usr/bin/imunify360-agent', 'config', 'update', '{"PAM": {"ftp_protection": false}}'] try: rc = subprocess.check_call(cmd) except (OSError, subprocess.CalledProcessError) as e: logger.exception("disable_pamftp_intergation error") def wire_signals(reroute_to: subprocess.Popen=None, undo=False): def child_proc_send(sig, _): reroute_to.transient_signal = sig try: reroute_to.send_signal(sig) except ProcessLookupError: logger.exception("child process died unexpectedly") for sig in TRANSIENT_SIGLIST: signal.signal(sig, signal.SIG_DFL if undo else child_proc_send) def kill_orphaned_child(pam_imunify_daemon): """ if any. """ try: pid = int(PIDFILE.read_text()) except (FileNotFoundError, ValueError): return with suppress(psutil.NoSuchProcess): proc = psutil.Process(pid) if proc.name() != Path(pam_imunify_daemon).name: return logger.error("orphaned child process: %s", proc) proc.terminate() time.sleep(2) proc.kill() def daemon_being_watched() -> bool: pam_imunify_daemon = '%s.bin' % sys.argv[0] kill_orphaned_child(pam_imunify_daemon) child_proc = subprocess.Popen([pam_imunify_daemon] + sys.argv[1:]) child_proc.transient_signal = None PIDFILE.write_text('%s\n' % child_proc.pid) logger.info("%s has started.", pam_imunify_daemon) wire_signals(reroute_to=child_proc) gc.collect() # to minimize memory footprint while in idle state try: rc = child_proc.wait() except ProcessLookupError: logger.exception("child process died unexpectedly") return False finally: wire_signals(undo=True) if child_proc.transient_signal is None: if rc < 0: logger.error("%s died unexpectedly (killed by %d).", pam_imunify_daemon, rc) else: logger.error("%s stopped unexpectedly (exit code %d).", pam_imunify_daemon, rc) return False logger.info("%s exited with code %d (stopped by signal %d).", pam_imunify_daemon, rc, child_proc.transient_signal) return True def daemon_being_watched_loop() -> bool: failures = [] while True: ok_stopped = daemon_being_watched() if ok_stopped: return True now = time.time() failures.append(now) if (sum(1 for timestamp in failures if timestamp >= now - FAILURES_PERIOD) > MAX_FAILURES_PER_HOUR): return False if __name__ == '__main__': try: logger_init() read_panic_watchdog_config() ok_stopped = daemon_being_watched_loop() if not ok_stopped: logger.error("PAM Imunify ftp integration is to be disabled " "after %d pam_imunify_daemon failures.", MAX_FAILURES_PER_HOUR) # Watching for and disabling Ftp integration is done in daemon # disable_pamftp_intergation() except Exception as e: logger.exception("unexpected error: %s", e) sys.exit(1)