diff --git a/gestion/config/firewall.py b/gestion/config/firewall.py index 7cb12f55..b899a7b2 100644 --- a/gestion/config/firewall.py +++ b/gestion/config/firewall.py @@ -4,7 +4,7 @@ """ Variables de configuration pour le firewall """ import datetime - +#: Interfaces réseaux des machines ayant un pare-feu particulié dev = { 'komaz': { 'out' : 'ens', @@ -51,12 +51,13 @@ else: #: Est-ce qu'on est en connexion de jour ou de nuit/week-end ? debit_jour = False - +#: Liste des réseaux non routables reseaux_non_routables = [ '10.0.0.0/8', '172.16.0.0/12','198.18.0.0/15', '169.254.0.0/16', '192.168.0.0/16', '224.0.0.0/4', '100.64.0.0/10', '0.0.0.0/8','127.0.0.0/8','192.0.2.0/24','198.51.100.0/24','203.0.113.0/24', ] +#: Ports ouverts à défaut pour les adhérents dans le pare-feu ports_default = { 'tcp' : { 'input' : [ '22' ], diff --git a/gestion/gen_confs/firewall4.py b/gestion/gen_confs/firewall4.py index 4533246e..ecf4a725 100755 --- a/gestion/gen_confs/firewall4.py +++ b/gestion/gen_confs/firewall4.py @@ -7,11 +7,6 @@ import sys sys.path.append('/usr/scripts/gestion') sys.path.append('/usr/scripts/lc_ldap') -if os.getuid() != 0: - from affich_tools import coul - sys.stderr.write(coul("Il faut être root pour utiliser le firewall\n", 'gras')) - sys.exit(1) - from config import NETs, blacklist_sanctions, blacklist_sanctions_soft, mac_komaz, mac_titanic, adm_users, accueil_route import pwd @@ -26,15 +21,21 @@ from affich_tools import anim, OK, cprint squeeze = os.uname()[2] < '3' +#: Connection à labase ldap conn = lc_ldap.lc_ldap_admin() +#: Nom de la machine exécutant le script hostname = socket.gethostname() +#: Chaines par défaut d'iptables default_chains = ['INPUT', 'OUTPUT', 'FORWARD', 'PREROUTING', 'POSTROUTING'] +#: Tables d'iptables tables = ['raw', 'mangle', 'filter', 'nat'] +#: Association des interfaces de ``hostname`` dev = hostname in config.firewall.dev.keys() and config.firewall.dev[hostname] or {} def pretty_print(table, chain): + """Affiche quelle chaine est en train d'être construite dans quelle table de NetFilter""" anim('\t%s dans %s' % (chain, table)) @@ -61,14 +62,17 @@ def tc(cmd, block=True): return stdoutdata + stderrdata class firewall_base(object) : + """Classe de base du pare-feu implémentant l'association mac-ip (pour les machines filaires) et les blacklists hard""" def machines(self): + """Renvois la liste de toutes les machines""" if self._machines: return self._machines self._machines, self._adherents = conn.allMachinesAdherents() return self._machines def adherents(self): + """Renvois la liste de tous les adhérents""" if self._adherents: return self._adherents self._machines, self._adherents = conn.allMachinesAdherents() @@ -76,6 +80,7 @@ class firewall_base(object) : return self._adherents def blacklisted_machines(self): + """Renvois la liste de toutes les machines ayant une blackliste actives""" if self._blacklisted_machines: return self._blacklisted_machines if self._machines: @@ -93,18 +98,21 @@ class firewall_base(object) : return self._blacklisted_machines def blacklisted_adherents(self): + """Renvois la liste de tous les adhérents ayant une blackliste active""" if self._blacklisted_adherents: return self._blacklisted_adherents self._blacklisted_adherents = filter(lambda adh: adh.blacklist_actif(), self.adherents()) return self._blacklisted_adherents def add(self, table, chain, rule): + """Ajoute la règle ``rule`` à la chaine ``chain`` dans la table ``table``""" if not chain in self.chain_list[table]: self.chain_list[table].append(chain) self.rules_list[table][chain]=[] self.rules_list[table][chain].append(rule) def delete(self, table=None, chain=None): + """Supprime ``chain`` de ``table`` si fournis, sinon supprime ``table``, sinon toutes les tables""" if not table: for table in tables: self.delete(table, chain) @@ -121,6 +129,8 @@ class firewall_base(object) : self.rules_list[table][chain]=[] def flush(self, table=None, chain=None): + """Vide ``chain`` dans ``table`` si fournis, sinon vide toutes + les chaines de ``table``, sinon vide toutes les chaines de toutes les tables""" if not table: for table in tables: self.flush(table, chain) @@ -132,6 +142,9 @@ class firewall_base(object) : self.rules_list[table][chain]=[] def restore(self, table=None, chains=[], noflush=False): + """Restores les règles du pare-feu dans le noyau en appelant ``iptables-restore``. + Si ``table`` est fournis, on ne restore que table. + Si ``noflush`` n'est pas à ``True`` tout le contenu précédent est supprimé""" str=self.format(chains) f=open('/tmp/ipt_rules', 'w') f.write(str) @@ -146,12 +159,14 @@ class firewall_base(object) : p.communicate(input=str) def apply(self, table, chain): + """Applique les règles de ``chain`` dans ``table``""" if not chain in self.chain_list[table]: return self.restore(table, [chain], noflush=True) self.delete(table, chain) def format(self, chains=[]): + """Transforme la structure interne des règles du pare-feu en celle comprise par ``iptables-restore``""" str = '' for table in self.chain_list.keys(): str += '*%s\n' % table @@ -167,6 +182,11 @@ class firewall_base(object) : def __init__(self): #initialisation des structures communes : récupération des ipset + if os.getuid() != 0: + from affich_tools import coul + sys.stderr.write(coul("Il faut être root pour utiliser le firewall\n", 'gras')) + sys.exit(1) + self.reloadable = { 'blacklist_hard' : self.blacklist_hard, @@ -210,7 +230,7 @@ class firewall_base(object) : def start(self): - # On precache avant tout la liste des machines et des adhérents, on en aura besoin + """Démarre le pare-feu : génère les règles, puis les restore""" anim('\tChargement des machines') self.machines() print OK @@ -231,27 +251,32 @@ class firewall_base(object) : return def stop(self): - + """Vide les règles du pare-feu""" fw.delete() fw.restore() return def restart(self): + """Alias de :py:func:`start`""" self.start() return def blacklist_maj(self, ips): + """Met à jours les blacklists pour les ip présentent dans la liste ``ips``""" self.blacklist_hard_maj(ips) def raw_table(self): + """Génère les règles pour la table ``raw`` et remplis les chaines de la table""" table = 'raw' return def mangle_table(self): + """Génère les règles pour la table ``mangle`` et remplis les chaines de la table""" table = 'mangle' return def filter_table(self): + """Génère les règles pour la table ``filter`` et remplis les chaines de la table""" table = 'filter' mac_ip_chain = self.test_mac_ip(table, fill_ipset=True) @@ -271,6 +296,7 @@ class firewall_base(object) : return def nat_table(self): + """Génère les règles pour la table ``nat`` et remplis les chaines de la table""" table = 'nat' return @@ -278,6 +304,7 @@ class firewall_base(object) : def blacklist_hard_maj(self, ip_list): + """Met à jour les blacklists hard, est appelée par :py:func:`blacklist_maj`""" for ip in ip_list: machine = conn.search("ipHostNumber=%s" % ip) # Est-ce qu'il y a des blacklists hard parmis les blacklists de la machine @@ -289,6 +316,9 @@ class firewall_base(object) : except IpsetError: pass def blacklist_hard(self, table=None, fill_ipset=False, apply=False): + """Génère la chaine ``BLACKLIST_HARD``. + Si ``fill_ipset`` est à ``True``, remplis l'ipset ``BLACKLIST-HARD``. + Si ``apply`` est à True, applique directement les règles""" chain = 'BLACKLIST_HARD' if fill_ipset: @@ -317,7 +347,7 @@ class firewall_base(object) : return chain def test_mac_ip_dispatch(self, func, machine): - """Détermine à quel set de mac-ip appliquer la fonction func (add, delete, append, ...)""" + """Détermine à quel set de mac-ip appliquer la fonction ``func`` (add, delete, append, ...)""" ips = machine['ipHostNumber'] for ip in ips: # Si la machines est sur le réseau des adhérents @@ -331,6 +361,9 @@ class firewall_base(object) : func('adm', "%s,%s" % (ip, machine['macAddress'][0])) def test_mac_ip(self, table=None, fill_ipset=False, apply=False): + """Génère la chaine ``TEST_MAC-IP``. + Si ``fill_ipset`` est à ``True``, remplis les ipsets ``MAC-IP-ADH``, ``MAC-IP-ADM``, ``MAC-IP-ADM``. + Si ``apply`` est à True, applique directement les règles""" chain = 'TEST_MAC-IP' if fill_ipset: @@ -369,6 +402,7 @@ class firewall_base(object) : return chain def mac_ip_maj(self, ip_list): + """Met à jour la correspondance mac-ip""" for ip in ip_list: machine = conn.search("ipHostNumber=%s" % ip) if machine: @@ -523,6 +557,7 @@ class firewall_komaz(firewall_base): return def clamp_mss(self, table=None, apply=False): + """Force la MSS (Max Segment Size) TCP à rentrer dans la MTU (Max Transfert Unit)""" chain = 'CLAMP-MSS' if table == 'mangle': pretty_print(table, chain) @@ -535,10 +570,8 @@ class firewall_komaz(firewall_base): return chain def ingress_filtering(self, table=None, apply=False): - """ - Pour ne pas router les paquêtes n'appartenant pas à notre plage ip voulant sortir de notre réseau - et empêcher certain type de spoof (cf http://travaux.ovh.net/?do=details&id=5183) - """ + """Pour ne pas router les paquêtes n'appartenant pas à notre plage ip voulant sortir de notre réseau + et empêcher certain type de spoof (cf http://travaux.ovh.net/?do=details&id=5183)""" chain = 'INGRESS_FILTERING' if table == 'filter': pretty_print(table, chain) @@ -559,6 +592,7 @@ class firewall_komaz(firewall_base): return chain def ssh_on_https(self, table=None, apply=False): + """Pour faire fonctionner ssh2.crans.org""" chain = 'SSH2' if table == 'nat': @@ -572,6 +606,7 @@ class firewall_komaz(firewall_base): return chain def connexion_secours(self, table=None, apply=False): + """Redirige les paquets vers un proxy lorsqu'on est en connexion de secours""" chain = 'CONNEXION-SECOURS' if table == 'mangle': @@ -596,6 +631,7 @@ class firewall_komaz(firewall_base): return chain def connexion_appartement(self, table=None, apply=False): + """PNAT les appartements derrière appartement.crans.org""" chain = 'CONNEXION-APPARTEMENT' if table == 'nat': @@ -628,6 +664,7 @@ class firewall_komaz(firewall_base): except IpsetError: pass def blacklist_soft(self, table=None, fill_ipset=False, apply=False): + """Redirige les gens blacklisté vers le portail captif""" chain = 'BLACKLIST_SOFT' if fill_ipset: @@ -670,6 +707,7 @@ class firewall_komaz(firewall_base): return chain def reseaux_non_routable(self, table=None, fill_ipset=False, apply=False): + """Bloque les réseaux non routables autres que ceux utilisés par le crans""" chain = 'RESEAUX_NON_ROUTABLES' if fill_ipset: @@ -695,6 +733,7 @@ class firewall_komaz(firewall_base): self.filtrage_ports('filter', apply=True) def filtrage_ports(self, table=None, apply=False): + """Ouvre les ports vers et depuis les machines du réseau crans""" chain = 'FILTRAGE-PORTS' def format_port(port): @@ -745,6 +784,7 @@ class firewall_komaz(firewall_base): return chain def limitation_debit(self, table=None, run_tc=False, apply=False): + """Limite le débit de la connexion selon l'agréement avec l'ENS""" chain = 'LIMITATION-DEBIT' debit_max = config.firewall.debit_max @@ -896,7 +936,8 @@ class firewall_zamok(firewall_base): print OK def blacklist_output(self, table=None, apply=False): - chain='BLACKLIST' + """Empêche les gens blacklisté d'utiliser zamok comme relaie""" + chain='BLACKLIST-OUTPUT' if table == 'filter': self.add(table, chain, '-d 127.0.0.1/8 -j ACCEPT') @@ -954,6 +995,7 @@ class firewall_routeur(firewall_base): return def portail_captif_route(self, table=None, apply=False): + """PNAT les (ip,port) à laisser passer à travers le portail captif""" chain = 'CAPTIF-ROUTE' if table == 'filter': @@ -979,6 +1021,7 @@ class firewall_routeur(firewall_base): return chain def portail_captif(self, table=None, apply=False): + """Redirige vers le portail captif""" chain = 'PORTAIL-CAPTIF' if table == 'nat': diff --git a/gestion/gen_confs/generate.py b/gestion/gen_confs/generate.py index 41dc2f36..3a51d8db 100755 --- a/gestion/gen_confs/generate.py +++ b/gestion/gen_confs/generate.py @@ -4,14 +4,14 @@ # Copyright (C) Frédéric Pauget # Licence : GPLv2 -"""Ce script permet de lancer la reconfiguration des divers services - -Usage: %(prog)s options -Les options possibles sont : -\t%(options)s -Les options avec = doivent être suivies d'un argument. Plusieurs -arguments peuvent être founis pour une même option, les séparer par & -""" +#"""Ce script permet de lancer la reconfiguration des divers services +# +#Usage: %(prog)s options +#Les options possibles sont : +#\t%(options)s +#Les options avec = doivent être suivies d'un argument. Plusieurs +#arguments peuvent être founis pour une même option, les séparer par & +#""" import sys, signal, os, getopt @@ -27,11 +27,6 @@ from syslog import * import platform openlog("generate") -# On vérifie que l'on est root -if os.getuid() != 0: - sys.stderr.write("Il faut être root\n") - sys.exit(1) - signal.signal(signal.SIGINT, signal.SIG_IGN) # Pas de Ctrl-C db = crans_ldap() @@ -73,6 +68,11 @@ class base_reconfigure: def __init__(self, to_do=None): + # On vérifie que l'on est root + if os.getuid() != 0: + sys.stderr.write("Il faut être root\n") + sys.exit(1) + if not to_do: if debug: print 'Lecture des services à redémarrer dans la base LDAP...'