]> git.llucax.com Git - software/pymin.git/blobdiff - services/firewall/__init__.py
Add GPL v3 license to the project
[software/pymin.git] / services / firewall / __init__.py
index 1ac1e60e6763c0b13082feaba9619195d30d0c46..6c1cbe30aaf2793b1a83e0a1cfc00c919cdcdcd4 100644 (file)
 # vim: set encoding=utf-8 et sw=4 sts=4 :
 
 # vim: set encoding=utf-8 et sw=4 sts=4 :
 
-# TODO See if it's better (more secure) to execute commands via python instead
-# of using script templates.
-
-from mako.template import Template
-from mako.runtime import Context
-from os import path
-try:
-    import cPickle as pickle
-except ImportError:
-    import pickle
-
-try:
-    from seqtools import Sequence
-except ImportError:
-    # NOP for testing
-    class Sequence: pass
-try:
-    from dispatcher import Handler, handler, HandlerError
-except ImportError:
-    # NOP for testing
-    class HandlerError(RuntimeError): pass
-    class Handler: pass
-    def handler(help):
-        def wrapper(f):
-            return f
-        return wrapper
-
-__ALL__ = ('FirewallHandler', 'Error', 'RuleError', 'RuleAlreadyExistsError',
-           'RuleNotFoundError')
-
-pickle_ext = '.pkl'
-pickle_rules = 'rules'
-
-config_filename = 'iptables.sh'
-
-template_dir = path.join(path.dirname(__file__), 'templates')
-
-class Error(HandlerError):
-    r"""
-    Error(command) -> Error instance :: Base FirewallHandler exception class.
-
-    All exceptions raised by the FirewallHandler inherits from this one, so you can
-    easily catch any FirewallHandler exception.
-
-    message - A descriptive error message.
-    """
-
-    def __init__(self, message):
-        r"Initialize the Error object. See class documentation for more info."
-        self.message = message
-
-    def __str__(self):
-        return self.message
-
-class RuleError(Error, KeyError):
-    r"""
-    RuleError(rule) -> RuleError instance.
-
-    This is the base exception for all rule related errors.
-    """
-
-    def __init__(self, rule):
-        r"Initialize the object. See class documentation for more info."
-        self.message = 'Rule error: "%s"' % rule
-
-class RuleAlreadyExistsError(RuleError):
-    r"""
-    RuleAlreadyExistsError(rule) -> RuleAlreadyExistsError instance.
-
-    This exception is raised when trying to add a rule that already exists.
-    """
-
-    def __init__(self, rule):
-        r"Initialize the object. See class documentation for more info."
-        self.message = 'Rule already exists: "%s"' % rule
-
-class RuleNotFoundError(RuleError):
-    r"""
-    RuleNotFoundError(rule) -> RuleNotFoundError instance.
-
-    This exception is raised when trying to operate on a rule that doesn't
-    exists.
-    """
-
-    def __init__(self, rule):
-        r"Initialize the object. See class documentation for more info."
-        self.message = 'Rule not found: "%s"' % rule
-
-class Rule(Sequence):
-    r"""Rule(chain, target[, src[, dst[, ...]]]) -> Rule instance.
-
-    chain - INPUT, OUTPUT or FORWARD.
-    target - ACCEPT, REJECT or DROP.
-    src - Source subnet as IP/mask.
-    dst - Destination subnet as IP/mask.
-    protocol - ICMP, UDP, TCP or ALL.
-    src_port - Source port (only for UDP or TCP protocols).
-    dst_port - Destination port (only for UDP or TCP protocols).
-    """
-
-    def __init__(self, chain, target, src=None, dst=None, protocol=None,
-                       src_port=None, dst_port=None):
-        r"Initialize object, see class documentation for details."
-        self.chain = chain
-        self.target = target
-        self.src = src
-        self.dst = dst
-        self.protocol = protocol
-        # TODO Validate that src_port and dst_port could be not None only
-        # if the protocol is UDP or TCP
-        self.src_port = src_port
-        self.dst_port = dst_port
-
-    def update(self, chain=None, target=None, src=None, dst=None, protocol=None,
-                       src_port=None, dst_port=None):
-        r"update([chain[, ...]]) -> Update the values of a rule (see Rule doc)."
-        if chain is not None: self.chain = chain
-        if target is not None: self.target = target
-        if src is not None: self.src = src
-        if dst is not None: self.dst = dst
-        if protocol is not None: self.protocol = protocol
-        # TODO Validate that src_port and dst_port could be not None only
-        # if the protocol is UDP or TCP
-        if src_port is not None: self.src_port = src_port
-        if dst_port is not None: self.dst_port = dst_port
-
-    def __cmp__(self, other):
-        r"Compares two Rule objects."
-        if self.chain == other.chain \
-                and self.target == other.target \
-                and self.src == other.src \
-                and self.dst == other.dst \
-                and self.protocol == other.protocol \
-                and self.src_port == other.src_port \
-                and self.dst_port == other.dst_port:
-            return 0
-        return cmp(id(self), id(other))
-
-    def as_tuple(self):
-        r"Return a tuple representing the rule."
-        return (self.chain, self.target, self.src, self.dst, self.protocol,
-                    self.src_port)
-
-class RuleHandler(Handler):
-    r"""RuleHandler(rules) -> RuleHandler instance :: Handle a list of rules.
-
-    This class is a helper for FirewallHandler to do all the work related to rules
-    administration.
-
-    rules - A list of Rule objects.
-    """
-
-    def __init__(self, rules):
-        r"Initialize the object, see class documentation for details."
-        self.rules = rules
-
-    @handler(u'Add a new rule.')
-    def add(self, *args, **kwargs):
-        r"add(rule) -> None :: Add a rule to the rules list (see Rule doc)."
-        rule = Rule(*args, **kwargs)
-        if rule in self.rules:
-            raise RuleAlreadyExistsError(rule)
-        self.rules.append(rule)
-
-    @handler(u'Update a rule.')
-    def update(self, index, *args, **kwargs):
-        r"update(index, rule) -> None :: Update a rule (see Rule doc)."
-        # TODO check if the modified rule is the same of an existing one
-        index = int(index) # TODO validation
-        try:
-            self.rules[index].update(*args, **kwargs)
-        except IndexError:
-            raise RuleNotFoundError(index)
-
-    @handler(u'Delete a rule.')
-    def delete(self, index):
-        r"delete(index) -> Rule :: Delete a rule from the list returning it."
-        index = int(index) # TODO validation
-        try:
-            return self.rules.pop(index)
-        except IndexError:
-            raise RuleNotFoundError(index)
-
-    @handler(u'Get information about a rule.')
-    def get(self, index):
-        r"get(rule) -> Rule :: Get all the information about a rule."
-        index = int(index) # TODO validation
-        try:
-            return self.rules[index]
-        except IndexError:
-            raise RuleNotFoundError(index)
-
-    @handler(u'Get information about all rules.')
-    def show(self):
-        r"show() -> list of Rules :: List all the complete rules information."
-        return self.rules
-
-class FirewallHandler(Handler):
-    r"""FirewallHandler([pickle_dir[, config_dir]]) -> FirewallHandler instance.
-
-    Handles firewall commands using iptables.
-
-    pickle_dir - Directory where to write the persistent configuration data.
-
-    config_dir - Directory where to store de generated configuration files.
-
-    Both defaults to the current working directory.
-    """
-
-    def __init__(self, pickle_dir='.', config_dir='.'):
-        r"Initialize FirewallHandler object, see class documentation for details."
-        self.pickle_dir = pickle_dir
-        self.config_dir = config_dir
-        filename = path.join(template_dir, config_filename)
-        self.template = Template(filename=filename)
-        try:
-            self._load()
-        except IOError:
-            # This is the first time the handler is used, create a basic
-            # setup using some nice defaults
-            self.rules = list() # TODO defaults?
-            self._dump()
-            self._write_config()
-        self.rule = RuleHandler(self.rules)
-
-    # Does this (start, stop, restart, reload) makes sense??? 
-    # Implement a "try" command that apply the changes for some time and
-    # then goes back to the previous configuration if the changes are not
-    # commited. TODO
-    @handler(u'Start the service.')
-    def start(self):
-        r"start() -> None :: Start the firewall."
-        #esto seria para poner en una interfaz
-        #y seria el hook para arrancar el servicio
-        pass
-
-    @handler(u'Stop the service.')
-    def stop(self):
-        r"stop() -> None :: Stop the firewall."
-        #esto seria para poner en una interfaz
-        #y seria el hook para arrancar el servicio
-        pass
-
-    @handler(u'Restart the service.')
-    def restart(self):
-        r"restart() -> None :: Restart the firewall."
-        #esto seria para poner en una interfaz
-        #y seria el hook para arrancar el servicio
-        pass
-
-    @handler(u'Reload the service config (without restarting, if possible).')
-    def reload(self):
-        r"reload() -> None :: Reload the configuration of the firewall."
-        #esto seria para poner en una interfaz
-        #y seria el hook para arrancar el servicio
-        pass
-
-    @handler(u'Commit the changes (reloading the service, if necessary).')
-    def commit(self):
-        r"commit() -> None :: Commit the changes and reload the firewall."
-        #esto seria para poner en una interfaz
-        #y seria que hace el pickle deberia llamarse
-        #al hacerse un commit
-        self._dump()
-        self._write_config()
-        self.reload() # TODO exec the script
-
-    @handler(u'Discard all the uncommited changes.')
-    def rollback(self):
-        r"rollback() -> None :: Discard the changes not yet commited."
-        self._load()
-
-    def _dump(self):
-        r"_dump() -> None :: Dump all persistent data to pickle files."
-        # XXX podría ir en una clase base
-        self._dump_var(self.rules, pickle_rules)
-
-    def _load(self):
-        r"_load() -> None :: Load all persistent data from pickle files."
-        # XXX podría ir en una clase base
-        self.rules = self._load_var(pickle_rules)
-
-    def _pickle_filename(self, name):
-        r"_pickle_filename() -> string :: Construct a pickle filename."
-        # XXX podría ir en una clase base
-        return path.join(self.pickle_dir, name) + pickle_ext
-
-    def _dump_var(self, var, name):
-        r"_dump_var() -> None :: Dump a especific variable to a pickle file."
-        # XXX podría ir en una clase base
-        pkl_file = file(self._pickle_filename(name), 'wb')
-        pickle.dump(var, pkl_file, 2)
-        pkl_file.close()
-
-    def _load_var(self, name):
-        r"_load_var() -> object :: Load a especific pickle file."
-        # XXX podría ir en una clase base
-        return pickle.load(file(self._pickle_filename(name)))
-
-    def _write_config(self):
-        r"_write_config() -> None :: Generate all the configuration files."
-        # XXX podría ir en una clase base, ver como generalizar variables a
-        # reemplazar en la template
-        out_file = file(path.join(self.config_dir, config_filename), 'w')
-        ctx = Context(out_file, rules=self.rules)
-        self.template.render_context(ctx)
-        out_file.close()
-
-if __name__ == '__main__':
-
-    import os
-
-    fw_handler = FirewallHandler()
-
-    def dump():
-        print '-' * 80
-        print 'Rules:'
-        print fw_handler.rule.show()
-        print '-' * 80
-
-    dump()
-
-    fw_handler.rule.add('input','drop','icmp')
-
-    fw_handler.rule.update(0, dst='192.168.0.188/32')
-
-    fw_handler.rule.add('output','accept', '192.168.1.0/24')
-
-    fw_handler.commit()
-
-    dump()
-
-    for f in (pickle_rules + pickle_ext, config_filename):
-        os.unlink(f)
+from formencode import validators as V
+from pymin.config import Option
+from handler import FirewallHandler
+
+def setup_service(options, config):
+    options.add_group('firewall', 'Firewall service', [
+        Option('pickle_dir', V.String, metavar='DIR',
+               help='store persistent data in DIR directory'),
+        Option('config_dir', V.String, metavar='DIR',
+               help='write config file in DIR directory'),
+    ])
+
+def get_service(config):
+    return FirewallHandler(config.firewall.pickle_dir, config.firewall.config_dir)