scripts/gestion/gen_confs/firewall_new.py
Valentin Samir c71900a5ba [firewall_new,generate,ldap_crans] Prise en compte des déco chambre invalide et carte étudiant
Ignore-this: 16f86d33fe0e98057fd9533abc25aa88
TOujours effectué sur squid, elles était alors simplement ignoré
puisque la connection au web est maintemant directe.
On ajoute donc des blacklistes soft "virtuelle" pour carte et
chambre invalide dans ldap_crans, ce qui a pour effet de faire
que le pare-feu redirige vers sable (et squid) les machines consernées.
On dit à générate de recharger les blackliste du pare-feu lorsque l'on coche
une carte étudiant dans gest_crans ou quand le status chambre invalide change.
btw, on reporte aussi les modifications de crans_ldap dans lc_ldap.

darcs-hash:20121104020449-3a55a-78f73e677e75e8eaa087fd9bd6f8c1aec2b593ea.gz
2012-11-04 03:04:49 +01:00

1562 lines
64 KiB
Python
Executable file

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# 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>
# Nicolas Salles <salles@crans.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
# Copyright (c) 2006 Nicolas Salles
#
# 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')
sys.path.append('/usr/scripts/lc_ldap')
import syslog
import pwd
import commands
from lock import *
from ldap_crans import crans_ldap, ann_scol, hostname
from ldap_crans import AssociationCrans, Machine, MachineWifi, BorneWifi
from affich_tools import *
from commands import getstatusoutput
from iptools import AddrInNet, NetSubnets, IpSubnet
from config import NETs, mac_komaz, mac_wifi, mac_titanic, mac_g, conf_fw, p2p, vlans, debit_max_radin, adm_users, accueil_route
from ipset import IpsetError, Ipset
from lc_ldap import lc_ldap
syslog.openlog('firewall')
debug = 1
db = crans_ldap()
QUERY=lc_ldap(uri='ldap://ldap.adm.crans.org/')
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)
class TcError(Exception):
""" Gestion des erreurs de tc """
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
def iptables_save():
""" Sauvegarde d'ipatbles """
status,output=getstatusoutput("mkdir -p /var/log/firewall/")
if status:
raise IptablesError(cmd,status,output)
status,output=getstatusoutput("/sbin/iptables-save > /var/log/firewall/backup-firewall_`date +%Y-%m-%d_%H:%M:%S`.log" )
if status:
raise IptablesError(cmd,status,output)
return output
def tc(cmd):
""" Interface de tc """
syslog.syslog(syslog.LOG_INFO, cmd)
status, output = getstatusoutput("/sbin/tc " + cmd)
if status:
raise TcError(cmd, status, output)
return output
def redirect_chain(table, chain_in, chain_out, ip) :
try:
iptables("-t %s -N %s" % (table, chain_out))
except IptablesError:
iptables("-t %s -F %s" % (table, chain_out))
# On redirige les paquets de la chaîne in dans la chaîne out
iptables("-t %s -A %s -o ens -s %s -j %s" % (table, chain_in, ip, chain_out))
iptables("-t %s -A %s -o crans -d %s -j %s" % (table, chain_in, ip, chain_out))
class firewall_crans :
"""
Classe parente pour les firewalls du crans
Implémentée directement à partir du firewall de komaz, initialement
écrit par Manuel Sabban et Frédéric Pauget.
* les méthodes à surcharger pour l'implémentation eme des firewall
sont mangle_table, nat_table, filter_table, pour la préparation
du fw, start_fw_funcs pour la mise en place du filtrage.
* 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 factorisées pour la simple et bonne raison que les sources
de la liste to_do originale ne seront pas forcément identiques
(c'est un peu sale...)
* la classe parente contient à peu de choses prés tout ce qu'il
faut pour mettre en place un fw basique n'effectuant que la
verif MAC-IP.
"""
zone_serveur = NETs['serveurs'][0]
vlan_adm = NETs['adm'][0]
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 100/second --hashlimit-mode srcip,dstip,dstport --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')
self.mac_ip_set = Ipset("MAC-IP","macipmap","--from 138.231.136.0 --to 138.231.151.255")
self.mac_ip_adm_set = Ipset("MAC-IP-ADM","macipmap","--from 10.231.136.0 --to 10.231.136.255")
try:
self.mac_ip_set.list()
except IpsetError:
self.mac_ip_set.create()
try:
self.mac_ip_adm_set.list()
except IpsetError:
self.mac_ip_adm_set.create()
def __del__(self) :
""" Destruction du lock """
# Comprend pas pourquoi il faut réimporter ici -- Fred
from lock import remove_lock
remove_lock('firewall')
def reload_qos(self):
"""Recherche la QoS"""
return
def mangle_table(self) :
""" Remplit la table mangle """
return
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(u'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(u'Démarrage firewall', 'gras')
# Préliminaires
if not self.__machines() or self.exception_catcher(self.__stop) :
cprint(u"Abandon", 'rouge')
return
# Initialisation
self.exception_catcher(self.mangle_table)
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(u"\t -> fin de la procédure de démarrage",'vert')
def stop(self):
""" Arrête le firewall """
cprint(u"Arrêt du firewall", 'gras')
self.pre_stop_hook()
self.exception_catcher(self.__stop)
cprint(u"\t -> fin de la procédure d'arrêt",'vert')
def __stop(self) :
iptables_save()
self.anim = anim("\tSuppression des règles")
iptables("-t nat -P PREROUTING ACCEPT")
iptables("-F")
iptables("-t nat -F")
iptables("-t mangle -F")
iptables("-X")
iptables("-t nat -X")
iptables("-t mangle -X")
print OK
def test_mac_ip(self):
iptables('-t filter -F TEST_MAC-IP')
self.mac_ip_gen()
iptables('-t filter -A TEST_MAC-IP -m set --match-set %s src -j RETURN' % self.mac_ip_set.set)
iptables('-t filter -A TEST_MAC-IP -m set --match-set %s src -j RETURN' % self.mac_ip_adm_set.set)
# Proxy ARP de Komaz et Titanic pour OVH
ip_ovh = db.search('host=ovh.adm.crans.org')['machineCrans'][0].ip()
iptables('-t filter -A TEST_MAC-IP -m mac -s %s --mac-source %s -j RETURN' % (ip_ovh, mac_komaz))
iptables('-t filter -A TEST_MAC-IP -m mac -s %s --mac-source %s -j RETURN' % (ip_ovh, mac_titanic))
iptables('-t filter -A TEST_MAC-IP -j DROP')
def __test_mac_ip(self, machine, flushed = False):
ip = machine.ip()
if ip.startswith("138.231.1"):
if not flushed:
try:
self.mac_ip_set.delete(ip)
except IpsetError:
pass
if machine.__class__.__name__ == "MachineWifi" and hostname != 'gordon':
# Machine Wifi, c'est la mac de gordon
self.mac_ip_set.add("%s,%s" % (ip,mac_wifi))
else:
# Machine fixe
self.mac_ip_set.add("%s,%s" % (ip,machine.mac()))
elif ip.startswith("10.231.136."):
if not flushed:
try:
self.mac_ip_adm_set.delete(ip)
except IpsetError:
pass
self.mac_ip_adm_set.add("%s,%s" % (ip,machine.mac()))
def __test_mac_ip_delete(self, ip):
if ip.startswith("138.231.1"):
try:
self.mac_ip_set.delete(ip)
except IpsetError:
pass
elif ip.startswith("10.231.136."):
try:
self.mac_ip_adm_set.delete(ip)
except IpsetError:
pass
def mac_ip_maj(self, ip_list):
for ip in ip_list:
machines=db.search("ip=%s" % ip)['machine']
if not machines:
self.__test_mac_ip_delete(ip)
else:
try:
#~ self.mac_ip_set.add("%s,%s" % (ip, machines[0].mac()))
self.__test_mac_ip(machines[0])
except IpsetError:
pass
def mac_ip_gen(self):
self.anim = anim('\tChaîne TEST_MAC-IP', len(self.__machines()))
self.mac_ip_set.flush()
self.mac_ip_adm_set.flush()
self.anim.reinit()
for machine in self.__machines():
self.anim.cycle()
self.__test_mac_ip(machine, flushed = True)
self.anim.reinit()
print OK
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 chaînes 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 build_chaine_adherent(self,chaine,methode) :
# On construit d'abord les autorisations particulières
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 mangle :
PREROUTING (policy par défaut : ACCEPT)
1) proxy transparent
2) marquage des paquets bittorrent
POSTROUTING (policy par défaut : ACCEPT)
1) passage dans un sous-réseau de l'ip crans : SUBNET
SUBNET classe pour chaque ip de son réseau dans la classe htb correspondante
table nat :
PREROUTING (policy par défaut : ACCEPT)
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 défaut : ACCEPT)
1) passage par BLACKLIST
2) passage par FILTRE_P2P (ACCEPT sur le trafic de filtres_p2p, REJECT sur le trafic de
filtres_p2p_bloq, sanctions 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 : logging des paquets matchés par les protocoles de filtres_p2p
rejet des paquets matchés par les protocoles de filtres_p2p_bloq au dessus de la limite
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
INGRESS_FILTERING : ne laisse sortir que les paquets dont l'adresse IP source appartient au crans
"""
# interfaces physiques
eth_ext = "ens"
eth_int = "crans"
eth_adm = "crans.2"
# Ports ouverts
ports_default = { 'tcp_EXT_VERS_CRANS' : [ '22' ],
'tcp_CRANS_VERS_EXT': [ ':24', '26:79', '80: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
# Apple et WinMX desactives car possibilite de fausse detection par ipp2p
filtres_p2p = [ #('apple', 'AppleJuice'),
('soul', 'SoulSeek'),
#('winmx', 'WinMX'),
('edk', 'eDonkey'),
('dc', 'DirectConnect'),
('kazaa', 'KaZaa'),
('ares', 'Ares'),
('bit', 'Bittorrent'),
('gnu', 'GNUtella') ]
filtres_p2p_bloq = [
]
udp_torrent_tracker={
'tracker.openbittorrent.com':[['95.215.62.26',80],['95.215.62.5',80]],
'tracker.ccc.de':[['195.54.164.83',80]],
'tracker.istole.it':[['192.121.121.30',80]],
'tracker.publicbt.com':[['95.211.88.54',80],['95.211.88.49',80],['95.211.88.51',80]],
}
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', '100.64.0.0/10']
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))
iptables("-t filter -A RESEAUX_NON_ROUTABLES_DST -d 10.231.136.0/24 -j RETURN")
iptables("-t filter -A RESEAUX_NON_ROUTABLES_SRC -d 10.231.136.0/24 -j RETURN")
iptables("-t filter -A RESEAUX_NON_ROUTABLES_DST -d %s -j RETURN" % NETs['personnel-ens'][0])
iptables("-t filter -A RESEAUX_NON_ROUTABLES_SRC -s %s -j RETURN" % NETs['personnel-ens'][0])
for reseau in self.liste_reseaux_non_routables :
iptables("-t filter -A RESEAUX_NON_ROUTABLES_DST -d %s -j DROP" % reseau)
iptables("-t filter -A RESEAUX_NON_ROUTABLES_SRC -s %s -j DROP" % reseau)
self.anim.cycle()
self.anim.reinit()
print OK
def reload_qos(self):
self.mangle_table()
self.qos()
def mangle_table(self):
self.anim = anim('\tStructure de la table mangle')
# On vide complètement la table
iptables("-t mangle -F")
iptables("-t mangle -X")
#Log de paquets
iptables('-t mangle -A PREROUTING -i %s -m state --state NEW -j LOG --log-prefix "LOG_ALL "' % self.eth_int)
iptables('-t mangle -A PREROUTING -i %s -m state --state NEW -j LOG --log-prefix "LOG_ALL "' % self.eth_ext)
# Proxy transparent
iptables("-t mangle -A PREROUTING -s %s -j RETURN" % self.zone_serveur)
#~ iptables("-t mangle -A PREROUTING -p tcp --destination-port 80 "
#~ "-s %s -d ! %s -j MARK --set-mark %s" %
#~ (NETs['fil'][0], NETs['wifi'][0], conf_fw.mark['proxy']))
#~ iptables("-t mangle -A PREROUTING -m mark --mark %s -j ACCEPT" %
#~ conf_fw.mark['proxy'])
iptables("-t mangle -N BLACKLIST_SOFT")
iptables("-t mangle -A PREROUTING -p tcp --destination-port 80 "
"-s %s -d ! %s -j BLACKLIST_SOFT" %
(NETs['fil'][0], NETs['wifi'][0]))
iptables("-t mangle -A PREROUTING -m mark --mark %s -j ACCEPT" % conf_fw.mark['proxy'])
#connection de secours
iptables("-t mangle -A PREROUTING -p tcp -s 138.231.136.0/16 ! -d 138.231.136.0/16 --destination-port 80 -m condition --condition secours -j MARK --set-mark %s" % (conf_fw.mark['secours']))
iptables("-t mangle -A PREROUTING -m mark --mark %s -j ACCEPT" % conf_fw.mark['secours'])
# Parametres pour iptables/tc
mark = conf_fw.mark['bittorrent']
debit_max = conf_fw.debit_max
debit_max_semi=debit_max/2
eth_ext = self.eth_ext
eth_int = self.eth_int
# Classification du traffic : extérieur <-> ftp
iptables("-t mangle -A POSTROUTING -o %(eth_int)s -d 138.231.136.98 "
"-j CLASSIFY --set-class 1:9997" % locals())
iptables("-t mangle -A POSTROUTING -o %(eth_ext)s -s 138.231.136.98 "
"-j CLASSIFY --set-class 1:9997" % locals())
# On marque les paquets bittorrent uniquement
iptables("-t mangle -A PREROUTING -p tcp -j CONNMARK --restore-mark")
#iptables("-t mangle -A PREROUTING -p tcp -m mark ! --mark 0x0 -j ACCEPT")
iptables("-t mangle -A PREROUTING -p tcp -m ipp2p --bit "
"-j MARK --set-mark %s" % mark)
iptables("-t mangle -A PREROUTING -p tcp -m mark --mark %s "
"-j CONNMARK --save-mark" % mark)
warn = ''
# pas de QoS pour la zone ens
iptables("-t mangle -A POSTROUTING -d 138.231.0.0/16 -s 138.231.0.0/16 -j RETURN")
# Par défaut, on envoit les paquets dans la classe 9998
for net in NETs['all']:
iptables("-t mangle -A POSTROUTING -o %(eth_int)s -d %(net)s "
"-j CLASSIFY --set-class 1:9998" % locals())
iptables("-t mangle -A POSTROUTING -o %(eth_ext)s -s %(net)s "
"-j CLASSIFY --set-class 1:9998" % locals())
for net in NETs['personnel-ens']:
# pas de limitation en download
#iptables("-t mangle -A POSTROUTING -d %(net)s "
# "-j CLASSIFY --set-class 1:9998" % locals())
iptables("-t mangle -A POSTROUTING -s %(net)s "
"-j CLASSIFY --set-class 1:9998" % locals())
# On crée les chaînes de sous-réseaux
for net in NETs['all']:
for mask in conf_fw.mask:
for subnet in NetSubnets(net, mask):
index = conf_fw.mask.index(mask)
if index == 0:
prev_chain = "POSTROUTING"
else:
ip = subnet.split('/')[0]
prev_subnet = IpSubnet(ip, conf_fw.mask[index-1])
prev_chain = "SUBNET-%s" % prev_subnet
next_chain = "SUBNET-%s" % subnet
redirect_chain('mangle', prev_chain, next_chain, subnet)
print OK
self.anim = anim('\tLimitation du debit')
adherents = db.search('paiement=ok')['adherent']
self.adherents=adherents
debit_adh = int(debit_max / float(len(adherents)))
# Création des classes et qdisc
for interface in [eth_ext, eth_int]:
# On vide les classes et qdisc
try:
tc("qdisc del dev %s root" % interface)
except TcError, c:
warn += str(c) + '\n'
# On construit les classes et qdisc de base
# La partie principale qui définit le comportement par défaut
tc("qdisc add dev %(interface)s root handle 1: htb r2q 1" % locals())
tc("class add dev %(interface)s parent 1: classid 1:1 "
"htb rate %(debit_max)skbps ceil %(debit_max)skbps" % locals())
tc("class add dev %(interface)s parent 1:1 classid 1:9999 "
"htb rate %(debit_adh)skbps ceil %(debit_adh)skbps" % locals())
tc("qdisc add dev %(interface)s parent 1:9999 "
"handle 9999: sfq perturb 10" % locals())
debit_ftp = 1000 # kbps
tc("class add dev %(interface)s parent 1:1 classid 1:9997 "
"htb rate %(debit_ftp)skbps ceil %(debit_max_semi)skbps prio 1" % locals())
tc("qdisc add dev %(interface)s parent 1:9997 "
"handle 9997: sfq perturb 10" % locals())
tc("class add dev %(interface)s parent 1:1 classid 1:9998 "
"htb rate %(debit_max_semi)skbps ceil %(debit_max)skbps prio 1" % locals())
tc("qdisc add dev %(interface)s parent 1:9998 "
"handle 9998: sfq perturb 10" % locals())
for interface in ["crans.21"]:
# On vide les classes et qdisc
try:
tc("qdisc del dev %s root" % interface)
except TcError, c:
warn += str(c) + '\n'
# On construit les classes et qdisc de base
# La partie principale qui définit le comportement par défaut
tc("qdisc add dev %(interface)s root handle 1: htb r2q 1" % locals())
tc("class add dev %(interface)s parent 1: classid 1:1 "
"htb rate 128kbps ceil 128kbps" % locals())
tc("class add dev %(interface)s parent 1:1 classid 1:9998 "
"htb rate 128kbps ceil 128kbps prio 1" % locals())
tc("qdisc add dev %(interface)s parent 1:9998 "
"handle 9998: sfq perturb 10" % locals())
print OK
def qos(self):
if len(self.adherents) == 0 :
self.mangle_table()
# Parametres pour iptables/tc
mark = conf_fw.mark['bittorrent']
debit_max = conf_fw.debit_max
debit_max_semi=debit_max/2
eth_ext = self.eth_ext
eth_int = self.eth_int
adherents = self.adherents
debit_adh = int(debit_max / float(len(adherents)))
self.anim = anim('\tGénération des classes de QoS', len(adherents))
# On construit ensuite les classes et qdisc pour chaque adhérent
for adherent in adherents:
self.anim.cycle()
# On ne peut pas reprendre le numéro 1
class_id = int(adherent.id()) + 1
# Il nous faut un n° inférieur à 9999 unique
qdisc_id = class_id
for interface in [self.eth_ext, self.eth_int]:
tc("class add dev %(interface)s parent 1:1 classid 1:%(class_id)d "
"htb rate %(debit_adh)skbps ceil %(debit_max)skbps prio 0" % locals())
tc("qdisc add dev %(interface)s parent 1:%(class_id)d "
"handle %(qdisc_id)d: sfq perturb 10" % locals())
# Classification des adhérents dans leur classe respective
for machine in adherent.machines():
ip = machine.ip()
if not AddrInNet(ip, NETs['all']):
# Cas particulier d'une machine ayant une IP non CRANS
continue
subnet = IpSubnet(machine.ip(), conf_fw.mask[-1])
iptables("-t mangle -A SUBNET-%(subnet)s -o crans -d %(ip)s "
"-j CLASSIFY --set-class 1:%(class_id)s" % locals())
iptables("-t mangle -A SUBNET-%(subnet)s -o ens -s %(ip)s "
"-j CLASSIFY --set-class 1:%(class_id)s" % locals())
# +-----------------+
# | QOS pour le ftp |
# +-----------------+
# On ne veut pas que les gens à l'éxtérieur bouffe toute la
# bande passante.
# Classification des paquets à destination du ftp
iptables("-t mangle -A POSTROUTING -o %(eth_int)s -p tcp -d 138.231.136.98 --dport 21 "
"-j CLASSIFY --set-class 1:9997" % locals())
self.anim.reinit()
print OK
def nat_table(self) :
self.anim = anim('\tStructure de la table nat')
iptables("-t nat -P PREROUTING ACCEPT")
iptables("-t nat -P OUTPUT ACCEPT")
iptables("-t nat -A PREROUTING -p tcp -d 138.231.136.2 --dport 22 -j DNAT --to-destination 138.231.136.1:22") # redirection du ssh vers zamok
iptables("-t nat -A PREROUTING -p tcp -d 138.231.136.2 --dport 443 -j DNAT --to-destination 138.231.136.1:22") # redirection du ssh vers zamok (pour passer dans un proxy, avec corkscrew)
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 )
# Proxy transparent
iptables("-t nat -A PREROUTING -p tcp -m mark --mark %s " % conf_fw.mark['proxy'] +
"-j DNAT --to-destination 10.231.136.9:3128")
# Appartement ENS
iptables("-t nat -A POSTROUTING -o ens -s %s -j SNAT --to 138.231.136.44" % NETs['personnel-ens'][0])
iptables("-t nat -A POSTROUTING -o crans -s %s -j SNAT --to 138.231.136.44" % NETs['personnel-ens'][0])
#Connection de secours
iptables("-t nat -A PREROUTING -p tcp -m mark --mark %s " % conf_fw.mark['secours'] +
"-j DNAT --to-destination 10.231.136.9:3128")
print OK
def filter_table(self) :
self.anim = anim('\tStructure de la table filter')
for chaine in [ 'TEST_MAC-IP', 'RESEAUX_NON_ROUTABLES_SRC', 'RESEAUX_NON_ROUTABLES_DST' ] :
iptables('-t filter -N %s' % chaine)
iptables("-A FORWARD -i lo -j ACCEPT")
iptables("-A FORWARD -p icmp -j ACCEPT")
iptables("-A FORWARD -i tun-ovh -j ACCEPT")
iptables("-A FORWARD -d 224.0.0.0/4 -j DROP")
#iptables("-A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT")
for net in NETs['fil'] + NETs['wifi']:
iptables("-A FORWARD -s %s -i %s -j TEST_MAC-IP" % (net, self.eth_int))
for net in NETs['adm']:
iptables("-A FORWARD -s %s -i %s -j TEST_MAC-IP" % (net, self.eth_adm))
iptables("-A FORWARD -j RESEAUX_NON_ROUTABLES_DST")
iptables("-A FORWARD -i %s -j RESEAUX_NON_ROUTABLES_SRC" % self.eth_ext )
# Proxy transparent, pour les deconnexion soft
iptables("-A FORWARD -m mark --mark %s -j ACCEPT" % conf_fw.mark['proxy'])
#Connection de secours
# on ne peut pas faire passer https dans un proxy transparent sans faire de man in the middle et sans recompiler squid
iptables("-A FORWARD -p tcp -s 138.231.136.0/16 ! -d 138.231.136.0/16 --destination-port 443 -m condition --condition secours -j REJECT")
iptables("-A FORWARD -m mark --mark %s -j ACCEPT" % conf_fw.mark['secours'])
iptables("-P FORWARD ACCEPT")
for net in NETs['fil'] + NETs['wifi']:
iptables("-A INPUT -s %s -i %s -j TEST_MAC-IP" % (net, self.eth_int))
for net in NETs['adm']:
iptables("-A INPUT -s %s -i %s -j TEST_MAC-IP" % (net, self.eth_adm))
iptables("-P INPUT ACCEPT")
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', 'INGRESS_FILTERING',
'TEST_VIRUS_FLOOD', 'LOG_VIRUS', 'LOG_FLOOD','LOG_TRACKER','TRACKER_FILTER' ] :
iptables('-N %s' % chaine)
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 -s %s -j ACCEPT" % NETs['personnel-ens'][0])
iptables("-A FORWARD -d %s -j ACCEPT" % NETs['personnel-ens'][0])
iptables("-A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT")
iptables("-A FORWARD -j INGRESS_FILTERING")
# on ne route pas les paquets n'appartenant pas à notre plage ip -- xhub
for net in NETs['all']:
iptables("-A INGRESS_FILTERING -o ens -s %s -j RETURN" % net)
iptables("-A INGRESS_FILTERING -o ens -j LOG --log-prefix BAD_ROUTE ")
iptables("-A INGRESS_FILTERING -o ens -j DROP")
# protection contre un certain type de spoof
# cf http://travaux.ovh.net/?do=details&id=5183 -- xhub
for net_d in NETs['all']:
for net in NETs['all']:
iptables("-A INGRESS_FILTERING -i ens -s ! %s -d %s -j RETURN" % (net,net_d))
iptables("-A INGRESS_FILTERING -i ens -j LOG --log-prefix BAD_SRC ")
iptables("-A INGRESS_FILTERING -i ens -j DROP")
iptables("-A FORWARD -i %s -d %s -j ADMIN_VLAN" % (self.eth_int, self.vlan_adm) )
iptables("-A FORWARD -i %s -d %s -j REJECT" % (self.eth_ext, self.vlan_adm) )
iptables("-A FORWARD -s ! %s -j TEST_VIRUS_FLOOD" % self.zone_serveur)
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 classes_p2p_maj(self, ip_list):
""" Mise à jour de la classification pour les ip fournies
On ne crée que les règles iptables pour classer les paquets, les
classes correspondantes ne sont à créer que toutes à la fois """
## Que faut-il faire ?
self.anim = anim('\tAnalyse du travail à effectuer')
if ip_list == ['']:
print OK + ' (rien à faire)'
return
print OK
## Traitement
# MAJ des règles de classification de l'IP
def procedure():
self.anim = anim('\tMise à jour des classes p2p')
# Liste des classes candidates a la suppression
scheduled_del = []
warn = ''
# Parametres pour iptables/tc
mark = conf_fw.mark['bittorrent']
debit_max = conf_fw.debit_max
debit_adh = int(conf_fw.debit_max / 1200.) # XXX: guesstimate
eth_ext = self.eth_ext
eth_int = self.eth_int
try:
for ip in ip_list:
recherche = db.search('ip=%s&paiement=ok' % ip)
# Si l'ip n'appartient pas à un adhérent,
# on ne cherche pas plus loin
if not recherche['adherent']:
continue
machines = recherche['machine']
if not machines:
# Il faut supprimer cette entrée
iptables_option = '-D'
subnet = IpSubnet(ip, conf_fw.mask[-1])
all_regles = iptables("-t mangle -L SUBNET-%(subnet)s -n" % locals()).split('\n')
regles = [line for line in all_regles if ip in line]
# On sélectionne la première qui doit contenir ce que l'on veut
regle = regles[0].split()
class_id = int(regle[7].split(':')[1])
# On marque la classe comme candidate a la suppression
scheduled_del.append(class_id)
elif len(machines) == 1:
# Il faut ajouter cette entrée
iptables_option = '-A'
machine = machines[0]
adherent = machine.proprietaire()
ip = machine.ip()
subnet = IpSubnet(ip, conf_fw.mask[-1])
# On ne peut pas reprendre le numéro 1
class_id = int(adherent.id()) + 1
# On cree la classe et la qdisc s'il elles n'existent pas deja
qdisc_id = class_id
try:
for interface in [eth_ext, eth_int]:
tc("class add dev %(interface)s "
"parent 1:1 classid 1:%(class_id)d htb "
"rate %(debit_adh)s ceil %(debit_max)s" % locals())
tc("qdisc add dev %(interface)s "
"parent 1:%(class_id)d handle %(qdisc_id)d: "
"sfq perturb 10" % locals())
except TcError, e:
if "File exists" in e.output:
# La classe existe deja
pass
else:
raise e
else:
warn += "Plusieurs machines avec l'IP %s\n" % ip
# Il nous faut un n° inférieur à 9999 unique
iptables("-t mangle %(iptables_option)s SUBNET-%(subnet)s "
"-o %(eth_int)s -d %(ip)s "
"-j CLASSIFY --set-class 1:%(class_id)s" % locals())
iptables("-t mangle %(iptables_option)s SUBNET-%(subnet)s "
"-o %(eth_ext)s -s %(ip)s "
"-j CLASSIFY --set-class 1:%(class_id)s" % locals())
except IptablesError, c:
warn += str(c) + '\n'
for class_id in scheduled_del:
# TODO: supprimer les classes qui sont vraiment vides
pass
if warn:
print WARNING
sys.stdout.write(warn)
else:
print OK
self.exception_catcher(procedure)
def post_start_hook(self) :
self.anim = anim("\tMise en place du routage")
warn = ''
for cmd in [ 'echo 1 > /proc/sys/net/ipv4/ip_forward' ,
'echo 65536 > /proc/sys/net/ipv4/netfilter/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("\tArrêt du 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)
self.exception_catcher(self.qos)
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 chaînes de log (LOG_VIRUS et LOG_FLOOD) """
self.anim = anim('\tCréation des chaînes de log')
for filtre in [ 'VIRUS', 'FLOOD'] :
# Vidage de la chaîne
iptables('-F LOG_%s' % filtre)
iptables('-A LOG_%s %s %s:' % (filtre, self.log_template, filtre.capitalize()) )
iptables('-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('-F TEST_VIRUS_FLOOD')
self.anim = anim('\tFiltrage virus et floods')
ip_vers = ('65.19.154.90', '208.72.168.52', '66.109.25.121', '193.37.152.88', '89.149.202.101')
for ip in ip_vers:
iptables('-A TEST_VIRUS_FLOOD -s %s -j LOG_VIRUS' % ip)
iptables('-A TEST_VIRUS_FLOOD -d %s -j LOG_VIRUS' % ip)
for proto, ports in self.ports_virus.items() :
for port in ports :
iptables('-A TEST_VIRUS_FLOOD -p %s --dport %s -j LOG_VIRUS' % (proto, port) )
self.anim.cycle()
iptables('-A TEST_VIRUS_FLOOD %s -j RETURN' % self.filtre_flood) # Les limites en négatif ca ne marche pas.
self.anim.cycle()
iptables('-A TEST_VIRUS_FLOOD -j LOG_FLOOD')
self.anim.reinit()
print OK
def serveurs_vers_ext(self) :
""" Reconstruit la chaîne 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 chaîne EXT_VERS_SERVEURS """
if not self.build_chaine('EXT_VERS_SERVEURS', self.__ext_vers_serveurs) :
self.anim.reinit()
print OK
# Attention les règles sont à l'envers. Hint '-I'
iptables("-I EXT_VERS_SERVEURS -p tcp --dport ssh -m state --state NEW\
-m recent --name SSH --update --seconds 60 --hitcount 4 --rttl -j DROP")
iptables("-I EXT_VERS_SERVEURS -p tcp --dport ssh -m state --state NEW\
-m recent --name SSH --set")
def crans_vers_ext(self) :
""" Reconstruit la chaîne CRANS_VERS_EXT """
self.build_chaine_adherent('CRANS_VERS_EXT',self.__crans_vers_ext)
# Protocole GRE pour les VPN
iptables("-I CRANS_VERS_EXT -p gre -j ACCEPT")
def ext_vers_crans(self) :
""" Reconstruit la chaîne EXT_VERS_CRANS """
self.build_chaine_adherent('EXT_VERS_CRANS',self.__ext_vers_crans)
# Attention les règles sont à l'envers. Hint '-I'
iptables("-I EXT_VERS_CRANS -p tcp --dport ssh -m state --state NEW -j ACCEPT")
iptables("-I EXT_VERS_CRANS -p tcp --dport ssh -m state --state NEW\
-m recent --name SSH --update --seconds 60 --hitcount 4 --rttl -j DROP")
iptables("-I EXT_VERS_CRANS -p tcp --dport ssh -m state --state NEW\
-m recent --name SSH --set")
def admin_vlan(self) :
""" Reconstruit la chaîne 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)
# iptables("-I ADMIN_VLAN -p tcp -s %s --dport https -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]:
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 chaînes de blackliste (BLACKLIST_{DST,SRC}) """
iptables('-F BLACKLIST_DST')
iptables('-F BLACKLIST_SRC')
iptables('-t mangle -F BLACKLIST_SOFT')
# Peut-être à mettre dans config.py ?
blacklist_sanctions = ('upload', 'warez', 'p2p', 'autodisc_p2p', 'autodisc_upload', 'bloq')
blacklist_sanctions_soft = ('autodisc_virus','ipv6_ra','mail_invalide','virus',
'upload', 'warez', 'p2p', 'autodisc_p2p', 'autodisc_upload', 'bloq','carte_etudiant','chambre_invalide')
blacklist = []
blacklist_soft = []
# Recherche sur le champ ablacklist (clubs compris)
search = db.search('ablacklist=*&paiement=ok')
self.anim = anim("\tBlackliste adhérents+clubs", 2*len(search['adherent']+search['club']))
for entite in search['adherent'] + search['club']:
self.anim.cycle()
sanctions = entite.blacklist_actif()
for s in blacklist_sanctions:
if s in sanctions:
blacklist.extend(entite.machines())
break
for entite in search['adherent'] + search['club']:
self.anim.cycle()
sanctions = entite.blacklist_actif()
for s in blacklist_sanctions_soft:
if s in sanctions:
blacklist_soft.extend(entite.machines())
break
self.anim.reinit()
print OK
# Recherche sur le champ mblacklist
search = db.search('mblacklist=*&paiement=ok')
self.anim = anim("\tBlackliste machines", 2*len(search['machine']))
for entite in search['machine']:
self.anim.cycle()
sanctions = entite.blacklist_actif()
for s in blacklist_sanctions:
if s in sanctions:
blacklist.append(entite)
break
for entite in search['machine']:
self.anim.cycle()
sanctions = entite.blacklist_actif()
for s in blacklist_sanctions_soft:
if s in sanctions:
blacklist_soft.append(entite)
break
self.anim.reinit()
print OK
self.anim = anim("\tChaînes BLACKLIST", len(blacklist))
for machine in blacklist:
self.anim.cycle()
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
self.anim = anim("\tMarquage des machines pour blacklist soft", len(blacklist_soft))
for machine in blacklist_soft:
self.anim.cycle()
iptables("-t mangle -I BLACKLIST_SOFT -s %s -j MARK --set-mark %s" % (machine.ip(), conf_fw.mark['proxy']))
self.anim.reinit()
print OK
def filtre_p2p(self):
""" Construit la chaînes de filtrage du p2p (FILTRE_P2P) """
self.anim = anim("\tFiltrage p2p")
iptables('-F FILTRE_P2P')
iptables('-F TRACKER_FILTER')
# 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()
for filtre in self.filtres_p2p_bloq:
iptables('-A FILTRE_P2P -m ipp2p --%s -j LOG --log-prefix "IPP2P=%s "' % (filtre[0],
filtre[1]))
iptables('-A FILTRE_P2P -m ipp2p -m limit --%s --limit=%d/hour -j RETURN' % (filtre[0], p2p.limite[filtre[1]]/2))
iptables('-A FILTRE_P2P -m ipp2p --%s -j REJECT --reject-with icmp-admin-prohibited' % filtre[0])
self.anim.cycle()
#on rejetes les trackeur udp les plus connus
for tracker in self.udp_torrent_tracker.values():
for dest in tracker:
iptables('-A FILTRE_P2P -p udp -d %s --dport %s -j REJECT --reject-with icmp-admin-prohibited' % (dest[0],dest[1]))
#On log les requetes a des trackers torrents puis on les rejetes
iptables("-A TRACKER_FILTER -m string --algo kmp ! --string \"info_hash=\" -j ACCEPT")
iptables("-A TRACKER_FILTER -m string --algo kmp --string \"/scrape?\" -j LOG_TRACKER")
iptables("-A TRACKER_FILTER -m string --algo kmp ! --string \"peer_id=\" -j ACCEPT")
iptables("-A TRACKER_FILTER -m string --algo kmp ! --string \"port=\" -j ACCEPT")
iptables("-A TRACKER_FILTER -m string --algo kmp ! --string \"uploaded=\" -j ACCEPT")
iptables("-A TRACKER_FILTER -m string --algo kmp ! --string \"downloaded=\" -j ACCEPT")
iptables("-A TRACKER_FILTER -m string --algo kmp ! --string \"left=\" -j ACCEPT")
iptables("-A TRACKER_FILTER -j LOG_TRACKER")
#On analyse que les requetes http
iptables("-A FILTRE_P2P -i %s -p tcp -m string --algo kmp --string \"GET \" -j TRACKER_FILTER" % self.eth_int)
iptables("-A FILTRE_P2P -i %s -p tcp -m string --algo kmp --string \"get \" -j TRACKER_FILTER" % self.eth_int)
# fait bcp de faux positif, peux servir a detecter de nouveau trackers
iptables("-A FILTRE_P2P -i %s -p udp -m string --from 0 --to 65 --algo kmp --hex-string \"|4500002c00004000|\" -j LOG --log-level notice --log-prefix \"TRACKER_TORRENT: \"" % self.eth_int)
iptables('-A LOG_TRACKER -j LOG --log-level notice --log-prefix "TRACKER_TORRENT: "')
iptables('-A LOG_TRACKER -j REJECT --reject-with icmp-admin-prohibited')
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 défaut : DROP)
rien ne passe pas la chaîne FORWARD
INPUT (policy par défaut : ACCEPT)
"""
# interfaces physiques
eth_pub = "crans"
eth_adm = "crans.2"
nfs_ports = ["111", "2049", "32765:32769"]
def serv_out_adm(self) :
self.anim = anim('\tOutput vers VLAN adm', len(adm_users))
# Supression des éventuelles règles
iptables("-t filter -F SERV_OUT_ADM")
for user in 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 fx.adm.crans.org -j ACCEPT")
iptables("-A SERV_OUT_ADM -d daath.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 -P PREROUTING ACCEPT")
iptables("-t nat -A PREROUTING -i lo -j ACCEPT")
iptables("-t nat -P PREROUTING ACCEPT")
print OK
def filter_table(self):
self.anim = anim('\tStructure de la table filter')
iptables('-t filter -N SERV_OUT_ADM')
iptables('-t filter -N TEST_MAC-IP')
iptables("-t filter -A OUTPUT -d 224.0.0.0/4 -j DROP")
# <!> à placer dans filter
#for net in NETs['fil'] + NETs['adm'] + NETs['wifi'] :
# iptables("-t nat -A PREROUTING -s %s -j TEST_MAC-IP" % net)
iptables("-t filter -A OUTPUT -d 10.231.136.1 -j SERV_OUT_ADM") # moche mais visiblement ont avait acces a l'interface adm de zamok depuis zamok
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 filter -P OUTPUT ACCEPT")
print OK
def start_fw_funcs(self) :
self.exception_catcher(self.test_mac_ip)
self.serv_out_adm()
def blacklist(self):
"""Fondamentalement, bloque l'accès internet sur zamok aux
adhérents sanctionnés"""
iptables("-F OUTPUT")
# Règles OUTPUT de nat_table() à remettre en place
iptables("-t filter -A OUTPUT -o lo -j ACCEPT")
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)
self.filter_table()
blacklist_sanctions = ('upload', 'warez', 'p2p', 'autodisc_p2p', 'autodisc_upload', 'bloq')
# Recherche sur le champ ablacklist (clubs compris)
search = db.search('ablacklist=*&paiement=ok')
self.anim = anim("\tBlackliste des comptes Crans", len(search['adherent']))
for adh in search['adherent']:
self.anim.cycle()
sanctions = adh.blacklist_actif()
for s in blacklist_sanctions:
if s in sanctions:
try:
uid = adh.uidNumber()
iptables("-A OUTPUT -m owner --uid-owner %s -d 127.0.0.1/8 -j ACCEPT" % uid)
iptables("-A OUTPUT -m owner --uid-owner %s -d 138.231.136.1/21 -j ACCEPT" % uid)
iptables("-A OUTPUT -m owner --uid-owner %s -d 138.231.144.1/21 -j ACCEPT" % uid)
iptables("-A OUTPUT -m owner --uid-owner %s -j REJECT" % uid)
finally:
break
self.anim.reinit()
print OK
def filter_table_tweaks(self) :
self.anim = anim('\tRègles spécifiques à zamok')
iptables("-P INPUT ACCEPT")
iptables("-P FORWARD DROP")
print OK
"""
Rouge
"""
class firewall_redisdead(firewall_crans) :
"""
Structure du firewall :
table filter :
TEST_MAC-IP pour les paquets en INPUT
FORWARD (policy par défaut : DROP)
rien ne passe pas la chaîne FORWARD
INPUT (policy par défaut : ACCEPT)
"""
# interfaces physiques
eth_pub = "eth0"
eth_adm = "eth1"
def filter_table(self) :
self.anim = anim('\tStructure de la table filter')
iptables('-N TEST_MAC-IP')
for net in NETs['fil'] + NETs['wifi']:
iptables("-A INPUT -s %s -i %s -j TEST_MAC-IP" % (net, self.eth_pub))
for net in NETs['adm']:
iptables("-A INPUT -s %s -i %s -j TEST_MAC-IP" % (net, self.eth_adm))
iptables("-P INPUT ACCEPT")
iptables("-P OUTPUT ACCEPT")
print OK
def filter_table_tweaks(self) :
self.anim = anim('\tRègles spécifiques à redisdead')
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 défaut : DROP)
rien ne passe pas la chaîne FORWARD
INPUT (policy par défaut : ACCEPT)
"""
# interfaces physiques
eth_crans = "crans"
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")
for net in NETs['fil'] + NETs['adm'] + NETs['wifi'] :
iptables("-t nat -A PREROUTING -s %s -j TEST_MAC-IP" % net)
iptables("-t nat -P PREROUTING ACCEPT")
iptables("-t nat -P OUTPUT ACCEPT")
print OK
def filter_table_tweaks(self) :
self.anim = anim('\tRègles spécifiques à vert')
iptables("-P INPUT ACCEPT")
iptables("-P FORWARD DROP")
print OK
class firewall_sable(firewall_redisdead):
"""Comme pour rouge, avec le proxy transparent en plus"""
def filter_table_tweaks(self) :
iptables("-P INPUT ACCEPT")
iptables("-P FORWARD DROP")
for ip in accueil_route.keys():
for port in accueil_route[ip]:
iptables("-A FORWARD -p tcp -d %s --dport %s -j ACCEPT" % (ip,port))
iptables("-A FORWARD -p tcp -s %s --sport %s -j ACCEPT" % (ip,port))
def mangle_table(self):
iptables("-t mangle -F PREROUTING")
# Pour le transparent
iptables("-t mangle -i eth0.2 -A PREROUTING -p tcp --destination-port 3128 " +
"--destination 10.231.136.9 " +
"-m mac --mac-source %s " % mac_komaz +
"-j MARK --set-mark %s" % conf_fw.mark['proxy'])
iptables("-t mangle -A PREROUTING -m mark --mark %s -j ACCEPT" % conf_fw.mark['proxy'])
def nat_table(self):
firewall_redisdead.nat_table(self)
# Proxy transparent pour le filiaire
iptables("-t nat -A PREROUTING -i eth0.2 -m mark --mark %s -j ACCEPT" % conf_fw.mark['proxy'])
if_defaut = "eth0"
if_radin = "eth0.%d" % vlans["radin"]
if_accueil = "eth0.%d" % vlans["accueil"]
if_isolement = "eth0.%d" % vlans["isolement"]
#intranet et wiki pour le vlan accueil
for ip in accueil_route.keys():
for port in accueil_route[ip]:
iptables("-t nat -A PREROUTING -i eth0.7 -p tcp -d %s --dport %s -j ACCEPT" % (ip,port))
iptables("-t nat -A POSTROUTING -p tcp -s %s -d %s --dport %s -j MASQUERADE" % (NETs['accueil'][0],ip,port))
# Proxy transparent pour le wifi
iptables("-t nat -A PREROUTING -i %s -p tcp --dport 80" %if_defaut +
" -d ! 138.231.136.0/24 -j DNAT --to-destination" +
" 138.231.136.9:3128")
# Proxy transparent pour les vlans radin et accueil
for interface in [if_accueil, if_isolement]:
iptables("-t nat -i %s -A PREROUTING -p tcp --destination-port 80 -j DNAT --to-destination 10.51.0.1:3128" % interface)
iptables("-t nat -i %s -A PREROUTING -p tcp --destination-port 3128 -j ACCEPT" % interface)
iptables("-t nat -i %s -A PREROUTING -p tcp --destination-port 443 -j ACCEPT" % interface)
def post_start_hook(self) :
self.anim = anim("\tMise en place du routage")
warn = ''
for cmd in [ 'echo 1 > /proc/sys/net/ipv4/ip_forward' ,
'echo 65536 > /proc/sys/net/ipv4/netfilter/ip_conntrack_max' ] :
status,output=getstatusoutput(cmd)
if status :
warn += output + '\n'
if warn :
print WARNING
if self.debug :
print warn
else :
print OK
firewall_bleu = firewall_zamok
"""
Gordon
"""
class firewall_gordon(firewall_crans) :
"""
Structure du firewall :
table nat :
table filter :
MAC-IP
FORWARD (policy par défaut : ACCEPT)
INPUT (policy par défaut : ACCEPT)
table mangle :
POSTROUTING : le proxy transparent
"""
# interfaces physiques
eth_crans = "eth0"
eth_wifi = "eth0.3"
def filter_table(self) :
self.anim = anim('\tStructure de la table filter')
iptables('-t filter -N TEST_MAC-IP')
iptables("-t filter -P FORWARD ACCEPT")
iptables("-t filter -A FORWARD -i lo -j ACCEPT")
iptables("-t filter -A FORWARD -d 224.0.0.0/4 -j DROP")
for net in NETs['fil'] + NETs['adm'] + NETs['wifi'] :
iptables("-t filter -A FORWARD -s %s -j TEST_MAC-IP" % net)
iptables("-t filter -P FORWARD ACCEPT")
iptables("-t filter -P OUTPUT ACCEPT")
print OK
def filter_table_tweaks(self) :
self.anim = anim('\tRègles spécifiques à gordon')
iptables("-P INPUT ACCEPT")
iptables("-P FORWARD ACCEPT")
print OK
def mangle_table(self):
self.anim = anim('\tRègles spécifiques à gordon')
#~ iptables("-t mangle -A PREROUTING " +
#~ "-d ! 138.231.136.0/21 " +
#~ ("-i %s " % self.eth_wifi) +
#~ "-p tcp -m tcp --dport 80 " +
#~ "-j MARK --set-xmark %s/0xffffffff" % conf_fw.mark['proxy'])
print OK
def post_start_hook(self) :
self.anim = anim("\tMise en place du routage")
warn = ''
for cmd in [ 'echo 1 > /proc/sys/net/ipv4/ip_forward' ,
'echo 65536 > /proc/sys/net/ipv4/netfilter/ip_conntrack_max' ] :
status,output=getstatusoutput(cmd)
if status :
warn += output + '\n'
if warn :
print WARNING
if self.debug :
print warn
else :
print OK
if __name__ == '__main__' :
# Chaînes 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', 'mangle_table', 'classes_p2p_maj','reload_qos' ] :
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 chaînes> : reconstruit les chaînes
Les chaînes 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 )