scripts/gestion/gen_confs/firewall.py
bernat 2f844907c4 Lgre modif visant corriger un lger bug introduit lgrement par
une modification lgrement extensive de Stphane mais a priori
lgrement non teste.

Donc, je rsume : quand on modifie quelque chose en rapport avec le
wifi, faut tester si le wifi marche encore. Quand on modifie quelque
chose en rapport avec le firewall, faut tester si le firewall marche
encore. Quand on modifie quelque chose en rapport avec X, faut tester
si X marche encore.

PS: la connexion de secours ne fonctionne plus, comptez pas sur moi
pour rparer.

darcs-hash:20060327080349-d1718-08fd2ce848ca341d40b30b90b7efa190d762ca5c.gz
2006-03-27 10:03:49 +02:00

933 lines
36 KiB
Python
Executable file

#!/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>
# Mathieu Segaud <matt@minas-morgul.org>
#
# Rewritten as inherited classes from firewall_crans
# by Mathieu Segaud <matt@minas-morgul.org>
#
# Copyright (c) 2004 Manuel Sabban, Frédéric Pauget
# Copyright (c) 2005 Mathieu Segaud
#
# 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 EXPRSS 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.
import sys
sys.path.append('/usr/scripts/gestion')
import syslog
import pwd
from lock import *
from ldap_crans import crans_ldap, ann_scol, hostname
from ldap_crans import AssociationCrans, Machine, MachineWifi
from affich_tools import *
from commands import getstatusoutput
from iptools import AddrInNet
syslog.openlog('firewall')
debug = 1
db = crans_ldap()
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 d'iptables """
syslog.syslog(syslog.LOG_INFO,cmd)
status,output=getstatusoutput("/sbin/iptables "+cmd)
if status:
raise IptablesError(cmd,status,output)
return output
class firewall_crans :
"""
Classe parente pour les firewalls du crans
Implementee directement a partir du firewall de komaz, initialement
ecrit par Manuel Sabban et Fred Pauget.
* les méthodes a surcharger pour l'impementation eme des firewall
sont nat_table, filter_table, pour la preparation du fw,
start_fw_funcs pour la mise en place du filtrage.
* enable_route et disable_route utilisees seulement par komaz
* serveurs_maj, adh_maj_list_to_do et serveurs_maj_list_to_do,
pour la mise en place de la MAC-IP.
en particulier, adh_maj_list_to_do et serveurs_maj_list_to_do
sont factorisees pour la simple et bonne raison que les sources
de la liste to_do originale ne seront pas forcement identiques
(c'est un peu sale...)
* La classe parente contient a peu de choses pres tout ce qu'il
faut pour mettre en place un fw basique n'effectuant que la
verif MAC-IP.
"""
zone_serveur="138.231.136.0/28"
vlan_adm="138.231.144.0/24"
adm_users = [ "root", "identd", "daemon", "postfix", "freerad", "amavis", "nut", "respbats", "list", "sqlgrey"]
mac_wifi = '00:e0:81:58:d1:39'
mac_komaz = '00:01:02:b0:31:b6'
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 hashlimit --hashlimit 20 --hashlimit-mode srcip --hashlimit-name flood'
machines = []
debug = 1
def exception_catcher(self,tache) :
""" Exécute la tache founie en gérant les diverses exceptions
pouvant survenir
Retourne 1 en cas d'erreur et 0 sinon """
try :
tache()
return 0
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()
return 1
def __machines(self) :
""" Liste des machines du crans """
return db.all_machines(graphic=True)
def __init__(self) :
""" Pose un lock """
make_lock('firewall')
def __del__(self) :
""" Destruction du lock """
# Comprend pas pourquoi il faut réimporter ici -- Fred
from lock import remove_lock
remove_lock('firewall')
def nat_table(self) :
""" Remplit la table nat """
return
def filter_table(self) :
""" Remplit la table filter """
self.anim = anim('\tStructure de la table filter')
print OK
def filter_table_tweaks(self) :
""" Complete la table filter """
return
def start_fw_funcs(self) :
""" Ordonnance la construction du firewall """
self.exception_catcher(self.test_mac_ip)
def post_start_hook(self) :
""" Hook de fin de demarrage """
return
def pre_stop_hook(self) :
""" Hook de debut d'arret """
return
def restart(self):
""" Rédémarrage du firewall """
cprint('Redémarrage firewall','gras')
self.start(False)
def start(self,aff_txt_intro=True) :
""" Construction du firewall
aff_txt_intro s'occupe uniquement de l'esthétisme
"""
if aff_txt_intro : cprint('Démarrage firewall','gras')
# Préliminaires
if not self.__machines() or self.exception_catcher(self.__stop) :
cprint("Abandon",'rouge')
return
# Initialisation
self.exception_catcher(self.nat_table)
self.exception_catcher(self.filter_table)
self.exception_catcher(self.filter_table_tweaks)
# Remplissage
self.start_fw_funcs()
# On peux router
self.post_start_hook()
cprint("\t -> fin de la procédure de démarrage",'vert')
def stop(self):
""" Arrête le firewall """
cprint("Arrêt du firewall",'gras')
self.pre_stop_hook()
self.exception_catcher(self.__stop)
cprint("\t -> fin de la procédure d'arrêt",'vert')
def __stop(self) :
self.anim = anim("\tSuppression regles")
iptables("-t nat -P PREROUTING ACCEPT")
iptables("-F")
iptables("-t nat -F")
iptables("-X")
iptables("-t nat -X")
print OK
def test_mac_ip(self) :
""" Reconstruit la correspondance MAC-IP des machines des adhérents """
self.anim = anim('\tChaine TEST_MAC-IP',len(self.__machines())+1)
iptables("-t nat -P PREROUTING ACCEPT")
iptables("-t nat -F TEST_MAC-IP")
self.anim.cycle()
def procedure() :
for machine in self.__machines() :
self.__test_mac_ip(machine)
self.anim.cycle()
iptables("-t nat -A TEST_MAC-IP -j DROP")
iptables("-t nat -P PREROUTING ACCEPT")
self.anim.reinit()
print OK
self.exception_catcher(procedure)
def __test_mac_ip(self,machine):
ip=machine.ip()
mac=machine.mac()
insert = '-I'
if isinstance(machine, MachineWifi):
# Machine wifi, c'est la mac de Nectaris
iptables("-t nat %s TEST_MAC-IP -s "%(insert)+\
"%s -m mac --mac-source %s -j RETURN"%(ip, self.mac_wifi))
else:
# Machine fixe
iptables("-t nat %s TEST_MAC-IP -s "%(insert)+\
"%s -m mac --mac-source %s -j RETURN"%(ip,mac))
def serveurs_maj(self):
pass
def serveurs_maj_list_to_do(self) :
pass
def adh_maj_list_to_do(self) :
pass
def port_maj(self,ip_list) :
""" Mise à jour des ports pour les ip fournies """
# Note : système bourrin (on efface les chaines et on refait)
# mais rapide et efficace (si qqn veut se casser le cul à
# un système aussi délicat que pour la correspondance MAC-IP...)
# -- Fred
serveur_maj = False
adh_maj = False
for ip in ip_list :
if AddrInNet(ip,self.zone_serveur) :
serveur_maj = True
else :
adh_maj = True
if serveur_maj and adh_maj :
break
to_do=[]
if serveur_maj :
self.exception_catcher(self.serveurs_maj_list_to_do)
if adh_maj :
self.exception_catcher(self.adh_maj_list_to_do)
def mac_ip_maj(self,ip_list) :
""" Mise à jour de la correspondance MAC-IP pour les ip fournies """
## Que faut-il faire ?
self.anim = anim('\tAnalyse travail à effectuer')
if ip_list == [''] :
print OK + ' (rien à faire)'
return
mac_ip_maj = {}
serveur_maj = False
for ip in ip_list :
machine = db.search('ip=%s'% ip)['machine']
if not machine :
# Destruction des occurences
if AddrInNet(ip,self.zone_serveur) :
serveur_maj = True
else :
mac_ip_maj[ip] = None
elif len(machine) == 1 :
# Mise à jour de la machine
if AddrInNet(ip,self.zone_serveur) :
serveur_maj = True
# Il faut avoir payé ou être une machine du crans
if db.search('paiement=ok&ip=%s'% ip)['machine'] or \
machine[0].proprietaire().__class__ == AssociationCrans:
mac_ip_maj[ip] = machine[0]
else :
mac_ip_maj[ip] = None
else :
print WARNING
if debug :
sys.stderr.write("Plusieurs machines avec l'IP %s\n" % ip)
print OK
## Traitement
# Serveurs
if serveur_maj :
self.serveurs_maj()
# Correspondance MAC-IP
if mac_ip_maj or serveur_maj :
def procedure() :
warn = ''
self.anim = anim('\tActualisation TEST_MAC-IP')
for regle in iptables("-t nat -L TEST_MAC-IP -n").split('\n')[2:] :
regle = regle.split()
ip = regle[3]
mac = regle[6].lower()
if ip in mac_ip_maj.keys() :
# La règle correspond à une ip à mettre à jour
machine = mac_ip_maj[ip]
try :
if not machine :
# Il faut détruire cette entrée
iptables("-t nat -D TEST_MAC-IP -s %s -m mac --mac-source %s -j ACCEPT" % (ip, mac))
else :
if (isinstance(machine, MachineWifi) and mac != self.mac_wifi) \
or (not isinstance(machine, MachineWifi) and mac != machine.mac()):
# La correspondance MAC-IP est fausse => on ajoute la bonne règle
self.__test_mac_ip(machine)
# Supression de l'ancienne ligne
iptables("-t nat -D TEST_MAC-IP -s %s -m mac --mac-source %s -j ACCEPT" % (ip, mac))
# Toutes les autres occurences devront être détruites
mac_ip_maj[ip]=None
except IptablesError, c :
warn += c
# Ajout des machines qui n'étaient pas dans le firewall
for machine in mac_ip_maj.values() :
if machine :
self.__test_mac_ip(machine)
if warn :
print WARNING
sys.stdout.write(warn)
else :
print OK
self.exception_catcher(procedure)
def build_chaine_adherent(self,chaine,methode) :
# On construit d'abord les autorisations particuli\uffffres
if not self.build_chaine(chaine, methode) :
# Puis si pas de problèmes les autorisations par défaut
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('\tChaîne %s' % chaine,len(self.__machines())+1)
iptables("-F %s" % chaine)
self.anim.cycle()
def procedure() :
for machine in self.__machines() :
methode(machine)
self.anim.cycle()
iptables("-A %s -j REJECT" % chaine)
return self.exception_catcher(procedure)
"""
Komaz
"""
class firewall_komaz(firewall_crans) :
"""
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
table filter :
FORWARD (policy par defaut : ACCEPT)
1) passage par BLACKLIST
2) passage pas FILTRE_P2P (ACCEPT sur le trafic p2p, sanction gérées par déconnexion.py)
3) ce qui a pour source les serveurs de serveurs_crans est dirigé vers SERVEURS_VERS_EXT
4) ce qui a pour destination les serveurs de serveurs_crans est dirigé EXT_VERS_SERVEURS
5) tout ce qui vient de l'interface externe est dirigé vers EXT_VERS_CRANS
6) ce qui a pour source les serveurs de serveurs_crans est dirigé vers EXT_VERS_CRANS
BLACKLIST fitre des ip blacklistées (reject)
FILTRE_P2P filtre le traffic de p2p
EXT_VERS_CRANS et CRANS_VERS_EXT
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
"""
# interfaces physiques
eth_ext = "eth2"
eth_int = "eth0"
# 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:'] }
ports_virus = { 'tcp' : [ 135, 445 ] , 'udp' : [] }
# Filtrage du peer to peer
filtres_p2p = [ ('bit', 'Bittorrent'),
('apple', 'AppleJuice'),
('soul', 'SoulSeek'),
('winmx', 'WinMX'),
('edk', 'eDonkey'),
('dc', 'DirectConnect'),
('kazaa', 'KaZaa'),
('ares', 'Ares'),
('gnu', 'GNUtella') ]
ports_p2p = [ '412', '1214', '4662:4665' , '6346:6347', '6699', '6881:6889' ]
liste_reseaux_non_routables = [ '10.0.0.0/8', '172.16.0.0/12',
'169.254.0.0/16', '192.168.0.0/16', '224.0.0.0/4']
def reseaux_non_routables(self) :
""" Construction de RESEAUX_NON_ROUTABLES_{DST,SRC} """
self.anim = anim('\tFiltrage 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 nat_table(self) :
self.anim = anim('\tStructure de la table nat')
for chaine in [ 'TEST_MAC-IP', 'RESEAUX_NON_ROUTABLES_SRC', 'RESEAUX_NON_ROUTABLES_DST', 'TEST_VIRUS_FLOOD', 'LOG_VIRUS', 'LOG_FLOOD' ] :
iptables('-t nat -N %s' % chaine)
iptables("-t nat -P PREROUTING ACCEPT")
iptables("-t nat -A PREROUTING -i lo -j ACCEPT")
iptables("-t nat -A PREROUTING -d 224.0.0.0/4 -j DROP")
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 -i %s -p udp --destination 138.231.136.1 --destination-port 53 -j DNAT --to-destination 138.231.136.3" % self.eth_ext)
iptables("-t nat -A PREROUTING -i %s -j ACCEPT" % self.eth_ext )
iptables("-t nat -A PREROUTING -s %s -j ACCEPT" % self.zone_serveur )
iptables("-t nat -A PREROUTING -d %s -j ACCEPT" % self.zone_serveur )
# iptables("-t nat -A PREROUTING -i %s -p tcp --dport 80 -s ! %s -j DNAT --to-destination 138.231.136.3:81" % (self.eth_int, self.zone_serveur) )
# iptables("-t nat -A POSTROUTING -o %s -p tcp --dport 81 -s 138.231.136.0/21 -d 138.231.136.3 -j SNAT --to-source 138.231.136.4" % self.eth_int )
iptables("-t nat -A PREROUTING -j TEST_MAC-IP")
iptables("-t nat -P PREROUTING ACCEPT")
iptables("-t nat -P OUTPUT ACCEPT")
# Proxy transparent
iptables("-t mangle -F PREROUTING")
iptables("-t mangle -A PREROUTING -s %s -j RETURN" % self.zone_serveur)
iptables("-t mangle -A PREROUTING -p tcp --destination-port 80 " +
"-s 138.231.136.0/21 -d \! 138.231.148.0/22 -j MARK --set-mark 2")
iptables("-t nat -A PREROUTING -p tcp -m mark --mark 2 -j DNAT " +
"--to-destination 138.231.144.10:3128")
print OK
def filter_table_tweaks(self) :
self.anim = anim('\trègles spécifiques à komaz')
for chaine in [ 'ADMIN_VLAN', 'EXT_VERS_SERVEURS', 'SERVEURS_VERS_EXT' , 'EXT_VERS_CRANS', 'CRANS_VERS_EXT', 'BLACKLIST_SRC', 'BLACKLIST_DST' , 'FILTRE_P2P' ] :
iptables('-N %s' % chaine)
iptables("-A FORWARD -i lo -j ACCEPT")
iptables("-A FORWARD -p icmp -j ACCEPT")
iptables("-A FORWARD -i %s -d %s -j REJECT" % (self.eth_ext, self.vlan_adm) )
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 -s ! %s -d ! %s -j FILTRE_P2P" % (self.zone_serveur, self.zone_serveur) )
iptables("-A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT")
iptables("-A FORWARD -i %s -d %s -j ADMIN_VLAN" % (self.eth_int, self.vlan_adm) )
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 )
# Proxy transparent
iptables("-I FORWARD -m mark --mark 2 -j ACCEPT")
print OK
def post_start_hook(self) :
self.anim = anim("\tMise en place routage")
warn = ''
for cmd in [ 'echo 1 > /proc/sys/net/ipv4/ip_forward' ,
'echo 65536 > /proc/sys/net/ipv4/ip_conntrack_max' ,
'modprobe ip_conntrack_ftp' ,
'modprobe ip_conntrack_irc' ] :
status,output=getstatusoutput(cmd)
if status :
warn += output + '\n'
if warn :
print WARNING
if self.debug :
print warn
else :
print OK
def pre_stop_hook(self) :
self.anim = anim("\tArret routage")
status,output=getstatusoutput('echo 0 > /proc/sys/net/ipv4/ip_forward')
if status :
print ERREUR
else :
print OK
def start_fw_funcs(self) :
self.exception_catcher(self.log_chaines)
self.exception_catcher(self.test_virus_flood)
self.exception_catcher(self.reseaux_non_routables)
self.exception_catcher(self.blacklist)
self.exception_catcher(self.admin_vlan)
self.exception_catcher(self.serveurs_vers_ext)
self.exception_catcher(self.ext_vers_serveurs)
self.exception_catcher(self.crans_vers_ext)
self.exception_catcher(self.ext_vers_crans)
self.exception_catcher(self.test_mac_ip)
self.exception_catcher(self.filtre_p2p)
def serveurs_maj_list_to_do(self) :
self.exception_catcher(self.serveurs_vers_ext)
self.exception_catcher(self.ext_vers_serveurs)
def adh_maj_list_to_do(self) :
self.exception_catcher(self.crans_vers_ext)
self.exception_catcher(self.ext_vers_crans)
def log_chaines(self) :
""" Construction des chaines de log (LOG_VIRUS et LOG_FLOOD) """
self.anim = anim('\tCré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 test_virus_flood(self) :
""" Construction de la chaîne TEST_VIRUS """
iptables('-t nat -F TEST_VIRUS_FLOOD')
self.anim = anim('\tFiltrage 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 serveurs_vers_ext(self) :
""" Reconstruit la chaine SERVEURS_VERS_EXT """
if not 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 not 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 admin_vlan(self) :
""" Reconstruit la chaine ADMIN_VLAN """
iptables("-F ADMIN_VLAN")
nounou_machines = []
for adherent in db.search('droits=Nounou')['adherent'] :
for machine in adherent.machines() :
nounou_machines.append(machine.ip())
iptables("-A ADMIN_VLAN -j REJECT")
self.anim = anim('\tChaîne ADMIN_VLAN', len(nounou_machines))
for machine in nounou_machines :
self.anim.cycle()
iptables("-I ADMIN_VLAN -p tcp -s %s --dport ssh -j ACCEPT" % machine)
self.anim.reinit()
print OK
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.portTCPout(),
'udp' : machine.portUDPout() }
for proto in [ 'tcp', 'udp' ] :
if not ports[proto]: ports[proto] = ':'
for port in ports[proto]:
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]:
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]:
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]:
iptables("-I EXT_VERS_CRANS -d %s -p %s --dport %s -j ACCEPT" \
%(ip,proto,port))
def blacklist(self):
""" Construit les chaines de blackliste (BLACKLIST_{DST,SRC}) """
self.anim = anim("\tBlackliste")
iptables('-F BLACKLIST_DST')
iptables('-F BLACKLIST_SRC')
blacklist=[]
# Recherche sur le champ ablacklist (clubs compris)
search = db.search('ablacklist=*&paiement=%s'% ann_scol)
for entite in search['adherent']+search['club']:
self.anim.cycle()
sanctions = entite.blacklist_actif()
if 'upload' in sanctions or 'warez' in sanctions or 'p2p' in sanctions or 'autodisc_p2p' in sanctions or 'autodisc_upload' in sanctions :
blacklist+=entite.machines()
# Recherche sur le champ mblacklist (clubs aussi compris mais on extrait pas les résultats)
search = db.search('mblacklist=*&paiement=%s'% 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 or 'p2p' in sanctions or 'autodisc_p2p' in sanctions or 'autodisc_upload' in sanctions :
blacklist+=[entite]
for machine in blacklist:
iptables("-A BLACKLIST_DST -d %s -j REJECT --reject-with icmp-host-prohibited" % machine.ip())
iptables("-A BLACKLIST_SRC -s %s -j REJECT --reject-with icmp-host-prohibited" % machine.ip())
self.anim.reinit()
print OK
def filtre_p2p(self):
""" Construit la chaines de filtrage du p2p (FILTRE_P2P) """
self.anim = anim("\tFiltrage p2p")
iptables('-F FILTRE_P2P')
# On ne filtre que ce qui passe sur l'interface externe
iptables('-A FILTRE_P2P -i %s -o %s -j RETURN' % (self.eth_int, self.eth_int) )
for filtre in self.filtres_p2p :
iptables('-A FILTRE_P2P -m ipp2p --%s -j LOG --log-prefix "IPP2P=%s "' % (filtre[0],
filtre[1]))
#iptables('-A FILTRE_P2P -m ipp2p --%s -j REJECT --reject-with icmp-admin-prohibited' % filtre[0])
iptables('-A FILTRE_P2P -m ipp2p --%s -j RETURN' % filtre[0])
self.anim.cycle()
self.anim.reinit()
print OK
def serveurs_maj(self) :
self.exception_catcher(self.serveurs_vers_ext)
"""
Zamok
"""
class firewall_zamok(firewall_crans) :
"""
Structure du firewall :
table nat :
SERV_OUT_ADM
TEST_MAC-IP
table filter :
FORWARD (policy par defaut : DROP)
rien ne passe pas la chaine FORWARD
INPUT (policy par defaut : ACCEPT pour l'instant)
"""
# interfaces physiques
eth_pub = "eth0"
eth_adm = "eth0.2"
def serv_out_adm(self) :
self.anim = anim('\tOutput vers VLAN adm', len(self.adm_users))
# Supression des éventuelles règles
iptables("-t filter -F SERV_OUT_ADM")
for user in self.adm_users :
self.anim.cycle()
try:
iptables("-A SERV_OUT_ADM -m owner --uid-owner %d -j ACCEPT" % pwd.getpwnam(user)[2])
except KeyError:
continue
# LDAP et DNS toujours joignable
iptables("-A SERV_OUT_ADM -p tcp --dport ldap -j ACCEPT")
iptables("-A SERV_OUT_ADM -p tcp --dport domain -j ACCEPT")
iptables("-A SERV_OUT_ADM -p udp --dport domain -j ACCEPT")
# Pour le nfs (le paquet à laisser passer n'a pas d'owner)
iptables("-A SERV_OUT_ADM -d nfs.adm.crans.org -m owner ! --uid-owner 0 -j REJECT --reject-with icmp-net-prohibited")
iptables("-A SERV_OUT_ADM -d nfs.adm.crans.org -j ACCEPT")
# Rien d'autre ne passe
iptables("-A SERV_OUT_ADM -j REJECT --reject-with icmp-net-prohibited")
self.anim.reinit()
print OK
def nat_table(self) :
self.anim = anim('\tStructure de la table nat')
iptables('-t nat -N TEST_MAC-IP')
iptables('-t filter -N SERV_OUT_ADM')
iptables("-t nat -P PREROUTING ACCEPT")
iptables("-t nat -A PREROUTING -i lo -j ACCEPT")
iptables("-t nat -A PREROUTING -d 224.0.0.0/4 -j DROP")
iptables("-t nat -A PREROUTING -s 138.231.136.0/21 -j TEST_MAC-IP")
iptables("-t nat -A PREROUTING -s 138.231.144.0/24 -j TEST_MAC-IP")
iptables("-t nat -A PREROUTING -s 138.231.148.0/22 -j TEST_MAC-IP")
iptables("-t filter -A OUTPUT -o lo -j ACCEPT")
# pour une connection entrante venant du VLAN adm, il faut que le ACK
# puisse passer (interaction avec pam, uid de l'utilisateur en OUTPUT)
# on accepte donc les paquets si la connection est
# en RELATED et ESTABLISHED
iptables("-t filter -A OUTPUT -m state --state RELATED,ESTABLISHED -j ACCEPT")
iptables("-t filter -A OUTPUT -o %s -j SERV_OUT_ADM" % self.eth_adm)
iptables("-t nat -P PREROUTING ACCEPT")
iptables("-t filter -P OUTPUT ACCEPT")
print OK
def start_fw_funcs(self) :
self.exception_catcher(self.test_mac_ip)
self.serv_out_adm()
def filter_table_tweaks(self) :
self.anim = anim('\tRegles specifiques a zamok')
iptables("-P INPUT ACCEPT")
iptables("-P FORWARD DROP")
print OK
"""
Rouge
"""
class firewall_rouge(firewall_crans) :
"""
Structure du firewall :
table nat :
SERV_OUT_ADM
TEST_MAC-IP
table filter :
FORWARD (policy par defaut : DROP)
rien ne passe pas la chaine FORWARD
INPUT (policy par defaut : ACCEPT pour l'instant)
"""
# interfaces physiques
eth_pub = "eth0"
eth_adm = "eth0.2"
def nat_table(self) :
self.anim = anim('\tStructure de la table nat')
iptables('-t nat -N TEST_MAC-IP')
iptables("-t nat -P PREROUTING ACCEPT")
iptables("-t nat -A PREROUTING -i lo -j ACCEPT")
iptables("-t nat -A PREROUTING -d 224.0.0.0/4 -j DROP")
iptables("-t nat -A PREROUTING -s 138.231.136.0/21 -j TEST_MAC-IP")
iptables("-t nat -A PREROUTING -s 138.231.144.0/24 -j TEST_MAC-IP")
iptables("-t nat -A PREROUTING -s 138.231.148.0/22 -j TEST_MAC-IP")
iptables("-t nat -P PREROUTING ACCEPT")
iptables("-t nat -P OUTPUT ACCEPT")
print OK
def filter_table_tweaks(self) :
self.anim = anim('\tRegles specifiques a rouge')
iptables("-P INPUT ACCEPT")
iptables("-P FORWARD DROP")
print OK
"""
Vert
"""
class firewall_vert(firewall_crans) :
"""
Structure du firewall :
table nat :
MAC-IP
table filter :
FORWARD (policy par defaut : DROP)
rien ne passe pas la chaine FORWARD
INPUT (policy par defaut : ACCEPT pour l'instant)
"""
# interfaces physiques
eth_pub = "eth0"
def nat_table(self) :
self.anim = anim('\tStructure de la table nat')
iptables('-t nat -N TEST_MAC-IP')
iptables("-t nat -P PREROUTING ACCEPT")
iptables("-t nat -A PREROUTING -i lo -j ACCEPT")
iptables("-t nat -A PREROUTING -d 224.0.0.0/4 -j DROP")
iptables("-t nat -A PREROUTING -s 138.231.136.0/21 -j TEST_MAC-IP")
iptables("-t nat -A PREROUTING -s 138.231.144.0/24 -j TEST_MAC-IP")
iptables("-t nat -A PREROUTING -s 138.231.148.0/22 -j TEST_MAC-IP")
iptables("-t nat -P PREROUTING ACCEPT")
iptables("-t nat -P OUTPUT ACCEPT")
print OK
def filter_table_tweaks(self) :
self.anim = anim('\tRegles specifiques a vert')
iptables("-P INPUT ACCEPT")
iptables("-P FORWARD DROP")
print OK
class firewall_sila(firewall_rouge):
"""Comme pour rouge, avec le proxy transparent en plus"""
def nat_table(self):
firewall_rouge.nat_table(self)
# Pour le proxy transparent
iptables("-t nat -I PREROUTING -i eth0.2 -m mark --mark 2 -j ACCEPT")
iptables("-t mangle -F PREROUTING")
iptables("-t mangle -i eth0.2 -A PREROUTING -p tcp --destination-port 3128 " +
"--destination 138.231.144.10 " +
"-m mac --mac-source %s -j MARK --set-mark 2" % self.mac_komaz)
firewall_bleu = firewall_zamok
if __name__ == '__main__' :
# Chaines pouvant être recontruites
fw = eval('firewall_%s()' % hostname)
chaines = []
for nom in dir(fw) :
if nom in [ 'log_chaines' , 'test_virus_flood', 'reseaux_non_routables', 'test_mac_ip' , 'blacklist' , 'ext_vers_serveurs' , 'serveurs_vers_ext', 'ext_vers_crans', 'crans_vers_ext' , 'filtre_p2p', 'admin_vlan' , 'serv_out_adm'] :
chaines.append(nom)
def __usage(txt=None) :
if txt!=None : cprint(txt,'gras')
print """Usage:
%(p)s start : Construction du firewall.
%(p)s restart : Reconstruction du firewall.
%(p)s stop : Arrêt du firewall.
%(p)s <noms de chaines> : reconstruit les chaines
Les chaines pouvant être reconstruites sont :
%(chaines)s
Pour reconfiguration d'IPs particulières, utiliser generate. """ % \
{ 'p' : sys.argv[0].split('/')[-1] , 'chaines' : '\n '.join(chaines) }
sys.exit(-1)
# Bons arguments ?
if len(sys.argv) == 1 :
__usage()
for arg in sys.argv[1:] :
if arg in [ 'stop', 'restart', 'start' ] and len(sys.argv) != 2 :
__usage("L'argument %s ne peut être employé que seul." % arg)
if arg not in [ 'stop', 'restart', 'start' ] + chaines :
__usage("L'argument %s est inconnu." % arg)
for arg in sys.argv[1:] :
eval('fw.%s()' % arg )