import os import sys import subprocess from multiprocessing import Process, Manager, Pool, TimeoutError, freeze_support, active_children from sys import platform from time import sleep import uuid import yaml from util import find_data_file from util import fprint from util import kill from util import run_cmd from notification import send_notification import taskbartool import util import netstat import ssh import auth import panel import block history = list() displaydata = None settings = None netdata_res = None procdata_res = None killme = None ppanel = None datafile = "" #print(datafile) config = None interval = 10 win32 = platform == "win32" linux = platform == "linux" or platform == "linux2" macos = platform == "darwin" # Get unique system values if win32: sysid = hex(uuid.getnode()) datafile += sysid datafile += "gendata.csv" # Python is running as Administrator (so netstat can get filename, to block, etc), # so we use this to see who is actually logged in # it's very hacky startupinfo = subprocess.STARTUPINFO() #if not getattr(sys, "frozen", False): startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW # hide powershell window res = subprocess.check_output(["WMIC", "ComputerSystem", "GET", "UserName"], universal_newlines=True, startupinfo=startupinfo) _, username = res.strip().rsplit("\n", 1) userid, sysdom = username.rsplit("\\", 1) if linux or macos: sysid = hex(uuid.getnode()) #fprint(sysid) datafile += sysid datafile += "gendata.csv" res = subprocess.check_output(["who",], universal_newlines=True) userid = res.strip().split(" ")[0] sysdom = subprocess.check_output(["hostname",], universal_newlines=True).strip() #fprint(hostname) def netstat_done(res): fprint("netstat done, processing", settings) procdata_res = pool.apply_async(netstat.process, (res,), callback=process_done) #netstat.process(res) def process_done(res): if settings["running"] == True: fprint("uploading to sftp...", settings) #ssh.sftp_send_data(config, datafile, 'send') procdata_res = pool.apply_async(ssh.sftp_send_data, (config, datafile, 'send'), callback=upload_done) def upload_done(res): settings["block"] = True tmpstat = settings["stats"] tmpstat[2] += 1 settings["stats"] = tmpstat def login_done(res): if not res: fprint("Login failure", settings) settings["message"] = "Login failure" else: fprint("Login result in main: " + str(res), settings) settings["loggedin"] = res settings["continueui"] = True def blockdata_done(res): global settings fprint("FINISHED downloading block data", settings) tmpkill = settings["kill"] settings["kill"] = False #block_res = pool.apply_async(block.block_conn, (config, datafile, res, settings)) block_pids, block_ips, block_data, goodct = block.block_conn(config, datafile, res) tmpstat = settings["stats"] tmpstat[1] += goodct if tmpstat[0] > 0 and goodct > 0: tmpstat[4] = 1.0 / (goodct * 100.0 / tmpstat[0]) else: tmpstat[4] = 0.0 tmpstat[3] += 1 settings["stats"] = tmpstat tmplist = settings["badapps"] for x in block_pids: fprint(x, settings) if not x in tmplist: tmplist.append(x) settings["badapps"] = tmplist fprint(settings["badapps"], settings) tmplist = settings["badips"] for x in block_ips: fprint(x, settings) if not x in tmplist: tmplist.append(x) settings["badips"] = tmplist fprint(settings["badips"], settings) settings["kill"] = tmpkill tmplist = settings["badlines"] for x in block_data: fprint(x, settings) if not x in tmplist: tmplist.append(x) settings["badlines"] = tmplist fprint(settings["badlines"], settings) settings["newdata"] = True def readstat_done(res): settings["stats"] = res fprint("Read stats!" + str(settings["stats"]), settings) def killall(): kids = active_children() for kid in kids: kid.kill() fprint("Every child has been killed", settings) os.kill(os.getpid(), 9) # dirty kill of self def mainloop(pool): # worker pool: netstat, netstat cleanup, upload, download, ui tasks global config global counter global netdata_res global procdata_res global rawdata global killme global ppanel #print(killme) if killme.value > 0: #print("killing") util.clear_fwll() # clear the firewall rules before shutdown killall() #print(res.get(timeout=1)) if counter == 0: # runs every INTERVAL #fprint("start loop") if settings["stats"][1] > 0: util.write_stats(settings["stats"]) if netdata_res is None or netdata_res.ready(): #rawdata = netdata_res.get() #procdata_res = pool.apply_async(process_netstat, (rawdata)) fprint("netstat starting", settings) netdata_res = pool.apply_async(netstat.start, callback=netstat_done) #fprint(netdata_res.successful()) # runs every 50ms if settings["continueui"] == True: settings["continueui"] = False #if ppanel is not None: # login panel is already open # ppanel.terminate() # ppanel = Process(target=panel.openwindow, args=(displaydata,settings,killme)) # ppanel.start() if settings["showui"] == True: settings["showui"] = False ppanel = Process(target=panel.openwindow, args=(displaydata,settings,killme)) ppanel.start() if settings["login"] == True: login_res = pool.apply_async(auth.login, (config, settings["username"], settings["password"], sysid), callback=login_done) #fprint(auth.login(config, settings["username"], settings["password"], sysid)) settings["login"] = False if settings["block"] == True and settings["running"] == True: #blockdata_res = pool.apply_async(block.get_blocklist, (config, settings), callback=blockdata_done) res = block.get_blocklist(config, settings) blockdata_done(res) settings["block"] = False if config["core"]["level"] == 0: settings["kill"] = False settings["fwll"] = False if config["core"]["level"] == 1: settings["kill"] = True settings["fwll"] = False if config["core"]["level"] == 2: settings["kill"] = False settings["fwll"] = True if config["core"]["level"] == 3: settings["kill"] = True settings["fwll"] = True if settings["kill"] == True: tmplist = settings["badapps"] settings["badapps"] = list() for x in tmplist: send_notification("Killing PID " + str(x)) kill(x) if settings["fwll"] == True: global history tmplist = settings["badlines"] tmpstat = settings["stats"] tmpstat[0] += len(tmplist) settings["stats"] = tmpstat settings["badlines"] = list() for line in tmplist: badproto = line[1] badip = line[4] badport = line[5] if (badip, badport) not in history: fprint("Firewalling " + badip + ":" + str(badport), settings) send_notification("Firewalling " + badip + ":" + str(badport)) if win32: cmd = 'New-NetFirewallRule -DisplayName "IPPigeon Security Rule ' + badip + ':' + str(badport) + '" -Group "IPPigeon" -Direction Outbound -LocalPort Any -Protocol ' + badproto + ' -Action Block -RemoteAddress ' + badip + ' -RemotePort ' + str(badport) run_cmd(cmd) if linux: cmd = "nft add rule ip ippigeon output ip daddr " + badip + " " + badproto.lower() + " dport " + str(badport) + " drop" run_cmd(cmd) else: history.append((badip, badport)) settings["badapps"] = list() if settings["applyconfig"] == True: settings["applyconfig"] = False config = settings["config"] #fprint(settings["config"]) with open(find_data_file('config.yml'), 'w') as filewrite: #global config yaml.dump(config, filewrite) fprint("Config saved!", settings) sleep(interval / (interval * config["core"]["clockspeed"])) counter += 1 if counter == interval * config["core"]["clockspeed"]: counter = 0 class Logger(object): def __init__(self, filename="output.log"): self.log = open(filename, "a") self.terminal = sys.stdout def write(self, message): self.log.write(message) #close(filename) #self.log = open(filename, "a") try: self.terminal.write(message) except: sleep(0) def flush(self): print("",end="") if __name__ == '__main__': freeze_support() # required if packaged into single EXE # create manager to share data to me, background, foreground # create worker pool sys.stdout = Logger(filename=find_data_file("output.log")) sys.stderr = Logger(filename=find_data_file("output.log")) with Manager() as manager: with Pool(processes=5) as pool: with open(find_data_file('config.yml'), 'r') as fileread: #global config config = yaml.safe_load(fileread) #print(config['sftp']['host']) interval = config['core']['interval'] displaydata = manager.list(range(2)) # data to be printed settings = manager.dict() # configuration # setup shared data variables settings["login"] = False settings["loggedin"] = False settings["showui"] = False settings["continueui"] = False settings["killbox"] = list() settings["badapps"] = list() settings["badips"] = list() settings["badlines"] = list() settings["block"] = False settings["kill"] = False settings["config"] = config settings["applyconfig"] = False settings["fwll"] = 0 settings["running"] = config["core"]["autostart"] settings["newdata"] = False settings["appendbad"] = list() settings["logMsg"] = list() settings["whitelist"] = list() tmp = list() tmp.append(["N/A", "TCP", "N/A", "N/A", "20.112.52.29", "5000", "N/A", "N/A", "N/A", "N/A", "N/A", "N/A", "N/A"]) tmp.append(["N/A", "TCP", "N/A", "N/A", "20.81.111.85", "80", "N/A", "N/A", "N/A", "N/A", "N/A", "N/A", "N/A"]) tmp.append(["N/A", "TCP", "N/A", "N/A", "100.115.71.78", "5000", "N/A", "N/A", "N/A", "N/A", "N/A", "N/A", "N/A"]) tmp.append(["N/A", "TCP", "N/A", "N/A", "100.115.71.78", "5000", "N/A", "N/A", "N/A", "N/A", "N/A", "N/A", "N/A"]) tmp.append(["N/A", "TCP", "N/A", "N/A", "174.143.130.167", "443", "N/A", "N/A", "N/A", "N/A", "N/A", "N/A", "N/A"]) tmp.append(["N/A", "TCP", "N/A", "N/A", "216.47.134.203", "443", "N/A", "N/A", "N/A", "N/A", "N/A", "N/A", "N/A"]) tmp.append(["N/A", "TCP", "N/A", "N/A", "34.111.83.189", "443", "N/A", "N/A", "N/A", "N/A", "N/A", "N/A", "N/A"]) settings["appendbad"] = tmp # connections blocked, total connections allowed, count of data sent, data recieved, ratio blocked-unblocked settings["stats"] = [0, 0, 0, 0, 0.0] killme = manager.Value('d', 0) #killme = False # launch background UI app as process util.clear_fwll() p = Process(target=taskbartool.background, args=(displaydata,settings,killme)) p.start() #p.join() # not a foreground job, so let's not join it keeprunning = True # initial setup #netdata_res = pool.apply_async(netstat, callback=netstat_done) if linux: # clear existing nftables entries, create new table run_cmd("nft delete table ip ippigeon") run_cmd("nft create table ip ippigeon") run_cmd("nft add chain ip ippigeon output { type filter hook output priority 0 \; policy accept\; }") #run_cmd("nft add chain ippigeon filter") pool.apply_async(util.read_stats, callback=readstat_done) # launch loop - non-blocking! counter = 0 while(keeprunning): mainloop(pool)