def __init__(self, configpath): self.config = Config(configpath) self.loglevel = self.get_level(self.config.get_value("LOG", "level")) # create logger root = logging.getLogger() root.handlers.clear() # workaround for default stdout handler root.setLevel(self.loglevel) # create formatter and add it to the handlers formatter = logging.Formatter( '[%(asctime)s] [%(levelname)s] [%(filename)s:%(lineno)d] %(message)s') # create console handler -> logs are always written to stdout if bool(self.config.get_value("LOG", "to_stdout")): std_handler = logging.StreamHandler(stdout) std_handler.setLevel(self.loglevel) std_handler.setFormatter(formatter) root.addHandler(std_handler) # create file handler if enabled in configuration if bool(self.config.get_value("LOG", "to_files")): # create log filepath if not exists create_folder_if_not_exists( self.config.get_value("LOG", "filepath")) file_handler = logging.FileHandler( self.config.get_value("LOG", "filepath") + "/" + self.config.get_value("LOG", "filename") ) file_handler.setLevel(self.loglevel) file_handler.setFormatter(formatter) root.addHandler(file_handler)
def __init__(self, debug: bool = False) -> None: """TODO: Doku.""" self.cfg_log = Config(LOG_CONFIG_PATH) loglevel = self.cfg_log.get_value("LOG", "level") to_stdout = self.cfg_log.get_value("LOG", "to_stdout") to_files = self.cfg_log.get_value("LOG", "to_files") logpath = self.cfg_log.get_value("LOG", "filepath") logfile = self.cfg_log.get_value("LOG", "filename") self.log = Log(str(loglevel), bool(to_stdout), bool(to_files), str(logpath), str(logfile)) info("starting easywall-web") self.cfg = Config(CONFIG_PATH) if debug is True: info("loading Flask debug configuration") APP.config.from_object('easywall.web.__main__.DevelopmentConfig') else: info("loading Flask production configuration") APP.config.from_object('easywall.web.__main__.ProductionConfig') self.rules_handler = RulesHandler() self.login_attempts = self.cfg.get_value("WEB", "login_attempts") self.login_bantime = self.cfg.get_value("WEB", "login_bantime") self.ip_ban = IpBan(app=APP, ban_count=self.login_attempts, ban_seconds=self.login_bantime, ipc=True) self.ip_ban.url_pattern_add('^/static.*$', match_type='regex')
def __init__(self): """the init function creates some class variables""" self.config = Config("config/easywall.ini") self.enabled = bool(self.config.get_value("ACCEPTANCE", "enabled")) self.filename = self.config.get_value("ACCEPTANCE", "filename") debug("Acceptance Process initialized. Status: " + str(self.enabled) + " Filename: " + self.filename)
class TestFirstrun(unittest.TestCase): """TODO: Doku.""" def setUp(self) -> None: """TODO: Doku.""" prepare_configuration() self.config = Config(WEB_CONFIG_PATH) self.config.set_value("WEB", "username", "") self.config.set_value("WEB", "password", "") self.client = prepare_client() def tearDown(self) -> None: """TODO: Doku.""" restore_configuration() def test_firstrun_logged_out(self) -> None: """TODO: Doku.""" response = self.client.get('/') self.assertIn(b"username and password", response.data) def test_firstrun_save_logged_out(self) -> None: """TODO: Doku.""" response = self.client.post('/firstrun', data={ "username": "******", "password": "******", "password-confirm": "demo" }, follow_redirects=True) self.assertIn(b"Username and password have been successfully saved", response.data)
class Passwd(object): """the class contains the password generation and saving""" def __init__(self): """the init function creates the config variable and calls the user input""" self.config = Config("config/web.ini") self.ask_user() def savepasswd(self, password): """the function saves the password into the config file using the config class""" hostname = platform.node().encode("utf-8") salt = hashlib.sha512(hostname).hexdigest() pw_hash = hashlib.sha512( str(salt + password).encode("utf-8")).hexdigest() self.config.set_value("WEB", "password", pw_hash) print("Password successfully saved.") def saveuser(self, username): """the function saves the username into the config file using the config class""" self.config.set_value("WEB", "username", username) print("Username successfully saved.") def ask_user(self): """the function asks the user for the username and password""" username = input("easywall Web Username: "******"easywall Web Password: ") self.savepasswd(password)
def __init__(self, debug: bool = False) -> None: self.cfg = Config(CONFIG_PATH) loglevel = self.cfg.get_value("LOG", "level") to_stdout = self.cfg.get_value("LOG", "to_stdout") to_files = self.cfg.get_value("LOG", "to_files") logpath = self.cfg.get_value("LOG", "filepath") logfile = self.cfg.get_value("LOG", "filename") self.log = Log(str(loglevel), bool(to_stdout), bool(to_files), str(logpath), str(logfile)) self.login_attempts = self.cfg.get_value("WEB", "login_attempts") self.login_bantime = self.cfg.get_value("WEB", "login_bantime") self.ip_ban = IpBan(app=APP, ban_count=self.login_attempts, ban_seconds=self.login_bantime, ipc=True) self.ip_ban.url_pattern_add('^/static.*$', match_type='regex') info("starting easywall-web") self.rules_handler = RulesHandler() self.rules_handler.ensure_files_exist() if debug is True: port = self.cfg.get_value("WEB", "bindport") host = self.cfg.get_value("WEB", "bindip") APP.config.from_object('easywall_web.__main__.DevelopmentConfig') APP.run(str(host), str(port)) else: APP.config.from_object('easywall_web.__main__.ProductionConfig')
def __init__(self, debug: bool = False) -> None: self.cfg = Config(CONFIG_PATH) loglevel = self.cfg.get_value("LOG", "level") to_stdout = self.cfg.get_value("LOG", "to_stdout") to_files = self.cfg.get_value("LOG", "to_files") logpath = self.cfg.get_value("LOG", "filepath") logfile = self.cfg.get_value("LOG", "filename") self.log = Log(str(loglevel), bool(to_stdout), bool(to_files), str(logpath), str(logfile)) info("starting easywall-web") self.is_first_run = not folder_exists("rules") self.rules_handler = RulesHandler() if self.is_first_run: self.rules_handler.ensure_files_exist() if debug is True: port = self.cfg.get_value("WEB", "bindport") host = self.cfg.get_value("WEB", "bindip") APP.config.from_object('easywall_web.__main__.DevelopmentConfig') APP.run(str(host), str(port)) else: APP.config.from_object('easywall_web.__main__.ProductionConfig')
def setUp(self) -> None: """TODO: Doku.""" prepare_configuration() self.config = Config(WEB_CONFIG_PATH) self.config.set_value("WEB", "username", "") self.config.set_value("WEB", "password", "") self.client = prepare_client()
def test_init(self, input: Any, getpass: Any) -> None: """TODO: Doku.""" input.return_value = "test" getpass.return_value = "test" from easywall.web.passwd import Passwd Passwd() self.config = Config(WEB_CONFIG_PATH) self.assertEqual(self.config.get_value("WEB", "username"), "test")
class Log(object): """This class is a wrapper class around the logging module""" def __init__(self, configpath): self.config = Config(configpath) self.loglevel = self.get_level(self.config.get_value("LOG", "level")) # create logger root = logging.getLogger() root.handlers.clear() # workaround for default stdout handler root.setLevel(self.loglevel) # create formatter and add it to the handlers formatter = logging.Formatter( '[%(asctime)s] [%(levelname)s] [%(filename)s:%(lineno)d] %(message)s') # create console handler -> logs are always written to stdout if bool(self.config.get_value("LOG", "to_stdout")): std_handler = logging.StreamHandler(stdout) std_handler.setLevel(self.loglevel) std_handler.setFormatter(formatter) root.addHandler(std_handler) # create file handler if enabled in configuration if bool(self.config.get_value("LOG", "to_files")): # create log filepath if not exists create_folder_if_not_exists( self.config.get_value("LOG", "filepath")) file_handler = logging.FileHandler( self.config.get_value("LOG", "filepath") + "/" + self.config.get_value("LOG", "filename") ) file_handler.setLevel(self.loglevel) file_handler.setFormatter(formatter) root.addHandler(file_handler) def close_logging(self): """This function gently closes all handlers before exiting the software""" root = logging.getLogger() for handler in root.handlers: handler.close() root.removeFilter(handler) def get_level(self, log_level): """This internal function determines the log_level of the logging class""" level = logging.NOTSET if log_level == "debug": level = logging.DEBUG elif log_level == "info": level = logging.INFO elif log_level == "warning": level = logging.WARNING elif log_level == "error": level = logging.ERROR elif log_level == "critical": level = logging.CRITICAL return level
class Main(): """TODO: Doku.""" def __init__(self) -> None: """TODO: Doku.""" self.cfg = Config(CONFIG_PATH) self.cfg_log = Config(LOG_CONFIG_PATH) loglevel = self.cfg_log.get_value("LOG", "level") to_stdout = self.cfg_log.get_value("LOG", "to_stdout") to_files = self.cfg_log.get_value("LOG", "to_files") logpath = self.cfg_log.get_value("LOG", "filepath") logfile = self.cfg_log.get_value("LOG", "filename") self.log = Log(str(loglevel), bool(to_stdout), bool(to_files), str(logpath), str(logfile)) info("starting easywall") self.easywall = Easywall(self.cfg) self.event_handler = ModifiedHandler(self.apply) self.observer = Observer() self.stop_flag = False info("easywall has been started") def apply(self, filename: str) -> None: """TODO: Doku.""" info("starting apply process from easywall") delete_file_if_exists(filename) self.easywall.apply() def start_observer(self) -> None: """ Keep the main process running until it should be stopped. if someone is pressing ctrl + C the software will initiate the stop process """ self.observer.schedule(self.event_handler, ".") self.observer.start() try: while not self.stop_flag: sleep(2) except KeyboardInterrupt: info("KeyboardInterrupt received, starting shutdown") finally: self.shutdown() def shutdown(self) -> None: """Stop all threads and shut the software down gracefully.""" info("starting shutdown") self.observer.stop() delete_file_if_exists(".acceptance") self.observer.join() info("shutdown completed") self.log.close_logging()
def set_username_password(self) -> None: """TODO: Doku.""" self.config = Config(WEB_CONFIG_PATH) self.config.set_value("WEB", "username", "test") hostname = platform.node().encode("utf-8") salt = hashlib.sha512(hostname).hexdigest() pw_hash = hashlib.sha512( str(salt + "test").encode("utf-8")).hexdigest() self.config.set_value("WEB", "password", pw_hash)
def test_init(self, input, getpass): """ TODO: Doku """ input.return_value = "test" getpass.return_value = "test" from easywall_web.passwd import Passwd Passwd() self.config = Config(CONFIG_PATH) self.assertEqual(self.config.get_value("WEB", "username"), "test")
def create_rule_files(cfg: Config): """ the function checks if the rule files exist and creates them if they don't exist """ filepath = cfg.get_value("RULES", "filepath") create_folder_if_not_exists(filepath) filename = "" for ruletype in ["blacklist", "whitelist", "tcp", "udp", "custom"]: filename = cfg.get_value("RULES", ruletype) create_file_if_not_exists("{}/{}".format(filepath, filename))
def setUp(self): content = """[TEST] teststring = string testboolean = true testint = 1 testfloat = 1.1 """ create_file_if_not_exists("test.ini") write_into_file("test.ini", content) self.config = Config("test.ini")
def __init__(self): logging.info("Applying new configuration.") self.create_running_file() self.config = Config("config/easywall.ini") self.iptables = Iptables() self.acceptance = Acceptance() self.ipv6 = self.config.get_value("IPV6", "enabled") self.filepath = None self.filename = None self.date = None self.apply() self.delete_running_file()
def __init__(self, configpath: str): info("Applying new configuration.") self.create_running_file() self.config = Config(configpath) self.iptables = Iptables(configpath) self.acceptance = Acceptance(configpath) self.ipv6 = self.config.get_value("IPV6", "enabled") self.filepath = None self.filename = None self.date = None self.apply() self.delete_running_file()
class TestLogin(unittest.TestCase): """ TODO: Doku """ def setUp(self): prepare_configuration() self.client = prepare_client() self.config = Config(CONFIG_PATH) def tearDown(self): restore_configuration() def test_login(self): """ TODO: Doku """ self.client.get('/login') def test_login_post(self): """ TODO: Doku """ self.log_in(self.client) def test_logout(self): """ TODO: Doku """ self.log_in(self.client) self.client.get('/logout', follow_redirects=True) def set_username_password(self): """ TODO: Doku """ self.config = Config(CONFIG_PATH) self.config.set_value("WEB", "username", "test") hostname = platform.node().encode("utf-8") salt = hashlib.sha512(hostname).hexdigest() pw_hash = hashlib.sha512(str(salt + "test").encode("utf-8")).hexdigest() self.config.set_value("WEB", "password", pw_hash) def log_in(self, client): """ TODO: Doku """ self.set_username_password() return client.post('/login', data=dict(username="******", password="******"), follow_redirects=True)
def __init__(self) -> None: """the init function creates the config variable and calls the user input""" self.config = Config(CONFIG_PATH) parser = argparse.ArgumentParser() parser.add_argument("--username", "-u", help="set username") parser.add_argument("--password", "-p", help="set password") args = parser.parse_args() if args.username and args.password: self.saveuser(args.username) self.savepasswd(args.password) else: self.ask_user()
def __init__(self): """the init function creates some useful class variables""" debug("Setting up iptables...") self.config = Config("config/easywall.ini") self.ipv6 = bool(self.config.get_value("IPV6", "enabled")) self.iptables_bin = self.config.get_value("EXEC", "iptables") self.iptables_bin_save = self.config.get_value("EXEC", "iptables-save") self.iptables_bin_restore = self.config.get_value( "EXEC", "iptables-restore") if self.ipv6 is True: debug("IPV6 is enabled") self.ip6tables_bin = self.config.get_value("EXEC", "ip6tables") self.ip6tables_bin_save = self.config.get_value( "EXEC", "ip6tables-save") self.ip6tables_bin_restore = self.config.get_value( "EXEC", "ip6tables-restore")
def setUp(self): self.config_file = "test_easywall.ini" content = """[LOG] level = info to_files = false to_stdout = true filepath = filename = [IPV6] enabled = true [ACCEPTANCE] enabled = true duration = 1 [EXEC] iptables = /sbin/iptables ip6tables = /sbin/ip6tables iptables-save = /sbin/iptables-save ip6tables-save = /sbin/ip6tables-save iptables-restore = /sbin/iptables-restore ip6tables-restore = /sbin/ip6tables-restore [BACKUP] filepath = ./backup ipv4filename = iptables_v4_backup ipv6filename = iptables_v6_backup """ create_file_if_not_exists(self.config_file) write_into_file(self.config_file, content) self.cfg = Config(self.config_file) self.easywall = Easywall(self.cfg) self.easywall.rules.rules_firstrun()
def run(): """this is the main function of the program""" # Startup Process config = Config("config/easywall.ini") loglevel = config.get_value("LOG", "level") to_stdout = config.get_value("LOG", "to_stdout") to_files = config.get_value("LOG", "to_files") logpath = config.get_value("LOG", "filepath") logfile = config.get_value("LOG", "filename") log = Log(loglevel, to_stdout, to_files, logpath, logfile) info("Starting up easywall...") ensure_rules_files(config) event_handler = ModifiedHandler() observer = Observer() observer.schedule(event_handler, config.get_value("RULES", "filepath")) observer.start() info("easywall is up and running.") # waiting for file modifications try: while True: sleep(2) except KeyboardInterrupt: info("KeyboardInterrupt received, starting shutdown") finally: shutdown(observer, config, log)
def setUp(self) -> None: self.config_file = "test_easywall.ini" content = """[LOG] level = info to_files = no to_stdout = yes filepath = /var/log filename = easywall.log [IPTABLES] log_blocked_connections = yes log_blocked_connections_log_limit = 60 log_blacklist_connections = yes log_blacklist_connections_log_limit = 60 drop_broadcast_packets = yes drop_multicast_packets = yes drop_anycast_packets = yes ssh_brute_force_prevention = yes ssh_brute_force_prevention_log = yes ssh_brute_force_prevention_connection_limit = 5 ssh_brute_force_prevention_log_limit = 60 icmp_flood_prevention = yes icmp_flood_prevention_log = yes icmp_flood_prevention_connection_limit = 5 icmp_flood_prevention_log_limit = 60 drop_invalid_packets = yes drop_invalid_packets_log = yes drop_invalid_packets_log_limit = 60 port_scan_prevention = yes port_scan_prevention_log = yes port_scan_prevention_log_limit = 60 [IPV6] enabled = true icmp_allow_router_advertisement = yes icmp_allow_neighbor_advertisement = yes [ACCEPTANCE] enabled = yes duration = 1 timestamp = [EXEC] iptables = /sbin/iptables ip6tables = /sbin/ip6tables iptables-save = /sbin/iptables-save ip6tables-save = /sbin/ip6tables-save iptables-restore = /sbin/iptables-restore ip6tables-restore = /sbin/ip6tables-restore """ create_file_if_not_exists(self.config_file) write_into_file(self.config_file, content) self.cfg = Config(self.config_file) self.easywall = Easywall(self.cfg) self.easywall.rules.ensure_files_exist()
class Acceptance(object): """ the class contains function for checking the user acceptance after applying new firewall rules """ def __init__(self): """the init function creates some class variables""" self.config = Config("config/easywall.ini") self.enabled = bool(self.config.get_value("ACCEPTANCE", "enabled")) self.filename = self.config.get_value("ACCEPTANCE", "filename") debug("Acceptance Process initialized. Status: " + str(self.enabled) + " Filename: " + self.filename) def reset(self): """the function is called then the user did not accept the changes""" if self.enabled: create_file_if_not_exists(self.filename) write_into_file(self.filename, "false") debug("Acceptance has been reset.") def check(self): """the function checks for acceptance and executes the next steps""" if self.enabled: seconds = int(self.config.get_value("ACCEPTANCE", "time")) debug( "Starting Acceptance Check... waiting for " + str(seconds) + " seconds") while seconds > 0: sleep(1) seconds = seconds - 1 with open(self.filename, 'r') as accfile: accepted = accfile.read() accepted = accepted.replace("\n", "") if accepted == "true": debug("Acceptance Process Result: Accepted") return True else: debug( "Acceptance Process Result: Not Accepted (file content: " + accepted + ")") return False else: debug("Acceptance is disabled. Skipping check.") return True
def setUp(self): content = """[ACCEPTANCE] enabled = true duration = 1 """ create_file_if_not_exists("acceptance.ini") write_into_file("acceptance.ini", content) self.config = Config("acceptance.ini") self.acceptance = Acceptance(self.config)
def prepare_configuration() -> None: """ TODO: Doku """ if file_exists(CONFIG_PATH): rename_file(CONFIG_PATH, CONFIG_BACKUP_PATH) content = """[LOG] level = info to_files = no to_stdout = yes filepath = log filename = easywall-web.log [WEB] username = demo password = xxx bindip = 0.0.0.0 bindport = 12227 login_attempts = 10 login_bantime = 1800 [VERSION] version = 0.0.0 sha = 12345 date = 2020-01-01T00:00:00Z timestamp = 1234 [uwsgi] https-socket = 0.0.0.0:12227,easywall.crt,easywall.key processes = 5 threads = 2 callable = APP master = false wsgi-file = easywall_web/__main__.py need-plugin = python3 """ create_file_if_not_exists(CONFIG_PATH) write_into_file(CONFIG_PATH, content) config = Config(CONFIG_PATH) config.set_value("VERSION", "timestamp", str(int(time())))
def test_constructor_file_not_read(self) -> None: """TODO: Doku.""" create_file_if_not_exists("test.ini") content = """[DEFAULT] goodcontent = test badcontent """ write_into_file("test.ini", content) with self.assertRaises(ParsingError): Config("test.ini")
def get_config_version_mismatch(self) -> bool: """ TODO: Docu """ cfg1 = Config("config/easywall.sample.ini") cfg2 = Config("config/easywall.ini") for section in cfg1.get_sections(): if section not in cfg2.get_sections(): return True for key in cfg1.get_keys(section): if key not in cfg2.get_keys(section): return True return False
class Passwd(object): """the class contains the password generation and saving""" def __init__(self) -> None: """the init function creates the config variable and calls the user input""" self.config = Config(CONFIG_PATH) parser = argparse.ArgumentParser() parser.add_argument("--username", "-u", help="set username") parser.add_argument("--password", "-p", help="set password") args = parser.parse_args() if args.username and args.password: self.saveuser(args.username) self.savepasswd(args.password) else: self.ask_user() def savepasswd(self, password: str) -> None: """the function saves the password into the config file using the config class""" hostname = platform.node().encode("utf-8") salt = hashlib.sha512(hostname).hexdigest() pw_hash = hashlib.sha512(str(salt + password).encode("utf-8")).hexdigest() self.config.set_value("WEB", "password", pw_hash) print("Password successfully saved.") def saveuser(self, username: str) -> None: """the function saves the username into the config file using the config class""" self.config.set_value("WEB", "username", username) print("Username successfully saved.") def ask_user(self) -> None: """the function asks the user for the username and password""" username = input("easywall Web Username: "******"easywall Web Password: "******"easywall Web Password repeat: ") if password == password_repeat: self.savepasswd(password) else: print("password is not equal. password not saved!")
def __init__(self) -> None: """TODO: Doku.""" self.cfg = Config(CONFIG_PATH) self.cfg_log = Config(LOG_CONFIG_PATH) loglevel = self.cfg_log.get_value("LOG", "level") to_stdout = self.cfg_log.get_value("LOG", "to_stdout") to_files = self.cfg_log.get_value("LOG", "to_files") logpath = self.cfg_log.get_value("LOG", "filepath") logfile = self.cfg_log.get_value("LOG", "filename") self.log = Log(str(loglevel), bool(to_stdout), bool(to_files), str(logpath), str(logfile)) info("starting easywall") self.easywall = Easywall(self.cfg) self.event_handler = ModifiedHandler(self.apply) self.observer = Observer() self.stop_flag = False info("easywall has been started")