
merci Vincent et Manu, :) comme cela, c'est plus simple et ca devrait mieux passer. (les regles sont bien les memes cette fois ci) (desole j'avais teste une version legerement decalee (sans le set de la policy avant) sur egon ;)) darcs-hash:20050518123110-f163d-0f40630eba2c42ad0246645c2f718af53bc71f6f.gz
367 lines
15 KiB
Python
Executable file
367 lines
15 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>
|
|
#
|
|
# Rewritten as an inherited class 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.
|
|
""" Firewall de Komaz """
|
|
import sys
|
|
sys.path.append('/usr/scripts/gestion')
|
|
|
|
import syslog
|
|
from firewall_crans import firewall_crans, IptablesError, iptables
|
|
from lock import *
|
|
from ldap_crans import crans_ldap, ann_scol, machine, crans, invite
|
|
from affich_tools import *
|
|
from commands import getstatusoutput
|
|
from iptools import AddrInNet
|
|
syslog.openlog('firewall')
|
|
|
|
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' ]
|
|
|
|
debug = 1
|
|
|
|
def nat_table_tweaks(self) :
|
|
self.anim = anim(' règles spécifiques à komaz')
|
|
for chaine in [ 'LOG_VIRUS', 'LOG_FLOOD', 'TEST_VIRUS_FLOOD' ] :
|
|
iptables('-t nat -N %s' % chaine)
|
|
|
|
iptables("-t nat -I PREROUTING 3 -s ! %s -j TEST_VIRUS_FLOOD" % self.zone_serveur)
|
|
iptables("-t nat -I PREROUTING 6 -i %s -j ACCEPT" % self.eth_ext )
|
|
iptables("-t nat -I PREROUTING 6 -s %s -j ACCEPT" % self.zone_serveur )
|
|
iptables("-t nat -I PREROUTING 6 -d %s -j ACCEPT" % self.zone_serveur )
|
|
iptables("-t nat -R PREROUTING 5 -i %s -j RESEAUX_NON_ROUTABLES_SRC" % self.zone_serveur )
|
|
print OK
|
|
|
|
def filter_table_tweaks(self) :
|
|
self.anim = anim(' règles spécifiques à komaz')
|
|
for chaine in [ '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 -o %s -d %s -j REJECT" % (self.eth_int, 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 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 enable_route(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_h323',
|
|
'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 disable_route(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.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 __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() :
|
|
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 = crans_ldap().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)
|
|
|
|
|
|
|
|
|
|
if __name__ == '__main__' :
|
|
# Chaines pouvant être recontruites
|
|
global chaines
|
|
chaines = [ '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' ]
|
|
|
|
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 chaine <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)
|
|
|
|
fw = firewall_komaz()
|
|
for arg in sys.argv[1:] :
|
|
eval('fw.%s()' % arg)
|