From 6f5090f1d724f42963e6b44e97faba82c0fcb91e Mon Sep 17 00:00:00 2001 From: pauget Date: Sun, 14 Nov 2004 23:15:27 +0100 Subject: [PATCH] Esthtisme et gestion des erreurs iptables. darcs-hash:20041114221527-41617-7f8d80ddc5569f3f77edf5ad3dc0141969821612.gz --- gestion/classe_firewall.py | 372 ++++++++++++++++++++++++------------- 1 file changed, 244 insertions(+), 128 deletions(-) diff --git a/gestion/classe_firewall.py b/gestion/classe_firewall.py index 51306691..2f2debc7 100755 --- a/gestion/classe_firewall.py +++ b/gestion/classe_firewall.py @@ -15,10 +15,13 @@ # MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR # PURPOSE. """ classe Firewall de Komaz """ -import commands,time, syslog +import syslog import iptools,config -from ldap_crans import crans_ldap +import ldap_crans from affich_tools import * +from commands import getstatusoutput +from iptools import AddrInNet +syslog.openlog('firewall') class ErrorArgument(Exception): """ @@ -42,26 +45,23 @@ class ErrorMoreThanOneIp(ErrorIp): """ pass -class ErrorIptables(Exception): - """ - User defined exception - Les erreurs d'iptables - """ +class IptablesError(Exception): + """ Gestion des erreurs d'iptables """ def __init__(self,cmd,err_code,output): self.cmd=cmd self.err_code=err_code self.output=output - openlog(ident='syslog',logopt=0,facility=' LOG_USER') - syslog('LOG_ALERT',cmd,err,output) - -def iptables(self,cmd): + syslog.syslog(syslog.LOG_ERR,"%s : status %s,%s" % (cmd,err_code,output)) + def __str__(self): + return "%s\n status : %s\n %s" % (self.cmd,self.err_code,self.output) + +def iptables(cmd): + """ Interface à iptables  """ + syslog.syslog(syslog.LOG_INFO,cmd) status,output=getstatusoutput("/sbin/iptables "+cmd) if status: raise IptablesError(cmd,status,output) - self.file_log.write(time.time+": "+cmd) - return output - class ErrorNoSuchIp(ErrorIp): """ User defined exception @@ -74,23 +74,21 @@ class firewall: Structure du firewall : table nat : PREROUTING (policy par defaut : DROP) - 1) passage par TEST_VIRUS + 1) passage par TEST_VIRUS_FLOOD pour tout ce qui n'est pas dans zone_serveur 2) passage dans RESEAUX_NON_ROUTABLES_DST 3) passage est paquets venant de l'extérieur dans RESEAUX_NON_ROUTABLES_SRC 4) on laisse passer vers filter les paquets suivants : source ou destination les serveurs de serveurs_crans ce qui vient de l'extérieur - 7) passage par TEST_MAC-IP_FLOOD - 8) passage par TEST_FLOOD + 5) passage par TEST_MAC-IP - TEST_VIRUS droppe les paquets contenant des virus + TEST_VIRUS_FLOOD droppe les paquets contenant des virus ou les paquets de flood RESEAUX_NON_ROUTABLES_DST droppe les paquets dont la destination est non routable RESEAUX_NON_ROUTABLES_SRC droppe les paquets dont la source est non routable - TEST_MAC-IP_FLOOD envoi les bon paquets vers CRANS_VERS_EXT + TEST_MAC-IP envoi les bon paquets vers CRANS_VERS_EXT CRANS_VERS_EXT ACCEPT pour les paquets venant des machines du crans (test port-ip) REJECT pour le reste - TEST_FLOOD log puis drop des paquests avec bonne mac-ip (donc les floods) table filter : FORWARD (policy par defaut : ACCEPT) @@ -110,16 +108,19 @@ class firewall: """ zone_serveur="138.231.136.0/28" eth_ext = "eth2" - ports_default = { 'tcp_input' : ['22','1024:'], - 'tcp_output': [':79','81:134','136','140:444','446:'], - 'udp_input' : [''], - 'udp_output': [':136','140:'] } + # Ports ouverts + ports_default = { 'tcp_input' : [ '22' ], + 'tcp_output': [ ':79', '81:134', '136', '140:444', '446:'], + 'udp_input' : [ ], + 'udp_output': [ ':136','140:'] } + mac_wifi = '00:0c:f1:fa:f1:4b' limit = " -m limit --limit 10/s --limit-burst 10 " log_template = '-m limit --limit 1/s --limit-burst 1 -j LOG --log-level notice --log-prefix ' + filtre_flood = '-m dstlimit --dstlimit 20 --dstlimit-mode srcip-dstip --dstlimit-name flood' - reseaux_non_routables = [ '1.0.0.0/8','2.0.0.0/8','5.0.0.0/8','7.0.0.0/8',\ + liste_reseaux_non_routables = [ '1.0.0.0/8','2.0.0.0/8','5.0.0.0/8','7.0.0.0/8',\ '10.0.0.0/8','14.0.0.0/8','23.0.0.0/8','27.0.0.0/8','31.0.0.0/8','36.0.0.0/8',\ '37.0.0.0/8','39.0.0.0/8','41.0.0.0/8','42.0.0.0/8','71.0.0.0/8','72.0.0.0/8',\ '73.0.0.0/8','74.0.0.0/8','75.0.0.0/8','76.0.0.0/8','77.0.0.0/8','78.0.0.0/8',\ @@ -139,194 +140,308 @@ class firewall: ports_virus = { 'tcp' : [ 135, 445 ] , 'udp' : [] } - def __init__(self): - self.db = crans_ldap() - self.file_log=open("/var/log/fw.log","a") + machines = [] + debug = 1 + + def __machines(self) : + """ Liste des machines du crans """ + if not self.machines : + self.anim = anim(" Interrogation de la base LDAP") + self.machines = ldap_crans.crans_ldap().search('ip=*')['machine'] + print OK + return self.machines - def __del__(self): - self.file_log.close() - def start(self) : """ Construction du firewall """ - # On commence sur de bonnes bases - self.stop() - cprint('Démarrage firewall','gras') + # Préliminaires + if not self.__machines() or self.stop() : + print "Abandon" + return - anim('\tConstruction structure de la table nat') - for chaine in [ 'TEST_VIRUS' , 'TESTS_MAC-IP_FLOOD' , 'RESEAUX_NON_ROUTABLES_SRC', 'RESEAUX_NON_ROUTABLES_DST', 'TEST_FLOOD'] : + self.anim = anim(' Construction structure de la table nat') + for chaine in [ 'LOG_VIRUS', 'LOG_FLOOD', 'TEST_VIRUS_FLOOD' , 'TEST_MAC-IP' , 'RESEAUX_NON_ROUTABLES_SRC', 'RESEAUX_NON_ROUTABLES_DST' ] : iptables('-t nat -N %s' % chaine) - iptables("-t nat -P PREROUTING ACCEPT") - iptables("-t nat -i lo -j ACCEPT") - iptables("-t nat -A PREROUTING -j TEST_VIRUS") - iptables("-t nat -A PREROUTING -j RESEAUX_NON_ROUTABLES_DST") - iptables("-t nat -A PREROUTING -i %s -j RESEAUX_NON_ROUTABLES_SRC" % self.eth_ext ) - iptables("-t nat -A PREROUTING -d %s -j ACCEPT" % self.zone_serveur ) - iptables("-t nat -A PREROUTING -s %s -j ACCEPT" % self.zone_serveur ) - iptables("-t nat -A PREROUTING -i %s -j ACCPET" % self.eth_ext ) - iptables("-t nat -A PREROUTING -j TESTS_MAC-IP_FLOOD") - print OK + try : + iptables("-t nat -P PREROUTING ACCEPT") + iptables("-t nat -A PREROUTING -i lo -j ACCEPT") + iptables("-t nat -A PREROUTING -s ! %s -j TEST_VIRUS_FLOOD" % self.zone_serveur) + iptables("-t nat -A PREROUTING -j RESEAUX_NON_ROUTABLES_DST") + iptables("-t nat -A PREROUTING -i %s -j RESEAUX_NON_ROUTABLES_SRC" % self.eth_ext ) + iptables("-t nat -A PREROUTING -d %s -j ACCEPT" % self.zone_serveur ) + iptables("-t nat -A PREROUTING -s %s -j ACCEPT" % self.zone_serveur ) + iptables("-t nat -A PREROUTING -i %s -j ACCEPT" % self.eth_ext ) + iptables("-t nat -A PREROUTING -j TEST_MAC-IP") + iptables("-t nat -P PREROUTING DROP") + print OK - anim('\tConstruction structure de la table filter') - for chaine in [ 'EXT_VERS_SERVEURS', 'SERVEURS_VERS_EXT' , 'EXT_VERS_CRANS', 'CRANS_VERS_EXT', 'BLACKLIST_SRC', 'BLACKLIST_DST' ] : - iptables('-N %s' % chaine) + self.anim = anim(' Construction structure de la table filter') + for chaine in [ 'EXT_VERS_SERVEURS', 'SERVEURS_VERS_EXT' , 'EXT_VERS_CRANS', 'CRANS_VERS_EXT', 'BLACKLIST_SRC', 'BLACKLIST_DST' ] : + iptables('-N %s' % chaine) - iptables("-A FORWARD -i %s -j BLACKLIST_DST" % self.eth_ext ) - iptables("-A FORWARD -o %s -j BLACKLIST_SRC" % self.eth_ext ) - iptables("-A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT") - iptables("-A FORWARD -i %s -d %s -j EXT_VERS_SERVEURS" % (self.eth_ext, proto, self.zone_serveur) ) - iptables("-A FORWARD -o %s -s %s -j SERVEURS_VERS_EXT" % (self.eth_ext, proto, self.zone_serveur) ) - iptables("-A FORWARD -i %s -j EXT_VERS_CRANS" % self.eth_ext ) - iptables("-A FORWARD -o %s -j CRANS_VERS_EXT" % self.eth_ext ) - print OK + iptables("-A FORWARD -i lo -j ACCEPT") + iptables("-A FORWARD -i %s -j BLACKLIST_DST" % self.eth_ext ) + iptables("-A FORWARD -o %s -j BLACKLIST_SRC" % self.eth_ext ) + iptables("-A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT") + iptables("-A FORWARD -i %s -d %s -j EXT_VERS_SERVEURS" % (self.eth_ext, self.zone_serveur) ) + iptables("-A FORWARD -o %s -s %s -j SERVEURS_VERS_EXT" % (self.eth_ext, self.zone_serveur) ) + iptables("-A FORWARD -i %s -j EXT_VERS_CRANS" % self.eth_ext ) + iptables("-A FORWARD -o %s -j CRANS_VERS_EXT" % self.eth_ext ) + print OK - self.log_chaines() - self.test_virus() - self.reseaux_non_routables() - self.blacklist() - - for machine in self.db.search('ip=*')['machine'] : - add_machine(machine) - - # Opérations finales - anim('\tFinalisation') - for chaine in [ 'EXT_VERS_SERVEURS', 'SERVEURS_VERS_EXT' , 'EXT_VERS_CRANS' , 'CRANS_VERS_EXT'] : - iptables('-A %s -j REJECT' % chaine) - print OK + except IptablesError, c : + print ERREUR + if self.debug : print c + self.stop() + return 1 + except : + print ERREUR + self.stop() + if self.debug : + import traceback + traceback.print_exc() + return 1 + + for tache in [ self.log_chaines, self.test_virus_flood, self.reseaux_non_routables, + self.blacklist , self.serveurs_vers_ext, self.ext_vers_serveurs, + self.crans_vers_ext, self.ext_vers_crans, self.test_mac_ip ] : + try : + tache() + except IptablesError, c : + self.anim.reinit() + print ERREUR + if self.debug : print c + except : + self.anim.reinit() + print ERREUR + import traceback + if self.debug : traceback.print_exc() def log_chaines(self) : """ Construction des chaines de log (LOG_VIRUS et LOG_FLOOD) """ - self.anim = anim('\tCréation des chaines de log',len(self.reseaux_non_routables)) + self.anim = anim(' Création des chaines de log') for filtre in [ 'VIRUS', 'FLOOD' ] : # Vidage de la chaîne - iptables('-F LOG_%s' % filtre) + iptables('-t nat -F LOG_%s' % filtre) iptables('-t nat -A LOG_%s %s %s:' % (filtre, self.log_template, filtre.capitalize()) ) iptables('-t nat -A LOG_%s -j DROP' % filtre ) self.anim.cycle() + self.anim.reinit() print OK def reseaux_non_routables(self) : """ Construction de RESEAUX_NON_ROUTABLES_{DST,SRC} """ - self.anim = anim('\tFiltrage ip non routables',len(self.reseaux_non_routables)) - for reseau in self.reseaux_non_routables : + self.anim = anim(' Filtrage ip non routables',len(self.liste_reseaux_non_routables)) + for reseau in self.liste_reseaux_non_routables : iptables("-t nat -A RESEAUX_NON_ROUTABLES_DST -d %s -j DROP" % reseau) iptables("-t nat -A RESEAUX_NON_ROUTABLES_SRC -s %s -j DROP" % reseau) self.anim.cycle() self.anim.reinit() print OK - def test_virus(self) : + def test_virus_flood(self) : """ Construction de la chaîne TEST_VIRUS """ - iptables('-t nat -F TEST_VIRUS') - self.anim = anim('\tFiltrage virus',len(self.ports_virus.keys())) + iptables('-t nat -F TEST_VIRUS_FLOOD') + self.anim = anim(' Filtrage virus et floods') for proto, ports in self.ports_virus.items() : - iptables('-t nat -A TEST_VIRUS -p %s --dport %s -j LOG_VIRUS' % (proto, port) ) - self.anim.cycle() + for port in ports : + iptables('-t nat -A TEST_VIRUS_FLOOD -p %s --dport %s -j LOG_VIRUS' % (proto, port) ) + self.anim.cycle() + iptables('-t nat -A TEST_VIRUS_FLOOD %s -j RETURN' % self.filtre_flood) # Les limites en négatif ca ne marche pas. + self.anim.cycle() + iptables('-t nat -A TEST_VIRUS_FLOOD -j LOG_FLOOD') self.anim.reinit() print OK def stop(self): - """ - Arrête le firewall - Pas d'arguments - """ - print "Arrêt du firewall" - iptables("iptables -F") - iptables("iptables -t nat -F") - iptables("iptables -X") + """ Arrête le firewall """ + self.anim = anim(" Arrêt du firewall") + try : + iptables("-t nat -P PREROUTING ACCEPT") + iptables("-F") + iptables("-t nat -F") + iptables("-X") + iptables("-t nat -X") + print OK + return 0 + except IptablesError, c : + print ERREUR + if self.debug : print c + except : + print ERREUR + if self.debug : + import traceback + traceback.print_exc() + return 1 + + def test_mac_ip(self) : + """ Reconstruit la correspondance MAC-IP des machines des adhérents """ + self.anim = anim(' Construction de la chaîne TEST_MAC-IP',len(self.__machines())+1) + iptables("-t nat -P PREROUTING ACCEPT") + iptables("-t nat -F TEST_MAC-IP") + self.anim.cycle() + try: + for machine in self.__machines() : + self.__test_mac_ip(machine) + self.anim.cycle() + iptables("-t nat -P PREROUTING DROP") + self.anim.reinit() + print OK + except IptablesError, c : + self.anim.reinit() + print ERREUR + if self.debug : print c + except : + self.anim.reinit() + print ERREUR + if self.debug : + import traceback + traceback.print_exc() + + def serveurs_vers_ext(self) : + """ Reconstruit la chaine SERVEURS_VERS_EXT """ + self.__build_chaine('SERVEURS_VERS_EXT', self.__serveurs_vers_ext) + + def ext_vers_serveurs(self) : + """ Reconstruit la chaine EXT_VERS_SERVEURS """ + self.__build_chaine('EXT_VERS_SERVEURS', self.__ext_vers_serveurs) + def crans_vers_ext(self) : + """ Reconstruit la chaine CRANS_VERS_EXT """ + self.__build_chaine('CRANS_VERS_EXT', self.__crans_vers_ext) + # Défauts + self.anim.reinit() + for proto in [ 'tcp' , 'udp' ] : + for port in self.ports_default["%s_input" % proto ] : + self.anim.cycle() + iptables("-A CRANS_VERS_EXT -p %s --dport %s -j ACCEPT" % (proto,port) ) + self.anim.reinit() + print OK + + def ext_vers_crans(self) : + """ Reconstruit la chaine EXT_VERS_CRANS """ + self.__build_chaine('EXT_VERS_CRANS', self.__ext_vers_crans) + # Défauts + self.anim.reinit() + for proto in [ 'tcp' , 'udp' ] : + for port in self.ports_default["%s_output" % proto ] : + self.anim.cycle() + iptables("-A EXT_VERS_CRANS -p %s --dport %s -j ACCEPT" % (proto,port) ) + self.anim.reinit() + print OK + + def __build_chaine(self,chaine, methode) : + self.anim = anim(' Construction de la chaîne %s' % chaine,len(self.__machines())+1) + iptables("-F %s" % chaine) + self.anim.cycle() + try: + for machine in self.__machines() : + methode(machine) + self.anim.cycle() + iptables("-A %s -j REJECT" % chaine) + self.anim.reinit() + print OK + except IptablesError, c : + self.anim.reinit() + print ERREUR + if self.debug : print c + except : + self.anim.reinit() + print ERREUR + if self.debug : + import traceback + traceback.print_exc() + def add_machine(self,machine): - self.__test_mac_ip_flood(machine) - self.__test_flood(machine) self.__serveurs_vers_ext(machine) self.__ext_vers_serveurs(machine) self.__crans_vers_ext(machine) self.__ext_vers_crans(machine) + self.__test_mac_ip(machine) def __serveurs_vers_ext(self,machine): ip=machine.ip() - if AddrInNet(ip,self.zone_serveur): - for i in machine.portTCPout().split(): - iptables("-t filter -A SERVEURS_VERS_EXT -d "+\ - "%s -p tcp --dport %s -j ACCEPT"\ - %(ip,i)) - for i in machine.portUDPout().split(): - iptables("-t filter -A SERVEURS_VERS_EXT-d "+\ - "%s -p udp --dport %s -j ACCEPT"\ - %(ip,i)) + mac = machine.mac() + ports = { 'tcp' : machine.portTCPin(), + 'udp' : machine.portUDPin() } + + if not AddrInNet(ip,self.zone_serveur): + # C'est une machine adhérent, rien à faire ici + return + + for proto in [ 'tcp', 'udp' ] : + for port in ports[proto].split() : + iptables("-I CRANS_VERS_EXT -s %s -p %s --dport %s -m mac --mac-source %s -j ACCEPT" \ + %(ip,proto,port,mac)) def __ext_vers_serveurs(self,machine): ip=machine.ip() - if AddrInNet(ip,self.zone_serveur): - for i in machine.portTCPin().split(): - iptables("-t filter -I EXT_VERS_SERVEURS "+\ - "-s %s -p tcp --dport %s -j ACCEPT"\ - %(ip,i)) - for i in machine.portUDPin().split(): - iptables("-t filter -I EXT_VERS_SERVEURS "+\ - "-s %s -p udp --dport %s -j ACCEPT"\ - %(ip,i)) + if not AddrInNet(ip,self.zone_serveur): + # C'est une machine adhérent, rien à faire ici + return + + ports = { 'tcp' : machine.portTCPin(), + 'udp' : machine.portUDPin() } + + for proto in [ 'tcp', 'udp' ] : + for port in ports[proto].split() : + iptables("-I EXT_VERS_SERVEURS -s %s -p %s --dport %s -j ACCEPT"\ + %(ip,proto,port)) def __crans_vers_ext(self,machine): ip=machine.ip() if AddrInNet(ip,self.zone_serveur): + # C'est un serveur, rien à faire ici return ports = { 'tcp' : machine.portTCPin(), 'udp' : machine.portUDPin() } for proto in [ 'tcp', 'udp' ] : - for port in ports[proto] + self.ports_default["%s_input" % proto ] : + for port in ports[proto].split() : iptables("-I CRANS_VERS_EXT -s %s -p %s --dport %s -j ACCEPT" \ %(ip,proto,port)) def __ext_vers_crans(self,machine): ip=machine.ip() if AddrInNet(ip,self.zone_serveur): + # C'est un serveur, rien à faire ici return ports = { 'tcp' : machine.portTCPout(), 'udp' : machine.portUDPout() } for proto in [ 'tcp', 'udp' ] : - for port in ports[proto] + self.ports_default["%s_output" % proto ] : + for port in ports[proto].split() : iptables("-I EXT_VERS_CRANS -d %s -p %s --dport %s -j ACCEPT" \ %(ip,proto,port)) - def __test_mac_ip_flood(machine): + def __test_mac_ip(self,machine): ip=machine.ip() mac=machine.mac() if machine.ipsec(): - iptables("-t nat -A PREROUTING -s "+\ - "%s -m mac --mac-source %s %s -j ACCEPT"%(ip,self.mac_wifi,self.limit)) + # Machine wifi, c'est la mac de Nectaris + iptables("-t nat -A TEST_MAC-IP -s "+\ + "%s -m mac --mac-source %s -j ACCEPT"%(ip,self.mac_wifi)) else: - iptables("-t nat -A PREROUTING -s "+\ - "%s -m mac --mac-source %s %s -j ACCEPT"%(ip,mac,self.limit)) - - - def __test_flood(machine): - ip=machine.ip() - mac=machine.mac() - if machine.ipsec(): - iptables("-t nat -A PREROUTING -s "+\ - "%s -m mac --mac-source %s -j LOG_FLOOD"%(ip,self.mac_wifi)) - else: - iptables("-t nat -A PREROUTING -s "+\ - "%s -m mac --mac-source %s -j LOG_FLOOD"%(ip,mac)) + # Machine fixe + iptables("-t nat -A TEST_MAC-IP -s "+\ + "%s -m mac --mac-source %s -j ACCEPT"%(ip,mac)) def blacklist(self): """ Construit les chaines de blackliste (BLACKLIST_{DST,SRC}) """ - anim("\tContruction blacklist") + self.anim = anim(" Contruction blackliste") iptables('-F BLACKLIST_DST') iptables('-F BLACKLIST_SRC') blacklist=[] - search=self.db.search('blacklist=*&paiement=%s'%ann_scol) + search=ldap_crans.crans_ldap().search('blacklist=*&paiement=%s'%ldap_crans.ann_scol) for entite in search['adherent']+search['club']+search['machine']: + self.anim.cycle() sanctions = entite.blacklist_actif() if 'upload' in sanctions or 'warez' in sanctions : - if search.__class__==machine: + if search.__class__ == ldap_crans.machine: blacklist+=[entite] else: blacklist+=entite.machines() @@ -335,6 +450,7 @@ class firewall: iptables("-A BLACKLIST_DST -d %s -j REJECT" % machine.ip()) iptables("-A BLACKLIST_SRC -s %s -j REJECT" % machine.ip()) + self.anim.reinit() print OK def del_entree(self,ip):