Mini Shell
#!/opt/imh-python/bin/python3
import pdb
import os
import signal
import time
import datetime
import re
import argparse
import threading
from subprocess import check_output
class Mitigator():
'''
currently has two main features:
1. watches for a queueprocd process related to jailshells, kills it
2. repeatedly attemps to clear virtfs if there are mounts
'''
def __init__(self):
self.log_file = '/var/log/mitigatord.log'
self.virtfs_attempts = 0
self.log_level = 2
self.virt_check_interval = 3
self.thread_list = []
def get_args(self):
'''
flags: -d makes logging to
log_file instead of stdout
'''
parser = argparse.ArgumentParser()
parser.add_argument('-d', '--daemon', action='store_true',
default=False, dest='daemon',
help=('logs to log file instead of stdout'))
self.args = parser.parse_args()
# slows down process checking for daemon mode
self.log(f'pid {os.getpid()} starting in daemon mode', 1)
# convert minutes to seconds
def proc_watcher(self):
'''
function to check for and kill
the process that spawns jailshells
'''
search_str = re.compile(
r'root\s+(\d+)[^\n]+queueprocd - process - update_users_jail\s*\n'
)
ps_aux = check_output('ps -fu root | grep -F queueprocd', shell=True, timeout = 60).decode('utf-8')
# lightest string comparison I think
if 'update_users_jail' not in ps_aux:
self.log('proc not found', 3)
return
# then gets pid with re
proc_finder = re.search(search_str, ps_aux)
if not proc_finder:
self.log('re.search failed, looping', 3)
return
pid = int(proc_finder.group(1))
self.log(f'found:\n{proc_finder.group(0).strip()}', 1)
try:
os.kill(pid, signal.SIGKILL)
except Exception as e:
self.log(e, 1)
return
def clear_virt(self):
'''
runs proc_watcher
clears umounts all virtfs mounts + orphans
'''
self.launch_thread('proc_watcher', self.proc_watcher)
time.sleep(0.4)
clear_cmd = ('grep -F " /home/virtfs/" /proc/mounts '
'''| awk '{print $2}' '''
'| xargs -I {} -P 5 umount -v {} &>/dev/null;'
'echo -n')
self.log('clearing virtfs mounts', 1)
check_output(clear_cmd, shell=True, timeout=3600)
self.log('clearing orphan mounts', 1)
check_output(
'/scripts/clear_orphaned_virtfs_mounts --clearall &>/dev/null',
timeout=3600,
shell=True,
)
self.log('done clearing virtfs mounts', 1)
def check_virt(self):
'''
checks for virtfs mounts
returns False if there are no virtfs mounts
runs clear_virt and returns True if mounts
'''
count_cmd = ('grep -F " /home/virtfs/" /proc/mounts '
'''| awk '{print $2}' | wc -l 2>/dev/null''')
virt_count = check_output(count_cmd, shell=True, timeout=60)
virt_count = virt_count.decode('utf-8').strip()
self.log(f'{virt_count} virtfs mounts', 2)
if int(virt_count) == 0:
self.log('no virt mounts found, looping', 2)
return False
else:
self.log(f'{virt_count} virtfs mounts', 1)
self.clear_virt()
return True
def virt_watcher(self):
'''
function for virtfs mount clearing loop
'''
while True:
self.check_virt()
time.sleep(self.virt_check_interval)
def launch_thread(self, name, target):
'''
uses daemon false so we dont need to join threads
'''
t = threading.Thread(
name=name,
target=target, daemon = False
)
t.start()
self.thread_list.append(t)
def log(self, log_str, lvl):
'''
provides logging with modular verbosity
see self.log_level in __init__
'''
if lvl >= self.log_level:
return
tstamp = datetime.datetime.now().replace(microsecond=0).isoformat()
log_str = f'{tstamp}:{log_str}'
if self.args.daemon:
with open(self.log_file, 'a') as fh:
if os.path.getsize(self.log_file) > 15000000:
fh.truncate(10000000)
print(log_str, file=fh)
return
print(log_str)
def main():
'''
handles main thread launching
'''
m = Mitigator()
m.get_args()
m.launch_thread('virt_watcher', m.virt_watcher)
if __name__ == '__main__':
main()
Zerion Mini Shell 1.0