import select import re import difflib import subprocess import datetime config_path = '/samhain/config/samhainrc' mount_dir = '/ien_fs/proc/self/mounts' inotify_processes = {} def get_monitored_dirs(path): with open(path) as f: lines = f.readlines() regex = re.compile(r'dir.*=.*99(/ien_fs/.*)') paths = [] for line in lines: m = regex.search(line) if m: paths.append(m.group(1)) return paths def process_mounts(old_mounts, new_mounts, monitored_paths): actions = {'+':'addition', '-':'deletion'} output = [m for m in difflib.ndiff(old_mounts, new_mounts) if m[0] != ' '] for mount in output: s = mount.split() action = actions.get(s[0], '') if old_mounts else 'existence' # Very rare case where this can fail. Only randomly saw this, but it crashed the program. try: path = s[2] mount_type = s[3] except IndexError: path = "Unknown" mount_type = "Unknown" result = [p for p in monitored_paths if path.startswith(p)] if result: time_format = '%Y-%m-%dT%H:%M:%S%z' date = datetime.datetime.now().astimezone().replace(microsecond=0).strftime(time_format) msg = f'CRIT : [{date}] msg=, path={path}, action={action},mount_type={mount_type}' print(msg) if action == 'addition': # https://stackoverflow.com/a/13143013/17126721 # Brilliant fix to orphaned shell command mask = 'modify,move,delete,create' cmd = f"exec inotifywait -m {path} -r -q -e {mask} --timefmt '{time_format}' --format 'CRIT : [%T] msg=, path=%w%f, inotify_event=%:e'" # No problem with using shell=True here IMO. # We don't do anything special with it, and the path will be absolute inotify_processes[path] = subprocess.Popen(cmd, shell=True) elif action == 'deletion' and path in inotify_processes: inotify_processes[path].terminate() inotify_processes.pop(path) # Grab list of monitored dirs from the samhain config file paths = get_monitored_dirs(config_path) # Initial run through before loop # If a suspicious mount exists at this point, we do not need to set up our own watches for this one as Samhain will cover these f = open(mount_dir) mounts = f.readlines() process_mounts([], mounts, paths) # Loop forever without constantly polling the file while True: r,w,x = select.select([],[],[f]) f.seek(0) new_mounts = f.readlines() process_mounts(mounts, new_mounts, paths) # We maintain the list of last known mounts to diff on mounts = new_mounts