#!/usr/bin/python # -*- coding: utf-8 -*- # # IPT.PY -- Gestion du firewall # # Copyright (C) 2010 Olivier Huber # Authors: Olivier Huber # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . import os, re, syslog, cPickle, socket from ldap_crans import crans_ldap, hostname from commands import getstatusoutput from config import NETs, role, prefix, rid, output_file, filter_policy, rid_primaires from config import blacklist_sanctions, blacklist_sanctions_soft, blacklist_bridage_upload, file_pickle, ann_scol, periode_transitoire from iptools import AddrInNet from ridtools import Rid, find_rid_plage import subprocess import netaddr blacklist_sanctions.extend(blacklist_sanctions_soft) blacklist_sanctions.extend(blacklist_bridage_upload) Mangle_policy = """ *mangle :PREROUTING ACCEPT [0:0] :INPUT ACCEPT [0:0] :FORWARD ACCEPT [0:0] :OUTPUT ACCEPT [0:0] :POSTROUTING ACCEPT [0:0] """ Raw_policy = """ *raw :PREROUTING ACCEPT [0:0] :OUTPUT ACCEPT [0:0] """ Filter_policy_template = """ *filter :INPUT %(policy_input)s [0:0] :FORWARD %(policy_forward)s [0:0] :OUTPUT %(policy_output)s [0:0] """ dprefix = { 'adherents': 'adherents', 'fil' : 'fil', 'adherents-v6' : 'fil', 'adm' : 'adm', 'wifi' : 'wifi', 'wifi-adh-v6' : 'wifi','personnel-ens':'personnel-ens', 'serveurs':'serveurs', 'wifi-adh':'wifi', 'bornes' : 'wifi', 'adm-v6':'adm', 'serveurs-v6':'serveurs'} default_chains = [ 'PREROUTING', 'INPUT', 'FORWARD', 'OUTPUT', 'POSTROUTING' ] # On ouvre une connexion avec la base db = crans_ldap() ############################################################################## # # Déclaration des classes pour les règles du firewall # ############################################################################## class Chain(object): ''' Classe regroupant toutes les règles du firewall ''' def __init__(self): self.items = [] def __call__(self, cmd): self.items.append(cmd) class Table(object): ''' Classe contenant toutes les chaînes du firewall ''' # TODO voir si on peut pas créer dynamiquement les méthodes def __init__(self): self.prerouting = Chain() self.input = Chain() self.forward = Chain() self.output = Chain() self.postrouting = Chain() self.mac = Chain() self.macfil = Chain() self.macserveurs = Chain() self.macfilv6 = Chain() self.macadm = Chain() self.macwifi = Chain() self.macwifiv6 = Chain() self.extfil = Chain() self.extfilv6 = Chain() self.extwifi = Chain() self.extwifiv6 = Chain() self.cransfil = Chain() self.cransfilv6 = Chain() self.cranswifi = Chain() self.cranswifiv6 = Chain() self.ieui64 = Chain() self.feui64 = Chain() self.blacklist_src = Chain() self.blacklist_dst = Chain() self.srv_out_adm = Chain() self.ingress_filtering = Chain() self.tracker_torrent = Chain() class Ip6tables(object): ''' Classe pour ''' def __init__(self): self.filter = Table() self.mangle = Table() self.raw = Table() def macip(self, mac, type_m): '''Fait la correspondance MAC-IP''' tab = {'serveurs' : 'fil' } if type_m in tab.keys(): type_m = tab[type_m] type_mm = re.sub('-', '', type_m) getattr(self.filter,'mac' + type_mm)(" ".join(['-m mac --mac-source', mac, '-j RETURN'])) # self.filter.mac(" ".join(['-m mac --mac-source', mac, # '-j RETURN'])) def extcrans(self, type_machine, ports, mac, dev): '''Ouverture des ports de l'extérieur vers la zone crans''' tab = { 'fil' : 'extfil', 'adherents-v6' : 'extfilv6', 'wifi' : 'extwifi', 'wifi-adh-v6' : 'extwifiv6', 'serveurs':'extfil' } ip = ipv6_addr(mac, type_machine) for proto in ['tcp', 'udp']: for port in ports[proto]: if port != ':': getattr(self.filter,tab[type_machine])('-i %s -p %s -d %s --dport %s -j \ ACCEPT' % (dev, proto, ip, port)) else: getattr(self.filter,tab[type_machine])('-i %s -p %s -s %s -j ACCEPT' % (dev, proto, ip)) def cransext(self, type_machine, ports, mac, dev): '''Ouverture des ports de la zone crans vers l'extérieur''' tab = { 'fil' : 'cransfil', 'adherents-v6' : 'cransfilv6', 'wifi' : 'cranswifi', 'wifi-adh-v6' : 'cranswifiv6', 'serveurs':'cransfil' } ip = ipv6_addr(mac, type_machine) for proto in ['tcp', 'udp']: for port in ports[proto]: if port != ':': getattr(self.filter,tab[type_machine])('-i %s -p %s -s %s --sport %s -j \ ACCEPT' % (dev, proto, ip, port)) else: getattr(self.filter,tab[type_machine])('-i %s -p %s -s %s -j ACCEPT' % (dev, proto, ip)) def blacklist(self, machine): ''' Met des règles empêchant toute communication vers et à partir de la machine considérée ''' ident = int(machine.id()) ip = "" for type_m, plages in rid_primaires.iteritems(): for plage in plages: if ident in range(plage[0], plage[1]): ip = ipv6_addr(machine.mac(), type_m) mac=machine.mac() break if ip: self.filter.blacklist_src('-m mac --mac-source %s -j REJECT --reject-with icmp6-port-unreachable' % mac) self.filter.blacklist_dst('-d %s -j REJECT --reject-with icmp6-adm-prohibited' % ip) def version(self): ''' methode retournant la version du protocole ip pour l'instance de la classe ''' return 6 class Update(object): ''' Cette classe comprend toutes les méthodes nécessaires à une mise-à-jour ou un régénération partielle du firewall''' def ports(self, rids, ip_proto = 4): ''' Met à jour les autorisations par machine sur l'ouverture des ports. Prend en argument une liste de rid (entier(s)) de machines à changer''' check_ip_proto(ip_proto) ipt_p = open_pickle(ip_proto) dev_crans = iface6('fil') if ip_proto == 4: dev_ext = iface('ens') elif ip_proto == 6: dev_ext = iface6('sixxs2') net = "" for r in rids: net, _ = find_rid_plage(r) if net == "Inconnu": raise RidError("Il n'y a pas de réseau associé au rid %i" % m) if '-v6' in net and ip_proto == 4: raise MismatchRidIpProto(r, ip_proto, net) machine = db.search('rid=%i' % r)['machine'] if len(machine) != 1: msg = "Il y a %i machines associée(s) au rid %i" % ( len(machine), r) raise RidError(msg) else: if ip_proto == 4: ip = Rid(int(m)).to_ipv4() elif ip_proto == 6: ip = ipv6_addr(machine[0].mac(), net) # On vérifie si la machine a déjà des entrées dans les chaînes # On est un peu sous optimal ici for sens in ['crans', 'ext']: items = getattr(ipt_p.filter,sens + re.sub('-', '',net)).items i = 0 while i < len(items): if ip in items[i] or 'REJECT' in items[i]: del items[i] else: i = i + 1 ports_io(ipt_p, machine[0], net, dev_ext, dev_crans) getattr(ipt_p.filter,'ext' + re.sub('-', '', net))('-j \ REJECT --reject-with icmp6-port-unreachable') getattr(ipt_p.filter,'crans' + re.sub('-', '', net))('-j \ REJECT --reject-with icmp6-port-unreachable') # On écrit et applique les règles write_rules(ipt_p) apply_rules(ip_proto) os.remove(file_pickle[ip_proto]) # On sauve les chaînes save_pickle(ipt_p) return 0 def blacklist(self, ip_proto = 4): ''' Permet de regénérer les diverses blacklistes ''' #TODO vérifier que la fonctionn est _vraiement_ ipv4 aware check_ip_proto(ip_proto) # On supprime les anciennes règles si elles existent. try: os.remove(output_file[ip_proto]) except: pass # On initialise un pare-feu vide ip6tables = Ip6tables() blacklist(ip6tables) # On écrit les données dans un fichier write_rules(ip6tables) #On vide les chaines des BLACKLIST_* flush_chain(ip_proto, ['BLACKLIST_DST', 'BLACKLIST_SRC']) # On les applique en concervant les chaines existantes apply_rules(ip_proto, no_flush=True) #### BACKWARD compatibilité ipt_p = open_pickle(ip_proto) blacklist(ipt_p) # On écrit et applique les règles <-- plus la peine de restorer tout, on ne traite que les chaines BLACKLIST_* ci dessus. # On pickel toujours pour la compatibilité avec les autres fonctions de la classe Update #write_rules(ipt_p) #apply_rules(ip_proto) os.remove(file_pickle[ip_proto]) # On sauve les chaînes save_pickle(ipt_p) return 0 def macs(self, macs, ip_proto = 4): ''' Méthode temporaire le temps de trouver une solution plus élégante l'interface devrait être la même que pour la fonction finale''' check_ip_proto(ip_proto) ipt_p = open_pickle(ip_proto) for type_m in ['fil', 'adherents-v6', 'adm', 'serveurs']: type_mm = re.sub('-', '', type_m) getattr(ipt_p.filter,'mac%s' % type_mm).items[:] = [] machines = db.all_machines(graphic = True) macips(ipt_p, machines, ['fil', 'adherents-v6', 'adm', 'serveurs']) write_rules(ipt_p) apply_rules(ip_proto) os.remove(file_pickle[ip_proto]) save_pickle(ipt_p) return 0 # Il faut voir comment passer les mac en argument # def macs(self, macs, ip_proto = 4): # ''' Fonction pour mettre à jour les adresses mac acceptées sur le réseau. # Prend comme argument un dictionnaire ayant pour clé 'remove' et 'add', # contenant respectivement les adresses mac a ôter et celles à ajouter''' # #TODO vérifier que la fonctionn est _vraiement_ ipv4 aware # ipt_p = open_pickle(ip_proto) # # On modifie la chaîne mac # nb = len(ipt_p.filter.mac.items) # for mac in macs['remove']: # for i in range(1, nb): # if mac in ipt_p.filter.mac.items[i]: # ipt_p.filter.mac.items.remove[i] # break # print "Erreur, la mac " + mac + " n'est pas dans la chaîne." # # for mac in macs['add']: # ipt_p.macip(mac) # # # On écrit et applique les règles # write_rules(ipt_p) # apply_rules(6) # # os.remove(file_pickle[ip_proto]) # # # On sauve les chaînes # save_pickle(ipt_p) # return 0 # ############################################################################## # # Déclaration des exceptions # ############################################################################## class NoIface(Exception): ''' Exception invoquée lorsqu'il n'y a pas d'interface associé à un réseau''' def __init__(self, net, msg): self.net = net self.msg = msg syslog.syslog(syslog.LOG_ERR,"Pas d'interface associé à %s" % self.net) syslog.syslog(syslog.LOG_ERR,"Résultat de la commande : \n %s" % self.msg) def __str__(self): txt = "Pas d'interface associé à %s \n \ Résultat de la commande : \n %s" % (self.net, self.msg) return txt class Iproute2Error(Exception): ''' Exception invoquée lorsqu'un appel à iproute2 renvoie une erreur''' def __init__(self, cmd, code, msg): self.cmd = cmd self.code = code self.msg = msg syslog.syslog(syslog.LOG_ERR,"%s : %s %s" % (self.cmd, self.code, self.msg)) def __str__(self): return "%s: %s %s" % ( self.cmd, self.code, self.msg) class NoRtTable(Exception): ''' Exception invoquée lorsqu'une table de routage n'existe pas''' def __init__(self, msg): self.msg = msg syslog.syslog(syslog.LOG_ERR,"La table donnée n'existe pas : %s" % self.msg) def __str__(self): return "La table donnée n'existe pas : %s" % self.msg class ApplyRulesError(Exception): ''' Exception invoquée lorsque l'application des règles par ip(6|)tables-restore échoue ''' def __init__(self, cmd, code, msg): self.cmd = cmd self.code = code self.msg = msg syslog.syslog(syslog.LOG_ERR,"%s : %s \n %s" % (self.cmd, self.code, self.msg)) def __str__(self): return "%s : %s \n %s" % (self.cmd, self.code, self.msg) class SysctlError(Exception): ''' Exception invoquée lorsque la modification d'un état dans /proc/sys échoue ''' def __init__(self, cmd, code, msg): self.cmd = cmd self.code = code self.msg = msg syslog.syslog(syslog.LOG_ERR,"%s : %s %s" % (self.cmd, self.code, self.msg)) def __str__(self): return "%s: %s %s" % ( self.cmd, self.code, self.msg) class Ip6tablesError(Exception): def __init__(self, cmd, code, msg): self.cmd = cmd self.code = code self.msg = msg syslog.syslog(syslog.LOG_ERR,"%s : %s %s" % (self.cmd, self.code, self.msg)) def __str__(self): return "%s: %s %s" % ( self.cmd, self.code, self.msg) class RidError(Exception): ''' Exception invoquée lorsqu'il y a un soucis avec un rid fourni ''' def __init__(self, msg): self.msg = msg syslog.syslog(syslog.LOG_ERR,"%s" % self.msg) def __str__(self): return "%s" % self.msg class MismatchRidIpProto(Exception): ''' Exception invoquée lorsqu'il y a un défaut d'appariement entre le rid et le protocole ip concerné''' def __init__(self, mrid, ip_proto, net): self.mrid = mrid self.ip_proto = ip_proto self.net = net self.msg = "Défaut d'appariement entre le rid %i (%s) \ et le protocole ip %i" % (self.mrid, self.net, self.ip_proto) syslog.syslog(syslog.LOG_ERR, self.msg) def __str__(self): return self.msg class IpProtoError(Exception): ''' Exception quand le protocole ip fourni est inconnu ''' def __init__(self, proto): self.msg = "Le protocole ip %i est inconnu" % proto syslog.syslog(syslog.LOG_ERR,"%s" % self.msg) def __str__(self): return "%s" % self.msg class UnknowUserError(Exception): ''' Exception quand l'utilisateur demandé n'existe pas sur la machine ''' def __init__(self, user): self.msg = "L'utilisateur %s est inconnu" % user syslog.syslog(syslog.LOG_ERR,"%s" % self.msg) def __str__(self): return "%s" % self.msg ############################################################################## # # Déclaration des fonctions # ############################################################################## def gethostbyname(hostname): hosts4=[] hosts6=[] try : for host in socket.getaddrinfo(hostname,None,socket.AF_INET,socket.IPPROTO_IP,socket.AI_CANONNAME): hosts4.append(host[4][0]) except(socket.gaierror): pass try : for host in socket.getaddrinfo(hostname,None,socket.AF_INET6,socket.IPPROTO_IP,socket.AI_CANONNAME): hosts6.append(host[4][0]) except(socket.gaierror): pass return (hosts4,hosts6) def check_ip_proto(ip_proto): ''' Vérifie que le protocole ip fourni est valide ''' if ip_proto != 4 and ip_proto != 6: raise IpProtoError(ip_proto) def ipv6_addr(mac, net): ''' Renvoie l'adresse ipv6 d'auto-configuration de la mac sur le réseau ''' mac_s = mac.split(':') eui = hex(int(mac_s[0],16) ^ 0x02)[2:] + ':'.join(mac_s[1:3]) + 'ff:fe' + ':'.join(mac_s[3:5]) + mac_s[5] return re.sub(':/64', eui , prefix[dprefix[net]][0]) def mac_addr(ipv6): ''' Renvoie l'adresse mac de l'ipv6 d'auto-configuration ''' ipv6_s= ipv6.split(':')[4:] mac='' if ipv6_s[1].endswith('ff') and ipv6_s[2].startswith('fe'): elt = "%04x" % int(ipv6_s[0], 16) mac += "%02x" % (int(elt[0:2],16) ^ 0x02) + ':' + elt[2:] elt = "%04x" % int(ipv6_s[1], 16) mac += ':' + elt[0:2] elt = "%04x" % int(ipv6_s[2], 16) mac += ':' + elt[2:] elt = "%04x" % int(ipv6_s[3], 16) mac += ':' + elt[0:2] + ':' + elt[2:] return mac return None # TODO Fusionner les deux fonctions. def iface(net): '''Retourne l'interface réseau associée à un certain type de réseau Pour l'instant on se base sur l'ipv4 pour identifier l'interface''' if net == 'adherents-v6': net = 'fil' if net == 'wifi-adh-v6': net = 'wifi' cmd = "ip a show| egrep 'inet[^6]'" code, msg = getstatusoutput(cmd) if code: raise Iproute2Error(cmd, code, msg) output = msg.splitlines() for line in output: if AddrInNet(line.split()[1].split('/')[0], NETs[net]): return line.split()[-1] raise NoIface(net, msg) def iface6(net): '''Retourne l'interface réseau associée à un certain type de réseau ipv6''' cmd = "ip -6 a show" code, msg = getstatusoutput(cmd) if code: raise Iproute2Error(cmd, code, msg) output = msg.splitlines() subnet = netaddr.IPNetwork(prefix[net][0]) i = 0 while i < len(output): if re.match('^[0-9]+:', output[i]): dev = re.sub('(@.*|:)', '', output[i].split()[1]) i = i + 1 while (i < len(output)) and ('inet6' in output[i]): if netaddr.IPNetwork(output[i].split()[1]) == subnet: return dev else: i = i + 2 else: i = i + 1 raise NoIface(net, msg) def check_table(table): ''' Vérifie que la table existe bien ''' ctables = open('/etc/iproute2/rt_tables', 'r') rt_tables = ctables.readlines() tables = [item for item in rt_tables if not re.match('#', item)] if any(re.search(table, elt) for elt in tables): return 0 else: raise NoRtTable(table) def custom_default_route(table, default, net, ip_proto = 4): ''' Permet de créer une route par default personnalisée''' check_ip_proto(ip_proto) check_table(table) dev = iface(net) cmd = "ip -%i route add table %s default via %s dev %s" % (ip_proto, table, default, dev) code, msg = getstatusoutput(cmd) if code: raise Iproute2Error(cmd, code, msg) def custom_subnet_route(table, subnet, net, ip_proto = 4): ''' Permet de créer une route personnalisée''' check_ip_proto(ip_proto) check_table(table) dev = iface(net) cmd = "ip -%i route add table %s from %s dev %s" % (ip_proto, table, subnet, dev) code, msg = getstatusoutput(cmd) if code: raise Iproute2Error(cmd, code, msg) def custom_subnet_via_route(table, subnet, net, via, ip_proto = 4): ''' Permet de créer une route personnalisée''' check_ip_proto(ip_proto) check_table(table) dev = iface(net) cmd = "ip -%i route add table %s from %s via %s dev %s" % (ip_proto, table, subnet, via, dev) code, msg = getstatusoutput(cmd) if code: raise Iproute2Error(cmd, code, msg) def custom_ip_rule(table, subnet, ip_proto = 4): ''' Ajout d'un règle de routage personnalisée basée sur un sous-reseaux de provenance''' check_ip_proto(ip_proto) check_table(table) cmd = "ip -%i rule add from %s table %s" % (ip_proto, subnet, table) code, msg = getstatusoutput(cmd) if code: raise Iproute2Error(cmd, code, msg) def custom_mark_rule(table, fw_mark, ip_proto = 4): ''' Ajout d'une règle de routage personnalisée basée sur une marque''' check_ip_proto(ip_proto) check_table(table) cmd = "ip -%i rule add fwmark %s table %s" % (ip_proto, fw_mark, table) code, msg = getstatusoutput(cmd) if code: raise Iproute2Error(cmd, code, msg) def srv(attribute): ''' Renvoie le (ou les) serveur(s) qui possède l'attribut ''' return [ server for server, attr in role.iteritems() if (attribute in attr)] def ip_attribut(attribute, cl, ip_proto = 4): ''' Renvoie l'ip du serveur qui possède l'attribut ''' check_ip_proto(ip_proto) host = srv(attribute)[0] #Gestion erreur ? # Autre net que fil ? if ip_proto == 4: return cl.search('host=%s.crans.org' % host)['machine'][0].ip() elif ip_proto == 6: mac = cl.search('host=%s.crans.org' % host)['machine'][0].mac() if mac: return ipv6_addr(mac, 'fil') else: print "Error" + " host=%s.crans.org" % name def flush_chain(ip_proto = 4, chain_list = []): check_ip_proto(ip_proto) f = open(output_file[ip_proto], 'r') if ip_proto == 4: cmd = '/sbin/iptables' elif ip_proto == 6: cmd = '/sbin/ip6tables' for chain in chain_list: run = [cmd, '-F', chain] try: p1 = subprocess.Popen(run, stdin=f, stdout=subprocess.PIPE) except OSError: syslog.syslog(syslog.LOG_ERR,"La chaine %s n'existe pas" % chain) print "La chaine %s n'existe pas" % chain stdout, stderr = p1.communicate() def apply_rules(ip_proto = 4, no_flush=False): ''' Applique les règles fournies dans le fichier passé en arguement ''' check_ip_proto(ip_proto) f = open(output_file[ip_proto], 'r') if ip_proto == 4: cmd = '/sbin/iptables-restore' elif ip_proto == 6: cmd = '/sbin/ip6tables-restore' if no_flush: cmd = [cmd, '--noflush'] try: p1 = subprocess.Popen(cmd, stdin=f, stdout=subprocess.PIPE) except OSError: syslog.syslog(syslog.LOG_ERR,"Le fichier %s n'existe pas" % f.name) print "Le fichier %s n'existe pas" % f.name stdout, stderr = p1.communicate() if p1.returncode < 0: raise ApplyRulesError(cmd, -p1.returncode, 'STDOUT\n' + stdout + 'STDERR\n' + stderr ) def enable_forwarding(ip_proto = 4): ''' Autorise le forwarding sur la machine considérée ''' check_ip_proto(ip_proto) cmd = 'echo 1 > /proc/sys/net/ipv%i/conf/all/forwarding' % ip_proto code, msg = getstatusoutput(cmd) if code: raise SysctlError(cmd, code, msg) def disable_forwarding(ip_proto = 4): ''' Désactive le forwarding sur la machine considérée ''' check_ip_proto(ip_proto) cmd = 'echo 0 > /proc/sys/net/ipv%i/conf/all/forwarding' % ip_proto code, msg = getstatusoutput(cmd) if code: raise SysctlError(cmd, code, msg) def not_private(arg): ''' Retourne un boolén suivant que la fonction passé en argument est privée ou non''' if re.match('^_.*', str(arg)): return False else: return True def open_pickle(ip_proto = 4): ''' Cette fonction ouvre un fichier contenant un dump d'une classe Ip(|6)tables''' check_ip_proto(ip_proto) try: f = open(file_pickle[ip_proto], 'r') except OSError: syslog.syslog(syslog.LOG_ERR,"Le fichier %s n'existe pas" % file_pickle[ip_proto]) print "Le fichier %s n'existe pas" % file_pickle[ip_proto] #XXX gestion des erreurs return cPickle.load(f) def save_pickle(ipt): ''' Cette fonction sauvegarde une instance de Ip(|6)tables''' fout = open(file_pickle[ipt.version()], 'w') cPickle.dump(ipt, fout) fout.close() def write_rules(ipt): ''' Cette fonction écrit les règles dans un fichier ''' # TODO vérifier que la fonctionn est _vraiement_ ipv4 aware ip_proto = ipt.version() fout = open(output_file[ip_proto], 'w') # On recherche si la machine a des politiques particulières if hostname in filter_policy.keys(): key = hostname else: key = 'default' # On applique les politiques par défaut sur les chaînes canoniques output = { 'filter' : Filter_policy_template % filter_policy[key], 'mangle' : Mangle_policy, 'raw' : Raw_policy } # On parcours une première fois l'instance pour initialiser correctement # nos chaînes for itables in ['filter', 'mangle', 'raw']: for ichain in filter(not_private, dir(ipt.__getattribute__(itables))): if ichain.upper() not in default_chains: if hasattr(ipt.__getattribute__(itables).__getattribute__(ichain),'items') and ipt.__getattribute__(itables).__getattribute__(ichain).items: output[itables] += ":%s - [0:0]\n" % ichain.upper() # On ajoute maintenant les règles for itables in ['filter', 'mangle', 'raw']: for ichain in filter(not_private, dir(ipt.__getattribute__(itables))): for rule in ipt.__getattribute__(itables).__getattribute__(ichain).items: output[itables] += '-A ' + ichain.upper() + ' ' + rule + '\n' output['filter'] += '-A FORWARD -j REJECT\n' # Ecriture dans le fichier fout.writelines(output['filter']) fout.write('COMMIT\n') fout.write('') fout.writelines(output['mangle']) fout.write('COMMIT\n') fout.write('') fout.writelines(output['raw']) fout.write('COMMIT\n') fout.close() def blacklist(ipt): ''' Permet de peupler les chaînes concernant les diverses blacklistes ''' blcklst = [] s = db.search('paiement=%s' % ann_scol) if periode_transitoire: tmp=db.search('paiement=%s' % ann_scol-1) s['adherent'].entend(tmp['adherent']) s['club'].entend(tmp['club']) del tmp for target in s['adherent'] + s['club']: sanctions = target.blacklist_actif() if [x for x in sanctions if x in blacklist_sanctions]: blcklst.extend(target.machines()) s = db.search('mblacklist=*&paiement=%s' % ann_scol) if periode_transitoire: s['machine'].entend(db.s('mblacklist=*&paiement=%s' % ann_scol-1)['machine']) for target in s['machine']: sanctions = target.blacklist_actif() if [x for x in sanctions if x in blacklist_sanctions]: blcklst.append(target) if ipt.filter.blacklist_src.items: ipt.filter.blacklist_src.items[:] = [] if ipt.filter.blacklist_dst.items: ipt.filter.blacklist_dst.items[:] = [] for machine in blcklst: ipt.blacklist(machine) return 0 def ports_io(ipt, machine, type_machine, dev_ext, dev_crans): ''' Fonction ouvrant les ports d'une machine selon le contenu de la base LDAP''' ports_in = { 'tcp' : machine.portTCPin(), 'udp' : machine.portUDPin() } ports_out = { 'tcp' : machine.portTCPout(), 'udp' : machine.portUDPout() } if ports_in.values() != [[], []]: ipt.extcrans(type_machine, ports_in, machine.mac(), dev_ext) if ports_out.values() != [[], []]: ipt.cransext(type_machine, ports_out, machine.mac(), dev_crans) def mac_ip(ipt, machines, types_machines): '''Réalise une correspondance MAC-IP pour un réseau considéré On vérifie d'abord que la MAC est connue et ensuite on n'accepte que les adresses en eui64''' tab = {'serveurs' : 'fil' } macips(ipt, machines, types_machines) for type_m in types_machines: if not '-v6' in type_m and not type_m in tab.keys(): dev = iface6(type_m) ipt.filter.input('-i %s -s %s -j %s' % (dev, prefix[type_m][0], 'MAC' + type_m.upper())) ipt.filter.input('-i %s -j IEUI64' % dev) # On active les extensions de vie privée for net in prefix['subnet']: ipt.filter.ieui64('-s %s -j RETURN' % net) # Pour le lien local, on n'accepte que les eui64 ipt.filter.ieui64('-s fe80::/64 -m eui64 -j RETURN') ipt.filter.ieui64('-j REJECT') def macips(ipt, machines, types_machines): ''' Construit la chaîne MAC ''' tab = {'serveurs' : 'fil' } for machine in machines: for type_m in types_machines: for plage in rid[type_m]: if int(machine.rid()) in range(plage[0], plage[1]): ipt.macip(machine.mac(), type_m) break for type_m in types_machines: if not type_m in tab.keys(): type_mm = re.sub('-', '', type_m) getattr(ipt.filter,'mac' + type_mm)('-j REJECT') #eval('ipt.filter.mac' + type_mm)('-j REJECT') return 0 def ingress_filtering(ipt): ''' Réalise un filtre sur les plages d'IP susceptibles d'être routées ''' ip_proto = ipt.version() if ip_proto == 6: dev_ext = iface6('sixxs2') # d'abord sur l'interface sur le réseau Cr@ns, on ne route que les # paquet dans le bon subnet. ipt.filter.ingress_filtering('-o %s -s %s -j RETURN' % (dev_ext, prefix['subnet'][0])) ipt.filter.ingress_filtering('-o %s -j LOG --log-prefix "BAD ROUTE "' % dev_ext) ipt.filter.ingress_filtering('-o %s -j REJECT' % dev_ext) # de l'extérieur, on ne veut que des paquet ne provenant pas de notre # réseau à destination de notre réseau ipt.filter.ingress_filtering('-i %s ! -s %s -d %s -j RETURN' % (dev_ext, prefix['subnet'][0], prefix['subnet'][0])) ipt.filter.ingress_filtering('-i %s -j LOG --log-prefix "BAD SRC "' % dev_ext) ipt.filter.ingress_filtering('-i %s -j REJECT' % dev_ext)