
Explication : La diffrence entre les machine de cette zone est principalement la place dans le fw (soit dans leur table spare, soit avec le reste) de manire viter aux serveurs de parcourir les rgles adhrent. Cela est utile principalement pour les serveurs ayant besion de sortir. De plus les machines de cette zone doivent avoir leurs port ouvert en sorie explicitement entrs dans la base LDAP. darcs-hash:20050831110222-41617-876e47ea16bc723e9a8e22177aa60508c566ec4b.gz
927 lines
35 KiB
Python
Executable file
927 lines
35 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 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.
|
|
|
|
import sys
|
|
sys.path.append('/usr/scripts/gestion')
|
|
|
|
import syslog
|
|
import pwd
|
|
from lock import *
|
|
from ldap_crans import crans_ldap, ann_scol, machine, crans, invite, hostname
|
|
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:0c:f1:fa:f1:4b'
|
|
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(' Structure 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(" -> 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(" -> fin de la procédure d'arrêt",'vert')
|
|
|
|
def __stop(self) :
|
|
self.anim = anim(" Suppression 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(' Chaine 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 -P PREROUTING DROP")
|
|
self.anim.reinit()
|
|
print OK
|
|
|
|
self.exception_catcher(procedure)
|
|
|
|
|
|
def __test_mac_ip(self,machine):
|
|
ip=machine.ip()
|
|
mac=machine.mac()
|
|
|
|
# FIXME: ceci est loin d'etre efficace
|
|
# fascisme: on met les machines du crans en premier
|
|
if machine.proprietaire().__class__ == crans:
|
|
insert = '-I'
|
|
else:
|
|
insert = '-A'
|
|
if machine.ipsec():
|
|
# Machine wifi, c'est la mac de Nectaris
|
|
iptables("-t nat %s TEST_MAC-IP -s "%(insert)+\
|
|
"%s -m mac --mac-source %s -j ACCEPT"%(ip,self.mac_wifi))
|
|
else:
|
|
# Machine fixe
|
|
iptables("-t nat %s TEST_MAC-IP -s "%(insert)+\
|
|
"%s -m mac --mac-source %s -j ACCEPT"%(ip,mac))
|
|
|
|
def _serveur_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(' Analyse 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
|
|
else :
|
|
# Il faut avoir payé ou être une machine du crans ou un invite
|
|
if db.search('paiement=ok&ip=%s'% ip)['machine'] or \
|
|
machine[0].proprietaire().__class__ == crans or \
|
|
machine[0].proprietaire().__class__ == invite :
|
|
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._serveur_maj()
|
|
|
|
# Correspondance MAC-IP
|
|
if mac_ip_maj or serveur_maj :
|
|
def procedure() :
|
|
warn = ''
|
|
self.anim = anim(' Actualisation 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 ( machine.ipsec() and mac!=self.mac_wifi ) \
|
|
or ( not machine.ipsec() 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(' Chaî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 (REJECT sur tout le trafic p2p)
|
|
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(' 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 nat_table(self) :
|
|
self.anim = anim(' Structure 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 -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 DROP")
|
|
iptables("-t nat -P OUTPUT ACCEPT")
|
|
print OK
|
|
|
|
def filter_table_tweaks(self) :
|
|
self.anim = anim(' rè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 )
|
|
print OK
|
|
|
|
def post_start_hook(self) :
|
|
self.anim = anim(" Mise 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(" Arret 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(' 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 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 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(' Chaî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' ] :
|
|
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() :
|
|
""" FIXME: bug dans la base ldap """
|
|
if ip == '138.231.141.26':
|
|
for fixed_port in port.split(','):
|
|
iptables("-I EXT_VERS_CRANS -d %s -p %s --dport %s -j ACCEPT" \
|
|
%(ip,proto,fixed_port))
|
|
continue
|
|
|
|
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(" Blackliste")
|
|
iptables('-F BLACKLIST_DST')
|
|
iptables('-F BLACKLIST_SRC')
|
|
|
|
blacklist=[]
|
|
search = db.search('blacklist=*&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 :
|
|
from ldap_crans import machine
|
|
if entite.__class__ == machine:
|
|
blacklist+=[entite]
|
|
else:
|
|
blacklist+=entite.machines()
|
|
|
|
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(" Filtrage 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 port in self.ports_p2p :
|
|
iptables('-A FILTRE_P2P -p tcp --dport %s -j REJECT --reject-with icmp-admin-prohibited' % port )
|
|
iptables('-A FILTRE_P2P -p udp --dport %s -j REJECT --reject-with icmp-admin-prohibited' % port )
|
|
self.anim.cycle()
|
|
|
|
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])
|
|
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(' Output 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(' Structure 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 nat -A PREROUTING -m mac --mac-source %s -j ACCEPT"
|
|
% (self.mac_komaz))
|
|
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 DROP")
|
|
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(' regles 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(' Structure 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 -A PREROUTING -m mac --mac-source %s -j ACCEPT"
|
|
% (self.mac_komaz))
|
|
|
|
iptables("-t nat -P PREROUTING DROP")
|
|
iptables("-t nat -P OUTPUT ACCEPT")
|
|
print OK
|
|
|
|
def filter_table_tweaks(self) :
|
|
self.anim = anim(' regles 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(' Structure 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 -A PREROUTING -m mac --mac-source %s -j ACCEPT"
|
|
% (self.mac_komaz))
|
|
|
|
iptables("-t nat -P PREROUTING DROP")
|
|
iptables("-t nat -P OUTPUT ACCEPT")
|
|
print OK
|
|
|
|
def filter_table_tweaks(self) :
|
|
self.anim = anim(' regles specifiques a vert')
|
|
iptables("-P INPUT ACCEPT")
|
|
iptables("-P FORWARD DROP")
|
|
print OK
|
|
|
|
firewall_sila = firewall_rouge
|
|
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 )
|