scripts/gestion/ipt.py
Pierre-Elliott Bécue a442fd6b22 [global/rid] J'ai essayé de mettre en place une bijection realm <-> NETs <-> rid
* Les rid sont maintenant des listes de tuples.
 * Il faut utiliser crans_utils.find_rid_plage, et crans_utils.find_ipv4_plage
 qui renvoient le realm d'un rid/d'une ipv4, et la plage associée (un tuple ou
 un slash ipv4).
 * Pour les anciens scripts, j'ai mis find_rid_plage dans ridtools, il faut l'utiliser
 également à la place des boucles for tp, (begin, end) in config.rid.iteritems()...
 * J'ai essayé de faire attention à tout ce que ça aurait pu casser, mais ce n'est
 pas garanti
2013-05-30 01:11:36 +02:00

843 lines
30 KiB
Python

#!/usr/bin/python
# -*- coding: utf-8 -*-
#
# IPT.PY -- Gestion du firewall
#
# Copyright (C) 2010 Olivier Huber
# Authors: Olivier Huber <huber@crans.org>
#
# 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 <http://www.gnu.org/licenses/>.
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', 'fil-v6' : 'fil', 'adm' : 'adm', 'wifi' : 'wifi',
'wifi-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', 'fil-v6' : 'extfilv6',
'wifi' : 'extwifi',
'wifi-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', 'fil-v6' :
'cransfilv6', 'wifi' : 'cranswifi',
'wifi-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', 'fil-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', 'fil-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 == 'fil-v6':
net = 'fil'
if net == 'wifi-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)