scripts/gestion/classe_firewall.py
pauget e59d649335 Bugs + lgre factorisation.
darcs-hash:20041128162035-41617-9b54e2d05d54a39c1273c013558a94c95d8f8b4d.gz
2004-11-28 17:20:35 +01:00

475 lines
16 KiB
Python
Executable file
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python
# -*- coding: iso-8859-1 -*-
# The authors of this code are
# Manuel Sabban <manu@feyd-rautha.org>
# Frédéric Pauget <pauget@crans.ens-cachan.fr>
#
# Copyright (c) 2004 Manuel Sabban, Frédéric Pauget
#
# Permission to use, copy, and modify this software with or without fee
# is hereby granted, provided that this entire notice is included in
# all source code copies of any software which is or includes a copy or
# modification of this software.
#
# THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR
# IMPLIED WARRANTY. IN PARTICULAR, NONE OF THE AUTHORS MAKES ANY
# REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE
# MERCHANTABILITY OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR
# PURPOSE.
""" classe Firewall de Komaz """
import syslog
import iptools,config
import ldap_crans
from affich_tools import *
from commands import getstatusoutput
from iptools import AddrInNet
syslog.openlog('firewall')
class ErrorArgument(Exception):
"""
User defined exception
Erreur sur les arguments d'appel du firewall
"""
pass
class ErrorIp(Exception):
"""
User defined exception
Erreur dans les fonctions Ips
"""
def __init__(self, ip):
self.ip=ip
class ErrorMoreThanOneIp(ErrorIp):
"""
User defined exception
Au moins deux utilisateurs ont la même ip
"""
pass
class IptablesError(Exception):
""" Gestion des erreurs d'iptables """
def __init__(self,cmd,err_code,output):
self.cmd=cmd
self.err_code=err_code
self.output=output
syslog.syslog(syslog.LOG_ERR,"%s : status %s,%s" % (cmd,err_code,output))
def __str__(self):
return "%s\n status : %s\n %s" % (self.cmd,self.err_code,self.output)
def iptables(cmd):
""" Interface à iptables  """
syslog.syslog(syslog.LOG_INFO,cmd)
status,output=getstatusoutput("/sbin/iptables "+cmd)
if status:
raise IptablesError(cmd,status,output)
class ErrorNoSuchIp(ErrorIp):
"""
User defined exception
Personne n'a cette ip
"""
pass
class firewall:
"""
Structure du firewall :
table nat :
PREROUTING (policy par defaut : DROP)
1) passage par TEST_VIRUS_FLOOD pour tout ce qui n'est pas dans zone_serveur
2) passage dans RESEAUX_NON_ROUTABLES_DST
3) passage est paquets venant de l'extérieur dans RESEAUX_NON_ROUTABLES_SRC
4) on laisse passer vers filter les paquets suivants :
source ou destination les serveurs de serveurs_crans
ce qui vient de l'extérieur
5) passage par TEST_MAC-IP
TEST_VIRUS_FLOOD droppe les paquets contenant des virus ou les paquets de flood
RESEAUX_NON_ROUTABLES_DST droppe les paquets dont la destination est non routable
RESEAUX_NON_ROUTABLES_SRC droppe les paquets dont la source est non routable
TEST_MAC-IP envoi les bon paquets vers CRANS_VERS_EXT
CRANS_VERS_EXT
ACCEPT pour les paquets venant des machines du crans (test port-ip)
REJECT pour le reste
table filter :
FORWARD (policy par defaut : ACCEPT)
1) passage par BLACKLIST
2) ce qui a pour source les serveurs de serveurs_crans est dirigé vers SERVEURS_VERS_EXT
3) ce qui a pour destination les serveurs de serveurs_crans est dirigé EXT_VERS_SERVEURS
4) tout ce qui vient de l'interface externe est dirigé vers EXT_VERS_CRANS
5) ce qui a pour source les serveurs de serveurs_crans est dirigé vers EXT_VERS_CRANS
BLACKLIST fitre des ip blacklistées (reject)
EXT_VERS_CRANS
ACCEPT pour les paquets vers les machines du crans (test port-ip)
REJECT pour le reste
EXT_VERS_SERVEURS et SERVEURS_VERS_EXT
ACCEPT pour bon mac-ip-port
REJECT pour le reste
"""
zone_serveur="138.231.136.0/28"
eth_ext = "eth2"
# Ports ouverts
ports_default = { 'tcp_EXT_VERS_CRANS' : [ '22' ],
'tcp_CRANS_VERS_EXT': [ ':79', '81:134', '136', '140:444', '446:'],
'udp_EXT_VERS_CRANS' : [ ],
'udp_CRANS_VERS_EXT': [ ':136','140:'] }
mac_wifi = '00:0c:f1:fa:f1:4b'
limit = " -m limit --limit 10/s --limit-burst 10 "
log_template = '-m limit --limit 1/s --limit-burst 1 -j LOG --log-level notice --log-prefix '
filtre_flood = '-m dstlimit --dstlimit 20 --dstlimit-mode srcip-dstip --dstlimit-name flood'
liste_reseaux_non_routables = [ '1.0.0.0/8','2.0.0.0/8','5.0.0.0/8','7.0.0.0/8',\
'10.0.0.0/8','14.0.0.0/8','23.0.0.0/8','27.0.0.0/8','31.0.0.0/8','36.0.0.0/8',\
'37.0.0.0/8','39.0.0.0/8','41.0.0.0/8','42.0.0.0/8','71.0.0.0/8','72.0.0.0/8',\
'73.0.0.0/8','74.0.0.0/8','75.0.0.0/8','76.0.0.0/8','77.0.0.0/8','78.0.0.0/8',\
'79.0.0.0/8','89.0.0.0/8','90.0.0.0/8','91.0.0.0/8','92.0.0.0/8','93.0.0.0/8',\
'94.0.0.0/8','95.0.0.0/8','96.0.0.0/8','97.0.0.0/8','98.0.0.0/8','99.0.0.0/8',\
'100.0.0.0/8','101.0.0.0/8','102.0.0.0/8','103.0.0.0/8','104.0.0.0/8',\
'105.0.0.0/8','106.0.0.0/8','107.0.0.0/8','108.0.0.0/8','109.0.0.0/8',\
'110.0.0.0/8','111.0.0.0/8','112.0.0.0/8','113.0.0.0/8','114.0.0.0/8',\
'115.0.0.0/8','116.0.0.0/8','117.0.0.0/8','118.0.0.0/8','119.0.0.0/8',\
'120.0.0.0/8','121.0.0.0/8','122.0.0.0/8','123.0.0.0/8','124.0.0.0/8',\
'125.0.0.0/8','126.0.0.0/8','127.0.0.0/8','169.254.0.0/16','172.16.0.0/12',\
'173.0.0.0/8','174.0.0.0/8','175.0.0.0/8','176.0.0.0/8','177.0.0.0/8',\
'178.0.0.0/8','179.0.0.0/8','180.0.0.0/8','181.0.0.0/8','182.0.0.0/8',\
'183.0.0.0/8','184.0.0.0/8','185.0.0.0/8','186.0.0.0/8','187.0.0.0/8',\
'189.0.0.0/8','190.0.0.0/8','192.168.0.0/16','197.0.0.0/8','223.0.0.0/8',\
'224.0.0.0/4','240.0.0.0/4']
ports_virus = { 'tcp' : [ 135, 445 ] , 'udp' : [] }
machines = []
debug = 1
def __machines(self) :
""" Liste des machines du crans """
if not self.machines :
self.anim = anim(" Interrogation de la base LDAP")
self.machines = ldap_crans.crans_ldap().search('ip=*')['machine']
print OK
return self.machines
def start(self) :
""" Construction du firewall """
cprint('Démarrage firewall','gras')
# Préliminaires
if not self.__machines() or self.stop() :
print "Abandon"
return
self.anim = anim(' Construction structure de la table nat')
for chaine in [ 'LOG_VIRUS', 'LOG_FLOOD', 'TEST_VIRUS_FLOOD' , 'TEST_MAC-IP' , 'RESEAUX_NON_ROUTABLES_SRC', 'RESEAUX_NON_ROUTABLES_DST' ] :
iptables('-t nat -N %s' % chaine)
try :
iptables("-t nat -P PREROUTING ACCEPT")
iptables("-t nat -A PREROUTING -i lo -j ACCEPT")
iptables("-t nat -A PREROUTING -s ! %s -j TEST_VIRUS_FLOOD" % self.zone_serveur)
iptables("-t nat -A PREROUTING -j RESEAUX_NON_ROUTABLES_DST")
iptables("-t nat -A PREROUTING -i %s -j RESEAUX_NON_ROUTABLES_SRC" % self.eth_ext )
iptables("-t nat -A PREROUTING -d %s -j ACCEPT" % self.zone_serveur )
iptables("-t nat -A PREROUTING -s %s -j ACCEPT" % self.zone_serveur )
iptables("-t nat -A PREROUTING -i %s -j ACCEPT" % self.eth_ext )
iptables("-t nat -A PREROUTING -j TEST_MAC-IP")
iptables("-t nat -P PREROUTING DROP")
print OK
self.anim = anim(' Construction structure de la table filter')
for chaine in [ 'EXT_VERS_SERVEURS', 'SERVEURS_VERS_EXT' , 'EXT_VERS_CRANS', 'CRANS_VERS_EXT', 'BLACKLIST_SRC', 'BLACKLIST_DST' ] :
iptables('-N %s' % chaine)
iptables("-A FORWARD -i lo -j ACCEPT")
iptables("-A FORWARD -p icmp -j ACCEPT")
iptables("-A FORWARD -i %s -j BLACKLIST_DST" % self.eth_ext )
iptables("-A FORWARD -o %s -j BLACKLIST_SRC" % self.eth_ext )
iptables("-A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT")
iptables("-A FORWARD -i %s -d %s -j EXT_VERS_SERVEURS" % (self.eth_ext, self.zone_serveur) )
iptables("-A FORWARD -o %s -s %s -j SERVEURS_VERS_EXT" % (self.eth_ext, self.zone_serveur) )
iptables("-A FORWARD -i %s -j EXT_VERS_CRANS" % self.eth_ext )
iptables("-A FORWARD -o %s -j CRANS_VERS_EXT" % self.eth_ext )
print OK
except IptablesError, c :
print ERREUR
if self.debug : print c
self.stop()
return 1
except :
print ERREUR
self.stop()
if self.debug :
import traceback
traceback.print_exc()
return 1
for tache in [ self.log_chaines, self.test_virus_flood, self.reseaux_non_routables,
self.blacklist , self.serveurs_vers_ext, self.ext_vers_serveurs,
self.crans_vers_ext, self.ext_vers_crans, self.test_mac_ip ] :
try :
tache()
except IptablesError, c :
self.anim.reinit()
print ERREUR
if self.debug : print c
except :
self.anim.reinit()
print ERREUR
import traceback
if self.debug : traceback.print_exc()
def log_chaines(self) :
""" Construction des chaines de log (LOG_VIRUS et LOG_FLOOD) """
self.anim = anim(' Création des chaines de log')
for filtre in [ 'VIRUS', 'FLOOD' ] :
# Vidage de la chaîne
iptables('-t nat -F LOG_%s' % filtre)
iptables('-t nat -A LOG_%s %s %s:' % (filtre, self.log_template, filtre.capitalize()) )
iptables('-t nat -A LOG_%s -j DROP' % filtre )
self.anim.cycle()
self.anim.reinit()
print OK
def reseaux_non_routables(self) :
""" Construction de RESEAUX_NON_ROUTABLES_{DST,SRC} """
self.anim = anim(' Filtrage ip non routables',len(self.liste_reseaux_non_routables))
for reseau in self.liste_reseaux_non_routables :
iptables("-t nat -A RESEAUX_NON_ROUTABLES_DST -d %s -j DROP" % reseau)
iptables("-t nat -A RESEAUX_NON_ROUTABLES_SRC -s %s -j DROP" % reseau)
self.anim.cycle()
self.anim.reinit()
print OK
def test_virus_flood(self) :
""" Construction de la chaîne TEST_VIRUS """
iptables('-t nat -F TEST_VIRUS_FLOOD')
self.anim = anim(' Filtrage virus et floods')
for proto, ports in self.ports_virus.items() :
for port in ports :
iptables('-t nat -A TEST_VIRUS_FLOOD -p %s --dport %s -j LOG_VIRUS' % (proto, port) )
self.anim.cycle()
iptables('-t nat -A TEST_VIRUS_FLOOD %s -j RETURN' % self.filtre_flood) # Les limites en négatif ca ne marche pas.
self.anim.cycle()
iptables('-t nat -A TEST_VIRUS_FLOOD -j LOG_FLOOD')
self.anim.reinit()
print OK
def stop(self):
""" Arrête le firewall """
self.anim = anim(" Arrêt du firewall")
try :
iptables("-t nat -P PREROUTING ACCEPT")
iptables("-F")
iptables("-t nat -F")
iptables("-X")
iptables("-t nat -X")
print OK
return 0
except IptablesError, c :
print ERREUR
if self.debug : print c
except :
print ERREUR
if self.debug :
import traceback
traceback.print_exc()
return 1
def test_mac_ip(self) :
""" Reconstruit la correspondance MAC-IP des machines des adhérents """
self.anim = anim(' Construction de la chaîne TEST_MAC-IP',len(self.__machines())+1)
iptables("-t nat -P PREROUTING ACCEPT")
iptables("-t nat -F TEST_MAC-IP")
self.anim.cycle()
try:
for machine in self.__machines() :
self.__test_mac_ip(machine)
self.anim.cycle()
iptables("-t nat -P PREROUTING DROP")
self.anim.reinit()
print OK
except IptablesError, c :
self.anim.reinit()
print ERREUR
if self.debug : print c
except :
self.anim.reinit()
print ERREUR
if self.debug :
import traceback
traceback.print_exc()
def serveurs_vers_ext(self) :
""" Reconstruit la chaine SERVEURS_VERS_EXT """
if self.__build_chaine('SERVEURS_VERS_EXT', self.__serveurs_vers_ext) :
self.anim.reinit()
print OK
def ext_vers_serveurs(self) :
""" Reconstruit la chaine EXT_VERS_SERVEURS """
if self.__build_chaine('EXT_VERS_SERVEURS', self.__ext_vers_serveurs) :
self.anim.reinit()
print OK
def crans_vers_ext(self) :
""" Reconstruit la chaine CRANS_VERS_EXT """
self.__build_chaine_adherent('CRANS_VERS_EXT',self.__crans_vers_ext)
def ext_vers_crans(self) :
""" Reconstruit la chaine EXT_VERS_CRANS """
self.__build_chaine_adherent('EXT_VERS_CRANS',self.__ext_vers_crans)
def __build_chaine_adherent(self,chaine,methode):
if self.__build_chaine(chaine, methode) :
# Défauts
self.anim.reinit()
for proto in [ 'tcp' , 'udp' ] :
for port in self.ports_default["%s_%s" % ( proto, chaine) ] :
self.anim.cycle()
iptables("-I %s -p %s --dport %s -j ACCEPT" % (chaine, proto,port) )
self.anim.reinit()
print OK
def __build_chaine(self,chaine, methode) :
self.anim = anim(' Construction de la chaîne %s' % chaine,len(self.__machines())+1)
iptables("-F %s" % chaine)
self.anim.cycle()
try:
for machine in self.__machines() :
methode(machine)
self.anim.cycle()
iptables("-A %s -j REJECT" % chaine)
self.anim.reinit()
return 1
except IptablesError, c :
self.anim.reinit()
print ERREUR
if self.debug : print c
except :
self.anim.reinit()
print ERREUR
if self.debug :
import traceback
traceback.print_exc()
def add_machine(self,machine):
self.__serveurs_vers_ext(machine)
self.__ext_vers_serveurs(machine)
self.__crans_vers_ext(machine)
self.__ext_vers_crans(machine)
self.__test_mac_ip(machine)
def __serveurs_vers_ext(self,machine):
ip=machine.ip()
if not AddrInNet(ip,self.zone_serveur):
# C'est une machine adhérent, rien à faire ici
return
mac = machine.mac()
ports = { 'tcp' : machine.portTCPin(),
'udp' : machine.portUDPin() }
for proto in [ 'tcp', 'udp' ] :
for port in ports[proto].split() :
iptables("-I SERVEURS_VERS_EXT -s %s -p %s --dport %s -m mac --mac-source %s -j ACCEPT" \
%(ip,proto,port,mac))
def __ext_vers_serveurs(self,machine):
ip=machine.ip()
if not AddrInNet(ip,self.zone_serveur):
# C'est une machine adhérent, rien à faire ici
return
ports = { 'tcp' : machine.portTCPin(),
'udp' : machine.portUDPin() }
for proto in [ 'tcp', 'udp' ] :
for port in ports[proto].split() :
iptables("-I EXT_VERS_SERVEURS -d %s -p %s --dport %s -j ACCEPT"\
%(ip,proto,port))
def __crans_vers_ext(self,machine):
ip=machine.ip()
if AddrInNet(ip,self.zone_serveur):
# C'est un serveur, rien à faire ici
return
ports = { 'tcp' : machine.portTCPout(),
'udp' : machine.portUDPout() }
for proto in [ 'tcp', 'udp' ] :
for port in ports[proto].split() :
iptables("-I CRANS_VERS_EXT -s %s -p %s --dport %s -j ACCEPT" \
%(ip,proto,port))
def __ext_vers_crans(self,machine):
ip=machine.ip()
if AddrInNet(ip,self.zone_serveur):
# C'est un serveur, rien à faire ici
return
ports = { 'tcp' : machine.portTCPin(),
'udp' : machine.portUDPin() }
for proto in [ 'tcp', 'udp' ] :
for port in ports[proto].split() :
iptables("-I EXT_VERS_CRANS -d %s -p %s --dport %s -j ACCEPT" \
%(ip,proto,port))
def __test_mac_ip(self,machine):
ip=machine.ip()
mac=machine.mac()
if machine.ipsec():
# Machine wifi, c'est la mac de Nectaris
iptables("-t nat -A TEST_MAC-IP -s "+\
"%s -m mac --mac-source %s -j ACCEPT"%(ip,self.mac_wifi))
else:
# Machine fixe
iptables("-t nat -A TEST_MAC-IP -s "+\
"%s -m mac --mac-source %s -j ACCEPT"%(ip,mac))
def blacklist(self):
""" Construit les chaines de blackliste (BLACKLIST_{DST,SRC}) """
self.anim = anim(" Contruction blackliste")
iptables('-F BLACKLIST_DST')
iptables('-F BLACKLIST_SRC')
blacklist=[]
search=ldap_crans.crans_ldap().search('blacklist=*&paiement=%s'%ldap_crans.ann_scol)
for entite in search['adherent']+search['club']+search['machine']:
self.anim.cycle()
sanctions = entite.blacklist_actif()
if 'upload' in sanctions or 'warez' in sanctions :
if search.__class__ == ldap_crans.machine:
blacklist+=[entite]
else:
blacklist+=entite.machines()
for machine in blacklist:
iptables("-A BLACKLIST_DST -d %s -j REJECT" % machine.ip())
iptables("-A BLACKLIST_SRC -s %s -j REJECT" % machine.ip())
self.anim.reinit()
print OK
def del_entree(self,ip):
""" Détruit toutes les occurences à l'IP dans le firewall """
for table in [ 'filter' , 'nat' ] :
for chaine in iptables("-t %s -L -n --line-numbers" % table).split('Chain ') :
nom_chaine = chaine.split(' ',1)[0]
to_del=[]
for regle in chaine.split('\n')[1:] :
# On regarde les règles une à une
regle = regle.split()
if ip in regle :
# Règle à dértuire
to_del.append(int(regle[0]))
# Destruction des règles de cette chaîne
to_del.sort()
to_del.reverse()
for i in to_del :
iptables('-t %s -D %s %s' % (table, nom_chaine, i) )