
Python nazis are back darcs-hash:20120119121655-28565-ec11f555ca24cb553d4f9fc4c5a33b2241c2c6c0.gz
752 lines
26 KiB
Python
Executable file
752 lines
26 KiB
Python
Executable file
#!/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
|
|
|
|
from ldap_crans import crans_ldap, hostname
|
|
from commands import getstatusoutput
|
|
from config import NETs, role, prefix, mid, output_file, filter_policy
|
|
from config import blacklist_sanctions, file_pickle
|
|
from iptools import AddrInNet
|
|
from midtools import Mid
|
|
import subprocess
|
|
import netaddr
|
|
|
|
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 = { 'fil' : 'fil', 'fil-v6' : 'fil', 'adm' : 'adm', 'wifi' : 'wifi',
|
|
'wifi-v6' : 'wifi' }
|
|
|
|
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.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'''
|
|
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' }
|
|
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' }
|
|
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 in mid.keys():
|
|
if ident in range(mid[type_m][0], mid[type_m][1]):
|
|
ip = ipv6_addr(machine.mac(), type_m)
|
|
break
|
|
if ip:
|
|
self.filter.blacklist_src('-s %s -j REJECT --reject-with \
|
|
icmp6-adm-prohibited' % ip)
|
|
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, mids, ip_proto = 4):
|
|
''' Met à jour les autorisations par machine sur l'ouverture des ports.
|
|
Prend en argument une liste de mid (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 m in mids:
|
|
for type_m in mid.keys():
|
|
if int(m) in range(mid[type_m][0], mid[type_m][1]):
|
|
net = type_m
|
|
if net == "":
|
|
raise MidError("Il n'y a pas de réseau associé au mid %i" % m)
|
|
if '-v6' in net and ip_proto == 4:
|
|
raise MismatchMidIpProto(m, ip_proto, net)
|
|
machine = db.search('mid=%i' % m)['machine']
|
|
if len(machine) != 1:
|
|
msg = "Il y a %i machines associée(s) au mid %i" % (
|
|
len(machine), m)
|
|
raise MidError(msg)
|
|
else:
|
|
if ip_proto == 4:
|
|
ip = Mid(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)
|
|
ipt_p = open_pickle(ip_proto)
|
|
blacklist(ipt_p)
|
|
# 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 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']:
|
|
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'])
|
|
|
|
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 MidError(Exception):
|
|
''' Exception invoquée lorsqu'il y a un soucis avec un mid fourni '''
|
|
def __init__(self, msg):
|
|
self.msg = msg
|
|
syslog.syslog(syslog.LOG_ERR,"%s" % self.msg)
|
|
def __str__(self):
|
|
return "%s" % self.msg
|
|
|
|
class MismatchMidIpProto(Exception):
|
|
''' Exception invoquée lorsqu'il y a un défaut d'appariement entre le mid
|
|
et le protocole ip concerné'''
|
|
def __init__(self, mmid, ip_proto, net):
|
|
self.mmid = mmid
|
|
self.ip_proto = ip_proto
|
|
self.net = net
|
|
self.msg = "Défaut d'appariement entre le mid %i (%s) \
|
|
et le protocole ip %i" % (self.mmid, 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 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 = '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])
|
|
|
|
# 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 apply_rules(ip_proto = 4):
|
|
''' 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'
|
|
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'):
|
|
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'
|
|
|
|
# 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('ablacklist=*&paiement=ok')
|
|
|
|
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=ok')
|
|
|
|
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'''
|
|
macips(ipt, machines, types_machines)
|
|
# TODO Il faut raffiner avant de rajouter le wifi
|
|
for type_m in types_machines:
|
|
if not '-v6' in type_m:
|
|
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)
|
|
ipt.filter.ieui64('-i %s -s %s -m eui64 -j RETURN' % (dev,
|
|
prefix[type_m][0]))
|
|
|
|
ipt.filter.ieui64('-s fe80::/64 -m eui64 -j RETURN')
|
|
ipt.filter.ieui64('-j DROP')
|
|
#ipt.filter.ieui64('-j REJECT')
|
|
|
|
def macips(ipt, machines, types_machines):
|
|
''' Construit la chaîne MAC '''
|
|
for machine in machines:
|
|
for type_m in types_machines:
|
|
if int(machine.id()) in range(mid[type_m][0], mid[type_m][1]):
|
|
ipt.macip(machine.mac(), type_m)
|
|
break
|
|
for type_m in types_machines:
|
|
type_mm = re.sub('-', '', type_m)
|
|
getattr(ipt.filter,'mac' + type_mm)('-j DROP')
|
|
#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 DROP' % 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 DROP' % dev_ext)
|