Esempio n. 1
0
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)
Esempio n. 2
0
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
Esempio n. 3
0
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()
Esempio n. 4
0
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))
Esempio n. 5
0
class Main(object):
    """
    TODO: Doku
    """

    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')
Esempio n. 6
0
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
Esempio n. 7
0
def run():
    """
    this is the first and main function of the program
    """
    config = Config(CONFIG_PATH)
    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("executing startup sequence...")

    create_rule_files(config)
    event_handler = ModifiedHandler()
    observer = Observer()
    observer.schedule(event_handler, config.get_value("RULES", "filepath"))
    observer.start()
    info("startup sequence successfully finished")
    start_observer(observer, config, log)
Esempio n. 8
0
class TestConfig(unittest.TestCase):
    """
    this class contains all test functions for the config module
    """
    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 tearDown(self):
        delete_file_if_exists("test.ini")

    def test_get_value_error(self):
        self.assertEqual(self.config.get_value("TEST", "notexistent"), "")

    def test_get_value_bool(self):
        self.assertEqual(self.config.get_value("TEST", "testboolean"), True)

    def test_get_value_int(self):
        self.assertEqual(self.config.get_value("TEST", "testint"), 1)

    def test_get_value_float(self):
        self.assertEqual(self.config.get_value("TEST", "testfloat"),
                         float(1.1))

    def test_get_sections(self):
        self.assertIn("TEST", self.config.get_sections())

    def test_set_value_success(self):
        self.assertEqual(self.config.set_value("TEST", "teststring", "erfolg"),
                         True)

    def test_set_value_fail_section(self):
        self.assertEqual(self.config.set_value("TEST2", "asd", "asd"), False)
Esempio n. 9
0
def shutdown(observer: Observer, config: Config, log: Log):
    """
    the function stops all threads and shuts the software down gracefully
    """
    info("starting shutdown...")
    observer.stop()
    delete_file_if_exists(".running")
    delete_file_if_exists(config.get_value("ACCEPTANCE", "filename"))
    observer.join()
    info("shutdown successfully completed")
    log.close_logging()
    exit(0)
Esempio n. 10
0
class Main(object):
    """
    TODO: Doku
    """

    def __init__(self, debug=False):
        APP.secret_key = os.urandom(12)
        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(loglevel, to_stdout, to_files, logpath, 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.rules_firstrun()

        if debug is True:
            port = self.cfg.get_value("WEB", "bindport")
            host = self.cfg.get_value("WEB", "bindip")
            APP.run(host, port, debug)
Esempio n. 11
0
class Main(object):
    """
    TODO: Doku
    """
    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')
Esempio n. 12
0
class TestPasswd(unittest.TestCase):
    """TODO: Doku."""
    def setUp(self) -> None:
        """TODO: Doku."""
        prepare_configuration()

    def tearDown(self) -> None:
        """TODO: Doku."""
        restore_configuration()

    @patch("builtins.input")
    @patch("getpass.getpass")
    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")
Esempio n. 13
0
def run():
    """this is the main function of the program"""
    # Startup Process
    masterlog = Log("config/easywall.ini")
    logging.info("Starting up easywall...")
    masterconfig = Config("config/easywall.ini")
    ensure_rules_files(masterconfig)
    event_handler = ModifiedHandler()
    observer = Observer()
    observer.schedule(
        event_handler, masterconfig.get_value("RULES", "filepath"))
    observer.start()
    logging.info("easywall is up and running.")

    # waiting for file modifications
    try:
        while True:
            time.sleep(2)
    except KeyboardInterrupt:
        shutdown(observer, masterconfig, masterlog)
    shutdown(observer, masterconfig, masterlog)
Esempio n. 14
0
class Main(object):
    """
    TODO: Doku
    """
    def __init__(self):
        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(loglevel, to_stdout, to_files, logpath, logfile)

        info("starting easywall")

        self.is_first_run = not folder_exists("rules")
        self.rules_handler = RulesHandler()
        if self.is_first_run:
            self.rules_handler.rules_firstrun()
        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:
        """
        this function is called to keep the main process running
        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:
        """
        the function stops all threads and shuts 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()
Esempio n. 15
0
class Easywall(object):
    """
    the class contains the main functions for the easywall core
    such as applying a new configuration or listening on rule file changes
    """

    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()

    def apply(self):
        """the function applies the configuration from the rule files"""
        self.acceptance.reset()

        # save current ruleset and reset iptables for clean setup
        self.iptables.save()
        self.iptables.reset()

        # drop intbound traffic and allow outbound traffic
        self.iptables.add_policy("INPUT", "DROP")
        self.iptables.add_policy("OUTPUT", "ACCEPT")

        # allow loopback access
        self.iptables.add_append("INPUT", "-i lo -j ACCEPT")

        # allow established or related connections
        self.iptables.add_append(
            "INPUT", "-m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT")

        # Block remote packets claiming to be from a loopback address.
        self.iptables.add_append(
            "INPUT", "-s 127.0.0.0/8 ! -i lo -j DROP", False, True)
        self.iptables.add_append("INPUT", "-s ::1/128 ! -i lo -j DROP", True)

        # Apply ICMP Rules
        self.apply_icmp()

        # Block IP-addresses from blacklist
        self.apply_blacklist()

        # Allow IP-addresses from whitelist
        self.apply_whitelist()

        # Allow TCP Ports
        self.apply_rules("tcp")

        # Allow UDP Ports
        self.apply_rules("udp")

        # log and reject all other packages
        self.iptables.add_append(
            "INPUT", "-j LOG --log-prefix \" easywall[other]: \"")
        self.iptables.add_append("INPUT", "-j REJECT")

        self.check_acceptance()

    def apply_icmp(self):
        """the function applies the icmp rules"""
        for icmptype in [0, 3, 8, 11]:
            self.iptables.add_append(
                "INPUT", "-p icmp --icmp-type " + str(icmptype) +
                " -m conntrack --ctstate NEW -j ACCEPT", False, True)
        if self.ipv6 is True:
            for icmptype in [
                    1, 2, 3, 4, 128, 133, 134, 135, 136, 137,
                    141, 142, 151, 152, 153]:
                self.iptables.add_append(
                    "INPUT", "-p ipv6-icmp --icmpv6-type " +
                    str(icmptype) + " -j ACCEPT", True)

    def apply_blacklist(self):
        """the function applies the blacklist rules from the rules file"""
        for ipaddr in self.get_rule_list("blacklist"):
            if ":" in ipaddr:
                self.iptables.add_append(
                    "INPUT", "-s " + ipaddr +
                    " -j LOG --log-prefix \" easywall[blacklist]: \"", True)
                self.iptables.add_append(
                    "INPUT", "-s " + ipaddr + " -j DROP", True)
            else:
                self.iptables.add_append(
                    "INPUT", "-s " + ipaddr +
                    " -j LOG --log-prefix \" easywall[blacklist]: \"", False,
                    True)
                self.iptables.add_append(
                    "INPUT", "-s " + ipaddr + " -j DROP", False, True)

    def apply_whitelist(self):
        """the function applies the whitelist rules from the rules file"""
        for ipaddr in self.get_rule_list("whitelist"):
            if ":" in ipaddr:
                self.iptables.add_append(
                    "INPUT", "-s " + ipaddr + " -j ACCEPT", True)
            else:
                self.iptables.add_append(
                    "INPUT", "-s " + ipaddr + " -j ACCEPT", False, True)

    def apply_rules(self, ruletype):
        """the function applies the rules from the rules file"""
        for port in self.get_rule_list(ruletype):
            if ":" in port:
                self.iptables.add_append(
                    "INPUT", "-p " + ruletype +
                    " --match multiport --dports " + port +
                    " -m conntrack --ctstate NEW -j ACCEPT")
            else:
                self.iptables.add_append(
                    "INPUT", "-p " + ruletype + " --dport " + port +
                    " -m conntrack --ctstate NEW -j ACCEPT")

    def check_acceptance(self):
        """the function checks for accetance of the new applied configuration"""
        info("Checking acceptance.")
        if self.acceptance.check() is False:
            info("Configuration not accepted, rolling back.")
            self.iptables.restore()
        else:
            self.rotate_backup()
            self.iptables.save()
            info("New configuration was applied.")

    def get_rule_list(self, ruletype):
        """the function retrieves the rules from the rules file"""
        rule_list = []
        with open(self.config.get_value("RULES", "filepath") + "/" +
                  self.config.get_value("RULES", ruletype), 'r') as rulesfile:
            for rule in rulesfile.read().split('\n'):
                if rule.strip() != "":
                    rule_list.append(rule)
        return rule_list

    def rotate_backup(self):
        """the function rotates the backup files to have a clean history of files"""
        self.filepath = self.config.get_value("BACKUP", "filepath")
        self.filename = self.config.get_value("BACKUP", "ipv4filename")
        self.date = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
        debug("rotating backup files in folder " +
              self.filepath + " -> add prefix " + self.date)
        self.rename_backup_file()
        if self.ipv6 is True:
            self.filename = self.config.get_value("BACKUP", "ipv6filename")
            self.rename_backup_file()

    def rename_backup_file(self):
        """the function renames a backup file"""
        rename_file("{}/{}".format(self.filepath, self.filename),
                    "{}/{}_{}".format(self.filepath, self.date, self.filename))

    def create_running_file(self):
        """the function creates a file in the main directory called .running"""
        create_file_if_not_exists(".running")

    def delete_running_file(self):
        """the function deletes a file in the main directory called .running"""
        delete_file_if_exists(".running")
Esempio n. 16
0

@app.route('/login', methods=['POST'])
def login_post_route():
    """The function calls the corresponding function from the appropriate module"""
    return login_post()


@app.route("/logout")
def logout_route():
    """The function calls the corresponding function from the appropriate module"""
    return logout()


@app.errorhandler(404)
def page_not_found_route(error):
    """The function calls the corresponding function from the appropriate module"""
    return page_not_found(error)


if __name__ == '__main__':
    # debugging mode
    app.secret_key = os.urandom(12)
    PORT = int(CFG.get_value("WEB", "bindport"))
    HOST = CFG.get_value("WEB", "bindip")
    DEBUG = True
    app.run(HOST, PORT, DEBUG)
else:
    # production mode
    app.secret_key = os.urandom(12)
Esempio n. 17
0
class Iptables(object):
    """the class contains functions that interact with the iptables software"""
    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 add_policy(self, chain, target):
        """the function creates a new policy in iptables"""
        debug("adding policy for chain " + chain + " and target " + target)
        if target == "ACCEPT" or target == "DROP":
            execute_os_command(self.iptables_bin + " -P " + chain + " " +
                               target)
            if self.ipv6 is True:
                execute_os_command(self.ip6tables_bin + " -P " + chain + " " +
                                   target)
        else:
            error("Invalid Target for addPolicy " + target)

    def add_chain(self, chain):
        """the function creates a new chain in iptables"""
        debug("adding chain " + chain)
        execute_os_command(self.iptables_bin + " -N " + chain)
        if self.ipv6 is True:
            execute_os_command(self.ip6tables_bin + " -N " + chain)

    def add_append(self, chain, rule, onlyv6=False, onlyv4=False):
        """the function creates a new append in iptables"""
        if onlyv4 is True or (onlyv6 is False and onlyv4 is False):
            debug("adding append for ipv4, chain: " + chain + ", rule: " +
                  rule)
            execute_os_command(self.iptables_bin + " -A " + chain + " " + rule)
        if self.ipv6 is True and (onlyv6 is True or
                                  (onlyv6 is False and onlyv4 is False)):
            debug("adding append for ipv6, chain: " + chain + ", rule: " +
                  rule)
            execute_os_command(self.ip6tables_bin + " -A " + chain + " " +
                               rule)

    def flush(self, chain=""):
        """the function flushes a iptables chain or all chains"""
        debug("flushing iptables chain: " + chain)
        execute_os_command(self.iptables_bin + " -F " + chain)
        if self.ipv6 is True:
            execute_os_command(self.ip6tables_bin + " -F " + chain)

    def delete_chain(self, chain=""):
        """the function deletes a chain in iptables"""
        debug("deleting chain " + chain)
        execute_os_command(self.iptables_bin + " -X " + chain)
        if self.ipv6 is True:
            execute_os_command(self.ip6tables_bin + " -X " + chain)

    def reset(self):
        """the function resets iptables to a clean state"""
        debug("resetting iptables to empty configuration")
        self.add_policy("INPUT", "ACCEPT")
        self.add_policy("OUTPUT", "ACCEPT")
        self.add_policy("FORWARD", "ACCEPT")
        self.flush()
        self.delete_chain()

    def list(self):
        """the function lists all iptables rules"""
        execute_os_command(self.iptables_bin + " -L")

    def save(self):
        """the function saves the current iptables state into a file"""
        debug("Starting Firewall Rule Backup...")
        # Create Backup Directory if not exists
        filepath = self.config.get_value("BACKUP", "filepath")
        create_folder_if_not_exists(filepath)

        # backing up ipv4 iptables rules
        debug("Backing up ipv4 rules...")
        filename = self.config.get_value("BACKUP", "ipv4filename")
        open(filepath + "/" + filename, 'w')
        self.save_execute(self.iptables_bin_save, filepath, filename)

        # backing up ipv6 iptables rules
        if self.ipv6 is True:
            debug("Backing up ipv6 rules...")
            filename = self.config.get_value("BACKUP", "ipv6filename")
            open(filepath + "/" + filename, 'w')
            self.save_execute(self.ip6tables_bin_save, filepath, filename)

    def save_execute(self, binary, filepath, filename):
        """the function executes the save of iptables into a file"""
        execute_os_command(binary + " | while read IN ; do echo $IN >> " +
                           filepath + "/" + filename + " ; done")

    def restore(self):
        """the function restores iptables rules from a file"""
        debug("Starting Firewall Rule Restore...")
        filepath = self.config.get_value("BACKUP", "filepath")
        create_folder_if_not_exists(filepath)

        debug("Restoring ipv4 rules...")
        filename = self.config.get_value("BACKUP", "ipv4filename")
        execute_os_command(self.iptables_bin_restore + " < " + filepath + "/" +
                           filename)

        if self.ipv6 is True:
            debug("Restoring ipv6 rules...")
            filename = self.config.get_value("BACKUP", "ipv6filename")
            execute_os_command(self.ip6tables_bin_restore + " < " + filepath +
                               "/" + filename)
Esempio n. 18
0
class Webutils(object):
    """Create a couple of shared functions used in the route functions."""
    def __init__(self) -> None:
        """TODO: Doku."""
        self.cfg = Config("config/web.ini")
        self.cfg_easywall = Config(CONFIG_PATH)
        self.cfg_log = Config(LOG_CONFIG_PATH)

    def check_login(self, request: Request) -> bool:
        """Check if the user/session is logged in."""
        if not session.get('logged_in'):
            return False
        if request.remote_addr != session.get('ip_address'):
            return False
        return True

    def check_first_run(self) -> bool:
        """Check if the webinterface is run for the first time."""
        username = self.cfg.get_value("WEB", "username")
        password = self.cfg.get_value("WEB", "password")
        if username == "" or password == "":
            return True
        return False

    # -------------------------
    # Payload Operations

    def get_default_payload(self,
                            title: str,
                            css: str = "easywall") -> DefaultPayload:
        """Create a object of information that are needed on every page."""
        payload = DefaultPayload()
        payload.year = datetime.today().year
        payload.title = title
        payload.customcss = css
        payload.machine = self.get_machine_infos()
        payload.latest_version = str(self.cfg.get_value("VERSION", "version"))
        payload.current_version = file_get_contents(".version")
        payload.commit_sha = str(self.cfg.get_value("VERSION", "sha"))
        payload.commit_date = self.get_commit_date(
            str(self.cfg.get_value("VERSION", "date")))
        payload.config_mismatch = self.get_config_version_mismatch("core")
        payload.web_config_mismatch = self.get_config_version_mismatch("web")
        return payload

    def get_machine_infos(self) -> dict:
        """Retrieve some information about the host and returns them as a list."""
        infos = {}
        infos["Machine"] = platform.machine()
        infos["Hostname"] = platform.node()
        infos["Platform"] = platform.platform()
        infos["Python Build"] = "".join(platform.python_build())
        infos["Python Compiler"] = platform.python_compiler()
        infos["Python Implementation"] = platform.python_implementation()
        infos["Python Version"] = platform.python_version()
        infos["Release"] = platform.release()
        infos["Libc Version"] = "".join(platform.libc_ver())
        return infos

    def get_config_version_mismatch(self, cfgtype: str) -> bool:
        """TODO: Doku."""
        if cfgtype == "core":
            cfg1 = Config("config/easywall.sample.ini")
            cfg2 = Config("config/easywall.ini")
        elif cfgtype == "web":
            cfg1 = Config("config/web.sample.ini")
            cfg2 = Config("config/web.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

    # -------------------------
    # Update Info Operations

    def get_commit_date(self, datestring: str) -> str:
        """
        Compare a datetime with the current date.

        for comparing the datestring parameter is in UTC timezone
        """
        date1 = datetime.strptime(str(datestring), "%Y-%m-%dT%H:%M:%SZ")
        date1 = date1.replace(tzinfo=timezone.utc).astimezone(tz=None).replace(
            tzinfo=None)
        date2 = datetime.now()
        return time_duration_diff(date1, date2)

    def update_last_commit_infos(self) -> None:
        """
        Retrieve the last commit information after a specific waiting time.

        after retrieving the information they are saved into the config file
        """
        currtime = int(time.time())
        lasttime = int(self.cfg.get_value("VERSION", "timestamp"))
        waitseconds = 3600  # 60 minutes × 60 seconds
        if currtime > (lasttime + waitseconds):
            commit = self.get_latest_commit()
            self.cfg.set_value("VERSION", "version", self.get_latest_version())
            self.cfg.set_value("VERSION", "sha", commit["sha"])
            self.cfg.set_value("VERSION", "date",
                               commit["commit"]["author"]["date"])
            self.cfg.set_value("VERSION", "timestamp", str(currtime))

    def get_latest_commit(self) -> Any:
        """
        Retrieve the informations of the last commit from github as json.

        also converts the information into a python object
        for example the object contains the last commit date and the last commit sha
        This function should not be called very often, because GitHub has a rate limit implemented
        """
        url = "https://api.github.com/repos/jpylypiw/easywall/commits/master"
        req = urllib.request.Request(
            url,
            data=None,
            headers={'User-Agent': 'easywall by github.com/jpylypiw/easywall'})
        if req.get_full_url().lower().startswith("https"):
            response = urllib.request.urlopen(req)
        else:
            raise ValueError from None
        return json.loads(response.read().decode('utf-8'))

    def get_latest_version(self) -> str:
        """Retrieve the latest version from github and returns the version string."""
        url = "https://raw.githubusercontent.com/jpylypiw/easywall/master/.version"
        req = urllib.request.Request(
            url,
            data=None,
            headers={'User-Agent': 'easywall by github.com/jpylypiw/easywall'})
        if req.get_full_url().lower().startswith("https"):
            response = urllib.request.urlopen(req)
        else:
            raise ValueError from None
        data = response.read()
        return str(data.decode('utf-8'))

    # -------------------------
    # Acceptance Operations

    def get_last_accept_time(self) -> str:
        """
        Retrieve the modify time of the acceptance file.

        also compares the time to the current time
        """
        timestamp = str(self.cfg_easywall.get_value("ACCEPTANCE", "timestamp"))
        if timestamp == "":
            return "never"
        timestamp_datetime = datetime.strptime(timestamp,
                                               '%Y-%m-%d %H:%M:%S.%f')
        now = datetime.now()
        return time_duration_diff(timestamp_datetime, now)

    def get_acceptance_status(self) -> str:
        """Get the status of the current acceptance."""
        filepath = ".acceptance_status"
        if file_exists(filepath):
            return file_get_contents(filepath)
        return ""
Esempio n. 19
0
class TestConfig(unittest.TestCase):
    """
    TODO: Doku
    """

    def setUp(self) -> None:
        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 tearDown(self) -> None:
        delete_file_if_exists("test.ini")

    def test_constructor_file_not_found(self) -> None:
        """
        TODO: Doku
        """
        with self.assertRaises(FileNotFoundError):
            Config("test2.ini")

    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 test_get_value_error(self) -> None:
        """
        TODO: Doku
        """
        self.assertEqual(self.config.get_value("TEST", "notexistent"), "")

    def test_get_value_bool(self) -> None:
        """
        TODO: Doku
        """
        self.assertEqual(self.config.get_value("TEST", "testboolean"), True)

    def test_get_value_int(self) -> None:
        """
        TODO: Doku
        """
        self.assertEqual(self.config.get_value("TEST", "testint"), 1)

    def test_get_value_float(self) -> None:
        """
        TODO: Doku
        """
        self.assertEqual(self.config.get_value(
            "TEST", "testfloat"), float(1.1))

    def test_get_sections(self) -> None:
        """
        TODO: Doku
        """
        self.assertIn("TEST", self.config.get_sections())

    def test_set_value_success(self) -> None:
        """
        TODO: Doku
        """
        self.assertEqual(self.config.set_value(
            "TEST", "teststring", "erfolg"), True)

    def test_set_value_fail_section(self) -> None:
        """
        TODO: Doku
        """
        self.assertEqual(self.config.set_value("TEST2", "asd", "asd"), False)
Esempio n. 20
0
class Webutils(object):
    """the class is called in the route modules and contains non route-specific functions"""

    def __init__(self):
        self.cfg = Config("config/web.ini")

    def check_login(self):
        """the function checks if the user/session is logged in"""
        if not session.get('logged_in'):
            return False
        return True

    # -------------------------
    # Payload Operations

    def get_default_payload(self, title, css="easywall"):
        """the function creates a object of information that are needed on every page"""
        payload = DefaultPayload()
        payload.year = datetime.today().year
        payload.title = title
        payload.customcss = css
        payload.machine = self.get_machine_infos()
        payload.latest_version = self.cfg.get_value("VERSION", "version")
        payload.current_version = file_get_contents("{}/../.version".format(get_abs_path_of_filepath(__file__)))
        payload.commit_sha = self.cfg.get_value("VERSION", "sha")
        payload.commit_date = self.get_commit_date(self.cfg.get_value("VERSION", "date"))
        return payload

    def get_machine_infos(self):
        """the function retrieves some information about the host and returns them as a list"""
        infos = {}
        infos["Machine"] = platform.machine()
        infos["Hostname"] = platform.node()
        infos["Platform"] = platform.platform()
        infos["Python Build"] = platform.python_build()
        infos["Python Compiler"] = platform.python_compiler()
        infos["Python Implementation"] = platform.python_implementation()
        infos["Python Version"] = platform.python_version()
        infos["Release"] = platform.release()
        infos["Libc Version"] = platform.libc_ver()
        return infos

    # -------------------------
    # Update Info Operations

    def get_commit_date(self, datestring):
        """
        the function compares a datetime with the current date
        for comparing the datestring parameter is in UTC timezone
        """
        date1 = datetime.strptime(str(datestring), "%Y-%m-%dT%H:%M:%SZ")
        date1 = date1.replace(
            tzinfo=timezone.utc).astimezone(
                tz=None).replace(
                    tzinfo=None)
        date2 = datetime.now()
        return time_duration_diff(date1, date2)

    def update_last_commit_infos(self):
        """
        the function retrieves the last commit information after a specific waiting time
        after retrieving the information they are saved into the config file
        """
        currtime = int(time.time())
        lasttime = int(self.cfg.get_value("VERSION", "timestamp"))
        waitseconds = 3600  # 60 minutes × 60 seconds
        if currtime > (lasttime + waitseconds):
            commit = self.get_latest_commit()
            self.cfg.set_value("VERSION", "version", self.get_latest_version())
            self.cfg.set_value("VERSION", "sha", commit["sha"])
            self.cfg.set_value("VERSION", "date", commit["commit"]["author"]["date"])
            self.cfg.set_value("VERSION", "timestamp", currtime)

    def get_latest_commit(self):
        """
        retrieves the informations of the last commit from github as json
        and converts the information into a python object
        for example the object contains the last commit date and the last commit sha
        This function should not be called very often, because GitHub has a rate limit implemented
        """
        url = "https://api.github.com/repos/jpylypiw/easywall-web/commits/master"
        req = urllib.request.Request(
            url,
            data=None,
            headers={
                'User-Agent': 'easywall by github.com/jpylypiw/easywall-web'
            }
        )
        response = urllib.request.urlopen(req)
        return json.loads(response.read().decode('utf-8'))

    def get_latest_version(self):
        """
        the function retrieves the latest version from github and returns the version string
        """
        url = "https://raw.githubusercontent.com/jpylypiw/easywall-web/master/.version"
        req = urllib.request.Request(
            url,
            data=None,
            headers={
                'User-Agent': 'easywall by github.com/jpylypiw/easywall-web'
            }
        )
        response = urllib.request.urlopen(req)
        data = response.read()
        return data.decode('utf-8')

    # -------------------------
    # Rule Operations

    def get_rule_status(self, ruletype):
        """
        the function checks if a custom / temporary rulefile exists
        and returns "custom" when a temporary rulefile exists or "production" when no file exists
        """
        filepath = self.get_rule_file_path(ruletype, True)
        if not os.path.exists(filepath):
            filepath = self.get_rule_file_path(ruletype)
            if not os.path.exists(filepath):
                return "error"
            return "production"
        return "custom"

    def get_rule_file_path(self, ruletype, tmp=False):
        """
        the function reads the configuration and returns the relative
        or absolute path to the rulefile for the ruletype
        """
        filename = self.cfg.get_value("RULES", ruletype)
        if tmp:
            filepath = self.cfg.get_value("WEB", "rules_tmp_path")
        else:
            filepath = self.cfg.get_value("RULES", "filepath")
            # workaround because the easywall dir is one dir up - this is not pretty
            if filepath.startswith("."):
                filepath = "../" + filepath
        create_folder_if_not_exists(filepath)
        return filepath + "/" + filename

    def get_rule_list(self, ruletype):
        """
        the function reads a file into the ram and returns a list of all rows in a list
        for example you get all the ip addresses of the blacklist in a array
        """
        rule_list = []

        status = self.get_rule_status(ruletype)
        filepath = self.get_rule_file_path(ruletype)
        if status == "custom":
            filepath = self.get_rule_file_path(ruletype, True)

        with open(filepath, 'r') as rulesfile:
            for rule in rulesfile.read().split('\n'):
                if rule.strip() != "":
                    rule_list.append(rule)
        return rule_list

    def save_rule_list(self, ruletype, rulelist, to_production=False):
        """
        the function writes a list of strings into a rulesfile
        for example it saves the blacklist rules into the blacklist temporary rulesfile
        """
        filepath = self.get_rule_file_path(ruletype, True)
        state = self.get_rule_status(ruletype)
        if to_production:
            filepath = self.get_rule_file_path(ruletype)
        try:
            rulelist = list(filter(None, rulelist))
            if not to_production or to_production and state == "custom":
                with open(filepath, mode='wt', encoding='utf-8') as rulesfile:
                    rulesfile.write('\n'.join(rulelist))
        except Exception as exc:
            print("{}".format(exc))
            return False
        return True

    # -------------------------
    # Acceptance Operations

    def apply_rule_list(self, ruletype):
        """
        the function copys the rulefile from the temporary path to the permanent path
        this is used to copy the rules from web to easywall folder
        """
        rule_list = self.get_rule_list(ruletype)
        self.save_rule_list(ruletype, rule_list, True)

    def get_last_accept_time(self):
        """
        the function retrieves the modify time of the acceptance file
        and compares the time to the current time
        """
        filepath = "../" + self.cfg.get_value("ACCEPTANCE", "filename")
        if os.path.exists(filepath):
            mtime = os.path.getmtime(filepath)
            mtime = datetime.utcfromtimestamp(mtime)
            mtime = mtime.replace(
                tzinfo=timezone.utc).astimezone(
                    tz=None).replace(
                        tzinfo=None)
            now = datetime.now()
            return time_duration_diff(mtime, now)
        else:
            return "never"

    def check_acceptance_running(self):
        """
        the function checks if there is a running file.
        """
        filepath = "../.running"
        if os.path.exists(filepath):
            return True
        return False
Esempio n. 21
0
class Webutils(object):
    """the class is called in the route modules and contains non route-specific functions"""
    def __init__(self):
        self.cfg = Config("config/web.ini")
        self.cfg_easywall = Config(CONFIG_PATH)

    def check_login(self):
        """the function checks if the user/session is logged in"""
        if not session.get('logged_in'):
            return False
        return True

    # -------------------------
    # Payload Operations

    def get_default_payload(self, title, css="easywall"):
        """the function creates a object of information that are needed on every page"""
        payload = DefaultPayload()
        payload.year = datetime.today().year
        payload.title = title
        payload.customcss = css
        payload.machine = self.get_machine_infos()
        payload.latest_version = self.cfg.get_value("VERSION", "version")
        payload.current_version = file_get_contents(".version")
        payload.commit_sha = self.cfg.get_value("VERSION", "sha")
        payload.commit_date = self.get_commit_date(
            self.cfg.get_value("VERSION", "date"))
        return payload

    def get_machine_infos(self):
        """the function retrieves some information about the host and returns them as a list"""
        infos = {}
        infos["Machine"] = platform.machine()
        infos["Hostname"] = platform.node()
        infos["Platform"] = platform.platform()
        infos["Python Build"] = platform.python_build()
        infos["Python Compiler"] = platform.python_compiler()
        infos["Python Implementation"] = platform.python_implementation()
        infos["Python Version"] = platform.python_version()
        infos["Release"] = platform.release()
        infos["Libc Version"] = platform.libc_ver()
        return infos

    # -------------------------
    # Update Info Operations

    def get_commit_date(self, datestring):
        """
        the function compares a datetime with the current date
        for comparing the datestring parameter is in UTC timezone
        """
        date1 = datetime.strptime(str(datestring), "%Y-%m-%dT%H:%M:%SZ")
        date1 = date1.replace(tzinfo=timezone.utc).astimezone(tz=None).replace(
            tzinfo=None)
        date2 = datetime.now()
        return time_duration_diff(date1, date2)

    def update_last_commit_infos(self):
        """
        the function retrieves the last commit information after a specific waiting time
        after retrieving the information they are saved into the config file
        """
        currtime = int(time.time())
        lasttime = self.cfg.get_value("VERSION", "timestamp")
        waitseconds = 3600  # 60 minutes × 60 seconds
        if currtime > (lasttime + waitseconds):
            commit = self.get_latest_commit()
            self.cfg.set_value("VERSION", "version", self.get_latest_version())
            self.cfg.set_value("VERSION", "sha", commit["sha"])
            self.cfg.set_value("VERSION", "date",
                               commit["commit"]["author"]["date"])
            self.cfg.set_value("VERSION", "timestamp", str(currtime))

    def get_latest_commit(self):
        """
        retrieves the informations of the last commit from github as json
        and converts the information into a python object
        for example the object contains the last commit date and the last commit sha
        This function should not be called very often, because GitHub has a rate limit implemented
        """
        url = "https://api.github.com/repos/jpylypiw/easywall/commits/master"
        req = urllib.request.Request(
            url,
            data=None,
            headers={'User-Agent': 'easywall by github.com/jpylypiw/easywall'})
        response = urllib.request.urlopen(req)
        return json.loads(response.read().decode('utf-8'))

    def get_latest_version(self) -> str:
        """
        the function retrieves the latest version from github and returns the version string
        """
        url = "https://raw.githubusercontent.com/jpylypiw/easywall/master/.version"
        req = urllib.request.Request(
            url,
            data=None,
            headers={'User-Agent': 'easywall by github.com/jpylypiw/easywall'})
        response = urllib.request.urlopen(req)
        data = response.read()
        return data.decode('utf-8')

    # -------------------------
    # Acceptance Operations

    def get_last_accept_time(self):
        """
        the function retrieves the modify time of the acceptance file
        and compares the time to the current time
        """
        timestamp = self.cfg_easywall.get_value("ACCEPTANCE", "timestamp")
        if timestamp == "":
            return "never"
        timestamp = datetime.strptime(timestamp, '%Y-%m-%d %H:%M:%S.%f')
        now = datetime.now()
        return time_duration_diff(timestamp, now)

    def get_acceptance_status(self):
        """
        get the status of the current acceptance
        """
        filepath = ".acceptance_status"
        if file_exists(filepath):
            return file_get_contents(filepath)
        return ""