[gestion/ipt.py] Elements de base pour la generation des firewalls
Ce module ajoute des fonctions plutôt de bas niveau pour générer des firewalls. Pour l'instant seul le firewall ipv6 utilise ce module. Cela fait quelque temps que le firewall6 tourne et tout semble bien marcher. Un effort a été fait pour tester tous les bouts de code, mais certaines parties peuvent avoir échappé à ma vigilance. L'organisation du code se présente ainsi : - un module ipt qui contient : o les classes Ip6tables, Table et Chain qui sont là pour "collecter" toutes les règles iptables. Ces dernières sont alors écrite dans un fichier dans /tmp et ensuite appliquée à l'aide d'un ip6tables-restore. L'instance qui contient toutes les règles est stockée en pickle et utilisée lors des mises à jours des règles : o il y ensuite les diverses fonctions qui permettent d'effectuer les actions nécessaires à la génération des règles. Elles ont été écrite dans le but d'être le plus générique possible et de pouvoir prendre en compte le protocole ip utilisé (toutes le ne sont pas forcément) o il y a une classe Update qui a des methodes associées au diverses mise à jour possible via generate ; o il y a aussi toutes les exceptions. - un script firewall6 qui sera présenté dans le prochain commit. - _toute_ la partie configuration devrait être écrite dans config.py. Le développement du module devrait se poursuivre un peu pour avoir toutes les fonctionnalités requises pour générer le firewall ipv4 Toute remarque, suggestion, critique (tant qu'elle est constructive) sera la bienvenue, sur nounou@ ou en privé. darcs-hash:20100226030644-8fbb1-926f7d4b7e8a6b834e1119bb28af89f239a598b8.gz
This commit is contained in:
parent
f7e1a58ffd
commit
e63dfbccd1
1 changed files with 722 additions and 0 deletions
722
gestion/ipt.py
Executable file
722
gestion/ipt.py
Executable file
|
@ -0,0 +1,722 @@
|
||||||
|
#!/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.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()
|
||||||
|
|
||||||
|
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)
|
||||||
|
eval('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' : 'self.filter.extfil', 'fil-v6' : 'self.filter.extfilv6',
|
||||||
|
'wifi' : 'self.filter.extwifi',
|
||||||
|
'wifi-v6' : 'self.filter.extwifiv6' }
|
||||||
|
ip = ipv6_addr(mac, type_machine)
|
||||||
|
for proto in ['tcp', 'udp']:
|
||||||
|
for port in ports[proto]:
|
||||||
|
if port != ':':
|
||||||
|
eval(tab[type_machine])('-i %s -p %s -d %s --dport %s -j \
|
||||||
|
ACCEPT' % (dev, proto, ip, port))
|
||||||
|
else:
|
||||||
|
eval(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' : 'self.filter.cransfil', 'fil-v6' :
|
||||||
|
'self.filter.cransfilv6', 'wifi' : 'self.filter.cranswifi',
|
||||||
|
'wifi-v6' : 'self.filter.cranswifiv6' }
|
||||||
|
ip = ipv6_addr(mac, type_machine)
|
||||||
|
for proto in ['tcp', 'udp']:
|
||||||
|
for port in ports[proto]:
|
||||||
|
if port != ':':
|
||||||
|
eval(tab[type_machine])('-i %s -p %s -s %s --sport %s -j \
|
||||||
|
ACCEPT' % (dev, proto, ip, port))
|
||||||
|
else:
|
||||||
|
eval(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 = eval('ipt_p.filter.%s.items' % (sens + re.sub('-', '',
|
||||||
|
net)))
|
||||||
|
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)
|
||||||
|
eval('ipt_p.filter.ext' + re.sub('-', '', net))('-j \
|
||||||
|
REJECT --reject-with icmp6-port-unreachable')
|
||||||
|
eval('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)
|
||||||
|
eval('ipt_p.filter.mac%s.items' % type_mm)[:] = []
|
||||||
|
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, msg):
|
||||||
|
self.msg = msg
|
||||||
|
syslog.syslog(syslog.LOG_ERR,"Pas d'interface associé à %s"
|
||||||
|
% self.msg)
|
||||||
|
def __str__(self):
|
||||||
|
return "Pas d'interface associé à %s" % self.msg
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
|
||||||
|
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 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')
|
||||||
|
|
||||||
|
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)
|
||||||
|
eval('ipt.filter.mac' + type_mm)('-j DROP')
|
||||||
|
return 0
|
Loading…
Add table
Add a link
Reference in a new issue