[firewall4] Séparation en plusieurs fichiers
En gros, un par pare feu
This commit is contained in:
parent
964abdd565
commit
2d2cbf2d9f
8 changed files with 1335 additions and 1268 deletions
File diff suppressed because it is too large
Load diff
4
gestion/gen_confs/firewall4/__init__.py
Normal file
4
gestion/gen_confs/firewall4/__init__.py
Normal file
|
@ -0,0 +1,4 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from firewall4 import *
|
233
gestion/gen_confs/firewall4/base.py
Normal file
233
gestion/gen_confs/firewall4/base.py
Normal file
|
@ -0,0 +1,233 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
import os
|
||||
import sys
|
||||
import socket
|
||||
import netaddr
|
||||
|
||||
import utils
|
||||
from utils import pretty_print, anim, OK, cprint
|
||||
|
||||
from gestion import config
|
||||
from gestion.gen_confs.ipset import IpsetError, Ipset
|
||||
|
||||
import config.firewall
|
||||
|
||||
#: Nom de la machine exécutant le script
|
||||
hostname = socket.gethostname()
|
||||
hostname='routeur'
|
||||
#: Association des interfaces de ``hostname``
|
||||
dev = hostname in config.firewall.dev.keys() and config.firewall.dev[hostname] or {}
|
||||
|
||||
|
||||
class firewall(utils.firewall_tools) :
|
||||
"""Classe de base du pare-feu implémentant l'association mac-ip (pour les machines filaires) et les blacklists hard"""
|
||||
|
||||
def __init__(self):
|
||||
super(firewall, self).__init__()
|
||||
|
||||
self.reloadable = {
|
||||
'blacklist_hard' : self.blacklist_hard,
|
||||
'test_mac_ip' : self.test_mac_ip,
|
||||
}
|
||||
|
||||
self.use_ipset = [self.blacklist_hard, self.test_mac_ip]
|
||||
|
||||
self.ipset['mac_ip']={
|
||||
'adh' : Ipset("MAC-IP-ADH","macipmap","--from 138.231.136.0 --to 138.231.151.255"),
|
||||
'adm' : Ipset("MAC-IP-ADM","macipmap","--from 10.231.136.0 --to 10.231.136.255"),
|
||||
'app' : Ipset("MAC-IP-APP","macipmap","--from 10.2.9.0 --to 10.2.9.255"),
|
||||
}
|
||||
|
||||
self.ipset['blacklist']={
|
||||
'hard' : Ipset("BLACKLIST-HARD","ipmap","--from 138.231.136.0 --to 138.231.151.255"),
|
||||
}
|
||||
|
||||
|
||||
def blacklist_maj(self, ips):
|
||||
"""Met à jours les blacklists pour les ip présentent dans la liste ``ips``"""
|
||||
self.blacklist_hard_maj(ips)
|
||||
|
||||
def raw_table(self):
|
||||
"""Génère les règles pour la table ``raw`` et remplis les chaines de la table"""
|
||||
table = 'raw'
|
||||
return
|
||||
|
||||
def mangle_table(self):
|
||||
"""Génère les règles pour la table ``mangle`` et remplis les chaines de la table"""
|
||||
table = 'mangle'
|
||||
return
|
||||
|
||||
def filter_table(self):
|
||||
"""Génère les règles pour la table ``filter`` et remplis les chaines de la table"""
|
||||
table = 'filter'
|
||||
|
||||
mac_ip_chain = self.test_mac_ip(table, fill_ipset=True)
|
||||
blacklist_hard_chain = self.blacklist_hard(table, fill_ipset=True)
|
||||
|
||||
chain = 'INPUT'
|
||||
self.add(table, chain, '-i lo -j ACCEPT')
|
||||
self.add(table, chain, '-p icmp -j ACCEPT')
|
||||
self.add(table, chain, '-m state --state RELATED,ESTABLISHED -j ACCEPT')
|
||||
for net in config.NETs['all'] + config.NETs['adm'] + config.NETs['personnel-ens']:
|
||||
self.add(table, chain, '-s %s -j %s' % (net, mac_ip_chain))
|
||||
self.add(table, chain, '-j %s' % blacklist_hard_chain)
|
||||
|
||||
chain = 'FORWARD'
|
||||
self.add(table, chain, '-j REJECT')
|
||||
|
||||
return
|
||||
|
||||
def nat_table(self):
|
||||
"""Génère les règles pour la table ``nat`` et remplis les chaines de la table"""
|
||||
table = 'nat'
|
||||
return
|
||||
|
||||
|
||||
def blacklist_hard_maj(self, ip_list):
|
||||
"""Met à jour les blacklists hard, est appelée par :py:func:`blacklist_maj`"""
|
||||
self.blacklist_hard(fill_ipset=True)
|
||||
# for ip in ip_list:
|
||||
# machine = self.conn.search(u"ipHostNumber=%s" % ip)
|
||||
# # Est-ce qu'il y a des blacklists hard parmis les blacklists de la machine
|
||||
# if machine and set([bl.value['type'] for bl in machine[0].blacklist_actif() ]).intersection(config.blacklist_sanctions):
|
||||
# try: self.ipset['blacklist']['hard'].add(ip)
|
||||
# except IpsetError: pass
|
||||
# else:
|
||||
# try: self.ipset['blacklist']['hard'].delete(ip)
|
||||
# except IpsetError: pass
|
||||
|
||||
def blacklist_hard(self, table=None, fill_ipset=False, apply=False):
|
||||
"""Génère la chaine ``BLACKLIST_HARD``.
|
||||
Si ``fill_ipset`` est à ``True``, remplis l'ipset ``BLACKLIST-HARD``.
|
||||
Si ``apply`` est à True, applique directement les règles"""
|
||||
chain = 'BLACKLIST_HARD'
|
||||
|
||||
if fill_ipset:
|
||||
anim('\tRestoration de l\'ipset %s' % self.ipset['blacklist']['hard'])
|
||||
# On récupère la liste de toutes les ips blacklistés hard
|
||||
bl_hard_ips = set(
|
||||
str(ip) for ips in
|
||||
[
|
||||
machine['ipHostNumber'] for machine in self.blacklisted_machines() if machine['ipHostNumber'] and reduce(lambda x,y: x or y, ( ip.value in netaddr.IPNetwork(n) for n in config.NETs['all'] for ip in machine['ipHostNumber']))
|
||||
if set([bl.value['type'] for bl in machine.blacklist_actif() ]).intersection(config.blacklist_sanctions)
|
||||
]
|
||||
for ip in ips
|
||||
)
|
||||
|
||||
self.ipset['blacklist']['hard'].restore(bl_hard_ips)
|
||||
print OK
|
||||
|
||||
if table == 'filter':
|
||||
pretty_print(table, chain)
|
||||
self.add(table, chain, '-m set --match-set %s src -j REJECT' % self.ipset['blacklist']['hard'] )
|
||||
self.add(table, chain, '-m set --match-set %s dst -j REJECT' % self.ipset['blacklist']['hard'] )
|
||||
print OK
|
||||
|
||||
if apply:
|
||||
self.apply(table, chain)
|
||||
return chain
|
||||
|
||||
def test_mac_ip_dispatch(self, func, machine):
|
||||
"""Détermine à quel set de mac-ip appliquer la fonction ``func`` (add, delete, append, ...)"""
|
||||
ips = machine['ipHostNumber']
|
||||
for ip in ips:
|
||||
# Si la machines est sur le réseau des adhérents
|
||||
if utils.AddrInNet(str(ip), config.NETs['wifi']):
|
||||
# Les machines wifi sont vues à travers komaz
|
||||
func('adh', "%s,%s" % (ip, config.mac_komaz))
|
||||
elif utils.AddrInNet(str(ip), config.NETs['fil']):
|
||||
func('adh', "%s,%s" % (ip, machine['macAddress'][0]))
|
||||
# Si la machine est sur le réseau admin
|
||||
elif utils.AddrInNet(str(ip), config.NETs['adm']):
|
||||
func('adm', "%s,%s" % (ip, machine['macAddress'][0]))
|
||||
|
||||
def test_mac_ip(self, table=None, fill_ipset=False, apply=False):
|
||||
"""Génère la chaine ``TEST_MAC-IP``.
|
||||
Si ``fill_ipset`` est à ``True``, remplis les ipsets ``MAC-IP-ADH``, ``MAC-IP-ADM``, ``MAC-IP-ADM``.
|
||||
Si ``apply`` est à True, applique directement les règles"""
|
||||
chain = 'TEST_MAC-IP'
|
||||
|
||||
if fill_ipset:
|
||||
anim('\tRestoration des ipsets %s' % ', '.join(self.ipset['mac_ip'].keys()))
|
||||
rules={
|
||||
'adh':[],
|
||||
'adm':[],
|
||||
'app':[],
|
||||
}
|
||||
for machine in self.machines():
|
||||
self.test_mac_ip_dispatch(lambda set, data: rules[set].append(data), machine)
|
||||
|
||||
for set,rules in rules.items():
|
||||
self.ipset['mac_ip'][set].restore(rules)
|
||||
print OK
|
||||
|
||||
if table == 'filter':
|
||||
pretty_print(table, chain)
|
||||
|
||||
for key in ['accueil', 'isolement', ]:
|
||||
for net in config.NETs[key]:
|
||||
self.add(table, chain, '-s %s -j RETURN' % net)
|
||||
for key in self.ipset['mac_ip'].keys():
|
||||
self.add(table, chain, '-m set --match-set %s src,src -j RETURN' % self.ipset['mac_ip'][key])
|
||||
|
||||
# Proxy ARP de Komaz et Titanic pour OVH
|
||||
ip_ovh = self.conn.search(u"host=ovh.adm.crans.org")[0]['ipHostNumber'][0]
|
||||
self.add(table, chain, '-m mac -s %s --mac-source %s -j RETURN' % (ip_ovh, config.mac_komaz))
|
||||
self.add(table, chain, '-m mac -s %s --mac-source %s -j RETURN' % (ip_ovh, config.mac_titanic))
|
||||
|
||||
self.add(table, chain, '-j REJECT')
|
||||
print OK
|
||||
|
||||
if apply:
|
||||
self.apply(table, chain)
|
||||
return chain
|
||||
|
||||
def mac_ip_maj(self, ip_list):
|
||||
"""Met à jour la correspondance mac-ip"""
|
||||
anim('\tActualisation de la correspondance mac-ipv4')
|
||||
for ip in ip_list:
|
||||
machine = self.conn.search(u"ipHostNumber=%s" % ip)
|
||||
if machine:
|
||||
try: self.test_mac_ip_dispatch(lambda set, data: self.ipset['mac_ip'][set].delete(data.split(',',1)[0]), {'ipHostNumber' : [ip], 'macAddress':[''] })
|
||||
except IpsetError: pass
|
||||
self.test_mac_ip_dispatch(lambda set, data: self.ipset['mac_ip'][set].add(data), machine[0])
|
||||
else:
|
||||
try: self.test_mac_ip_dispatch(lambda set, data: self.ipset['mac_ip'][set].delete(data.split(',',1)[0]), {'ipHostNumber' : [ip], 'macAddress':[''] })
|
||||
except IpsetError: pass
|
||||
print OK
|
||||
|
||||
|
||||
|
||||
class firewall_routeur(firewall):
|
||||
"""Associe mac-ip pour les machines voyant plusieurs réseaux (wifi, filaire, personnel, ...)"""
|
||||
def test_mac_ip_dispatch(self, func, machine):
|
||||
"""Détermine à quel set de mac-ip appliquer la fonction func (add, delete, append, ...)"""
|
||||
ips = machine['ipHostNumber']
|
||||
for ip in ips:
|
||||
# Si la machines est sur le réseau des adhérents
|
||||
if utils.AddrInNet(str(ip), config.NETs['wifi']):
|
||||
func('adh', "%s,%s" % (ip, machine['macAddress'][0]))
|
||||
elif utils.AddrInNet(str(ip), config.NETs['fil']):
|
||||
func('adh', "%s,%s" % (ip, machine['macAddress'][0]))
|
||||
# Si la machine est sur le réseau admin
|
||||
elif utils.AddrInNet(str(ip), config.NETs['adm']):
|
||||
func('adm', "%s,%s" % (ip, machine['macAddress'][0]))
|
||||
# Si la machine est sur le réseaux des appartements de l'ENS
|
||||
elif utils.AddrInNet(str(ip), config.NETs['personnel-ens']):
|
||||
func('app', "%s,%s" % (ip, machine['macAddress'][0]))
|
||||
|
||||
class firewall_wifionly(firewall):
|
||||
"""Associe mac-ip pour les machines wifi only : les machines filaires sont vues avec la mac de komaz"""
|
||||
def test_mac_ip_dispatch(self, func, machine):
|
||||
"""Détermine à quel set de mac-ip appliquer la fonction func (add, delete, append, ...)"""
|
||||
ips = machine['ipHostNumber']
|
||||
for ip in ips:
|
||||
# Si la machines est sur le réseau des adhérents
|
||||
if utils.AddrInNet(str(ip), config.NETs['wifi']):
|
||||
func('adh', "%s,%s" % (ip, machine['macAddress'][0]))
|
||||
elif utils.AddrInNet(str(ip), config.NETs['fil']):
|
||||
func('adh', "%s,%s" % (ip, config.mac_komaz))
|
||||
# Si la machine est sur le réseau admin
|
||||
elif utils.AddrInNet(str(ip), config.NETs['adm']):
|
||||
func('adm', "%s,%s" % (ip, machine['macAddress'][0]))
|
75
gestion/gen_confs/firewall4/firewall4.py
Executable file
75
gestion/gen_confs/firewall4/firewall4.py
Executable file
|
@ -0,0 +1,75 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import sys
|
||||
import socket
|
||||
|
||||
import utils
|
||||
import base
|
||||
import komaz
|
||||
import zamok
|
||||
import routeur
|
||||
|
||||
#: Nom de la machine exécutant le script
|
||||
hostname = socket.gethostname()
|
||||
|
||||
|
||||
|
||||
#: Associe à un ``hostname`` la classe du pare-feu correspondant
|
||||
firewall = {
|
||||
'komaz' : komaz.firewall,
|
||||
'zamok' : zamok.firewall,
|
||||
'routeur' : routeur.firewall,
|
||||
'gordon' : base.firewall_routeur,
|
||||
'eap' : base.firewall_wifionly,
|
||||
}
|
||||
|
||||
if hostname in firewall.keys():
|
||||
#: Classe du pare-feu pour la machine ``hostname`` ou :py:class:`firewall_base` si non trouvé
|
||||
firewall = firewall[hostname]
|
||||
else:
|
||||
#: Classe du pare-feu pour la machine ``hostname`` ou :py:class:`firewall_base` si non trouvé
|
||||
firewall = base.firewall
|
||||
|
||||
if __name__ == '__main__' :
|
||||
|
||||
fw = firewall()
|
||||
|
||||
# Chaînes pouvant être recontruites
|
||||
chaines = fw.reloadable.keys()
|
||||
|
||||
def __usage(txt=None) :
|
||||
if txt!=None : cprint(txt,'gras')
|
||||
|
||||
chaines.sort()
|
||||
|
||||
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:] :
|
||||
if arg == 'stop':
|
||||
fw.stop()
|
||||
elif arg == 'start':
|
||||
fw.start()
|
||||
elif arg == 'restart':
|
||||
fw.restart()
|
||||
else:
|
||||
fw.reload(arg)
|
555
gestion/gen_confs/firewall4/komaz.py
Normal file
555
gestion/gen_confs/firewall4/komaz.py
Normal file
|
@ -0,0 +1,555 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
import utils
|
||||
import base
|
||||
|
||||
from utils import pretty_print, OK, anim
|
||||
from base import dev
|
||||
|
||||
class firewall(base.firewall_routeur):
|
||||
|
||||
def __init__(self):
|
||||
super(self.__class__, self).__init__()
|
||||
|
||||
self.reloadable.update({
|
||||
'log_all' : self.log_all,
|
||||
'admin_vlan' : self.admin_vlan,
|
||||
'clamp_mss' : self.clamp_mss,
|
||||
'ingress_filtering' : self.ingress_filtering,
|
||||
'ssh_on_https' : self.ssh_on_https,
|
||||
'connexion_secours' : self.connexion_secours,
|
||||
'connexion_appartement' : self.connexion_appartement,
|
||||
'blacklist_soft' : self.blacklist_soft,
|
||||
'blacklist_upload' : self.blacklist_upload,
|
||||
'reseaux_non_routable' : self.reseaux_non_routable,
|
||||
'filtrage_ports' : self.filtrage_ports,
|
||||
'limitation_debit' : self.limitation_debit,
|
||||
'limit_ssh_connexion' : self.limit_ssh_connexion,
|
||||
'tunnel_6in4' : self.tunnel_6in4,
|
||||
})
|
||||
|
||||
self.use_ipset.extend([self.blacklist_soft, self.blacklist_upload, self.reseaux_non_routable])
|
||||
self.use_tc.extend([self.limitation_debit])
|
||||
|
||||
self.ipset['reseaux_non_routable'] = {
|
||||
'deny' : base.Ipset("RESEAUX-NON-ROUTABLE-DENY","nethash"),
|
||||
'allow' : base.Ipset("RESEAUX-NON-ROUTABLE-ALLOW","nethash"),
|
||||
}
|
||||
|
||||
self.ipset['blacklist'].update({
|
||||
'soft' : base.Ipset("BLACKLIST-SOFT","ipmap","--from 138.231.136.0 --to 138.231.151.255"),
|
||||
'upload' : base.Ipset("BLACKLIST-UPLOAD","ipmap","--from 138.231.136.0 --to 138.231.151.255"),
|
||||
})
|
||||
|
||||
def blacklist_maj(self, ips):
|
||||
self.blacklist_hard_maj(ips)
|
||||
self.blacklist_soft_maj(ips)
|
||||
self.blacklist_upload_maj(ips)
|
||||
|
||||
def raw_table(self):
|
||||
"""Génère les règles pour la table ``raw`` et remplis les chaines de la table"""
|
||||
table = 'raw'
|
||||
|
||||
chain = 'PREROUTING'
|
||||
self.add(table, chain, '-d 225.0.0.50 -j DROP')
|
||||
return
|
||||
|
||||
|
||||
def mangle_table(self):
|
||||
table = 'mangle'
|
||||
super(self.__class__, self).mangle_table()
|
||||
|
||||
chain = 'PREROUTING'
|
||||
self.add(table, chain, '-j %s' % self.log_all(table))
|
||||
self.add(table, chain, '-j %s' % self.connexion_secours(table))
|
||||
self.add(table, chain, '-p tcp -j CONNMARK --restore-mark')
|
||||
|
||||
chain = 'POSTROUTING'
|
||||
self.add(table, chain, '-j %s' % self.clamp_mss(table))
|
||||
#self.add(table,chain, '-j %s' % self.limitation_debit(table, run_tc=True))
|
||||
self.add(table, chain, '-j %s' % self.blacklist_upload(table, fill_ipset=True))
|
||||
return
|
||||
|
||||
def filter_table(self):
|
||||
table = 'filter'
|
||||
super(self.__class__, self).filter_table()
|
||||
|
||||
mac_ip_chain = self.test_mac_ip()
|
||||
blacklist_hard_chain = self.blacklist_hard()
|
||||
blacklist_soft_chain = self.blacklist_soft(table)
|
||||
|
||||
chain = 'INPUT'
|
||||
self.flush(table, chain)
|
||||
self.add(table, chain, '-i lo -j ACCEPT')
|
||||
self.add(table, chain, '-p icmp -j ACCEPT')
|
||||
self.add(table, chain, '-m state --state RELATED,ESTABLISHED -j ACCEPT')
|
||||
self.add(table, chain, '-j %s' % blacklist_soft_chain)
|
||||
for net in base.config.NETs['all'] + base.config.NETs['adm'] + base.config.NETs['personnel-ens']:
|
||||
self.add(table, chain, '-s %s -j %s' % (net, mac_ip_chain))
|
||||
self.add(table, chain, '-j %s' % blacklist_hard_chain)
|
||||
|
||||
chain = 'FORWARD'
|
||||
self.flush(table, chain)
|
||||
self.add(table, chain, '-i lo -j ACCEPT')
|
||||
self.add(table, chain, '-p icmp -j ACCEPT')
|
||||
self.add(table, chain, '-j %s' % self.admin_vlan(table))
|
||||
self.add(table, chain, '-j %s' % blacklist_soft_chain)
|
||||
self.add(table, chain, '-i %s -j %s' % (dev['out'], blacklist_hard_chain))
|
||||
self.add(table, chain, '-o %s -j %s' % (dev['out'], blacklist_hard_chain))
|
||||
self.add(table, chain, '-m state --state RELATED,ESTABLISHED -j ACCEPT')
|
||||
self.add(table, chain, '-j %s' % self.tunnel_6in4(table))
|
||||
self.add(table, chain, '-j %s' % self.reseaux_non_routable(table, fill_ipset=True))
|
||||
for net in base.config.NETs['all'] + base.config.NETs['adm'] + base.config.NETs['personnel-ens']:
|
||||
self.add(table, chain, '-s %s -j %s' % (net, mac_ip_chain))
|
||||
self.add(table, chain, '-j %s' % self.connexion_secours(table))
|
||||
self.add(table, chain, '-j %s' % self.connexion_appartement(table))
|
||||
self.add(table, chain, '-j %s' % self.ingress_filtering(table))
|
||||
self.add(table, chain, '-j %s' % self.limit_ssh_connexion(table))
|
||||
self.add(table, chain, '-i %s -j %s' % (dev['out'], self.filtrage_ports(table)))
|
||||
self.add(table, chain, '-o %s -j %s' % (dev['out'], self.filtrage_ports(table)))
|
||||
return
|
||||
|
||||
def nat_table(self):
|
||||
table = 'nat'
|
||||
super(self.__class__, self).nat_table()
|
||||
|
||||
chain = 'PREROUTING'
|
||||
self.add(table, chain, '-j %s' % self.ssh_on_https(table))
|
||||
self.add(table, chain, '-j %s' % self.connexion_secours(table))
|
||||
self.add(table, chain, '-j %s' % self.blacklist_soft(table))
|
||||
|
||||
chain = 'POSTROUTING'
|
||||
self.add(table, chain, '-j %s' % self.connexion_appartement(table))
|
||||
return
|
||||
|
||||
def tunnel_6in4(self, table=None, apply=False):
|
||||
chain = 'TUNNEL_IPV6'
|
||||
|
||||
tunnels_ipv6 = [ ('216.66.84.42', '138.231.136.12'), ('216.66.84.42','138.231.136.164') ]
|
||||
|
||||
if table == 'filter':
|
||||
pretty_print(table, chain)
|
||||
for sideA, sideB in tunnels_ipv6:
|
||||
self.add(table, chain, '--proto 41 -s %s -d %s -j ACCEPT' % (sideA, sideB))
|
||||
self.add(table, chain, '--proto 41 -s %s -d %s -j ACCEPT' % (sideB, sideA))
|
||||
print OK
|
||||
|
||||
if apply:
|
||||
self.apply(table, chain)
|
||||
return chain
|
||||
|
||||
def limit_ssh_connexion(self, table=None, apply=False):
|
||||
chain = 'LIMIT-SSH-CONNEXION'
|
||||
|
||||
if table == 'filter':
|
||||
pretty_print(table, chain)
|
||||
self.add(table, chain, '-i %s -p tcp --dport ssh -m state --state NEW -m recent --name SSH --set' % dev['out'])
|
||||
self.add(table, chain, '-i %s -p tcp --dport ssh -m state --state NEW -m recent --name SSH --update --seconds 30 --hitcount 10 --rttl -j DROP' % dev['out'])
|
||||
print OK
|
||||
|
||||
if apply:
|
||||
self.apply(table, chain)
|
||||
return chain
|
||||
|
||||
def test_mac_ip(self, table=None, fill_ipset=False, apply=False):
|
||||
chain = super(self.__class__, self).test_mac_ip()
|
||||
|
||||
if table == 'filter':
|
||||
for key in ['out', 'tun-ovh' ]:
|
||||
self.add(table, chain, '-i %s -j RETURN' % dev[key])
|
||||
|
||||
return super(self.__class__, self).test_mac_ip(table, fill_ipset, apply)
|
||||
|
||||
|
||||
def log_all(self, table=None, apply=False):
|
||||
chain = 'LOG_ALL'
|
||||
|
||||
if table == 'mangle':
|
||||
pretty_print(table, chain)
|
||||
for device in dev.values():
|
||||
self.add(table, chain, '-i %s -m state --state NEW -j LOG --log-prefix "LOG_ALL "' % device)
|
||||
print OK
|
||||
|
||||
if apply:
|
||||
self.apply(table, chain)
|
||||
return chain
|
||||
|
||||
def admin_vlan(self, table=None, apply=False):
|
||||
chain = 'VLAN-ADM'
|
||||
|
||||
if table == 'filter':
|
||||
pretty_print(table, chain)
|
||||
for net in base.config.NETs['adm']:
|
||||
self.add(table, chain, '-o %s -s %s -j ACCEPT' % (dev['tun-ovh'], net))
|
||||
self.add(table, chain, '-i %s -d %s -j ACCEPT' % (dev['tun-ovh'], net))
|
||||
self.add(table, chain, '-d %s -j REJECT' % net)
|
||||
print OK
|
||||
|
||||
if apply:
|
||||
self.apply(table, chain)
|
||||
return chain
|
||||
|
||||
def qos(self, table=None, apply=False):
|
||||
return
|
||||
|
||||
def clamp_mss(self, table=None, apply=False):
|
||||
"""Force la MSS (Max Segment Size) TCP à rentrer dans la MTU (Max Transfert Unit)"""
|
||||
chain = 'CLAMP-MSS'
|
||||
if table == 'mangle':
|
||||
pretty_print(table, chain)
|
||||
|
||||
self.add(table, chain, '-p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu')
|
||||
print OK
|
||||
|
||||
if apply:
|
||||
self.apply(table, chain)
|
||||
return chain
|
||||
|
||||
def ingress_filtering(self, table=None, apply=False):
|
||||
"""Pour ne pas router les paquêtes n'appartenant pas à notre plage ip voulant sortir de notre réseau
|
||||
et empêcher certain type de spoof (cf http://travaux.ovh.net/?do=details&id=5183)"""
|
||||
chain = 'INGRESS_FILTERING'
|
||||
if table == 'filter':
|
||||
pretty_print(table, chain)
|
||||
|
||||
for net in base.config.NETs['all']:
|
||||
self.add(table, chain, '-o %s -s %s -j RETURN' % (dev['out'], net))
|
||||
self.add(table, chain, '-o %s -j LOG --log-prefix BAD_ROUTE' % dev['out'])
|
||||
self.add(table, chain, '-o %s -j DROP' % dev['out'])
|
||||
for net_d in base.config.NETs['all']:
|
||||
for net_s in base.config.NETs['all']:
|
||||
self.add(table, chain,'-i %s ! -s %s -d %s -j RETURN' % (dev['out'], net_s, net_d))
|
||||
self.add(table, chain,'-i %s -j LOG --log-prefix BAD_SRC' % dev['out'])
|
||||
self.add(table, chain,'-i %s -j DROP' % dev['out'])
|
||||
print OK
|
||||
|
||||
if apply:
|
||||
self.apply(table, chain)
|
||||
return chain
|
||||
|
||||
def ssh_on_https(self, table=None, apply=False):
|
||||
"""Pour faire fonctionner ssh2.crans.org"""
|
||||
chain = 'SSH2'
|
||||
|
||||
if table == 'nat':
|
||||
pretty_print(table, chain)
|
||||
self.add(table, chain, '-p tcp -d 138.231.136.2 --dport 22 -j DNAT --to-destination 138.231.136.1:22') # redirection du ssh vers zamok
|
||||
self.add(table, chain, '-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)
|
||||
print OK
|
||||
|
||||
if apply:
|
||||
self.apply(table, chain)
|
||||
return chain
|
||||
|
||||
def connexion_secours(self, table=None, apply=False):
|
||||
"""Redirige les paquets vers un proxy lorsqu'on est en connexion de secours"""
|
||||
chain = 'CONNEXION-SECOURS'
|
||||
|
||||
if table == 'mangle':
|
||||
pretty_print(table, chain)
|
||||
self.add(table, chain, '-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' % (base.config.firewall.mark['secours']))
|
||||
self.add(table, chain, '-m mark --mark %s -j ACCEPT' % base.config.firewall.mark['secours'])
|
||||
print OK
|
||||
|
||||
if table == 'nat':
|
||||
pretty_print(table, chain)
|
||||
self.add(table, chain, '-p tcp -m mark --mark %s -j DNAT --to-destination 10.231.136.4:3129' % base.config.firewall.mark['secours'] )
|
||||
print OK
|
||||
|
||||
if table == 'filter':
|
||||
pretty_print(table, chain)
|
||||
self.add(table, chain, '-p tcp -s 138.231.136.0/16 ! -d 138.231.136.0/16 --destination-port 443 -m condition --condition secours -j REJECT')
|
||||
self.add(table, chain, '-m mark --mark %s -j ACCEPT' % base.config.firewall.mark['secours'])
|
||||
print OK
|
||||
|
||||
if apply:
|
||||
self.apply(table, chain)
|
||||
return chain
|
||||
|
||||
def connexion_appartement(self, table=None, apply=False):
|
||||
"""PNAT les appartements derrière appartement.crans.org"""
|
||||
chain = 'CONNEXION-APPARTEMENT'
|
||||
|
||||
if table == 'nat':
|
||||
pretty_print(table, chain)
|
||||
for dev_key in ['out', 'fil', 'wifi']:
|
||||
for net in base.config.NETs['personnel-ens']:
|
||||
self.add(table, chain, '-o %s -s %s -j SNAT --to 138.231.136.44' % (dev[dev_key], net))
|
||||
print OK
|
||||
|
||||
if table == 'filter':
|
||||
pretty_print(table, chain)
|
||||
for net in base.config.NETs['personnel-ens']:
|
||||
self.add(table, chain, '-s %s -j ACCEPT' % net)
|
||||
self.add(table, chain, '-d %s -j ACCEPT' % net)
|
||||
print OK
|
||||
|
||||
if apply:
|
||||
self.apply(table, chain)
|
||||
return chain
|
||||
|
||||
def blacklist_soft_maj(self, ip_list):
|
||||
self.blacklist_soft(fill_ipset=True)
|
||||
# for ip in ip_list:
|
||||
# machine = self.conn.search(u"ipHostNumber=%s" % ip)
|
||||
# # Est-ce qu'il y a des blacklists soft parmis les blacklists de la machine
|
||||
# if machine and set([bl.value['type'] for bl in machine[0].blacklist_actif() ]).intersection(base.config.blacklist_sanctions_soft):
|
||||
# try: self.ipset['blacklist']['soft'].add(ip)
|
||||
# except IpsetError: pass
|
||||
# else:
|
||||
# try: self.ipset['blacklist']['soft'].delete(ip)
|
||||
# except IpsetError: pass
|
||||
|
||||
def blacklist_soft(self, table=None, fill_ipset=False, apply=False):
|
||||
"""Redirige les gens blacklisté vers le portail captif"""
|
||||
chain = 'BLACKLIST_SOFT'
|
||||
|
||||
if fill_ipset:
|
||||
anim('\tRestoration de l\'ipset %s' % self.ipset['blacklist']['soft'])
|
||||
# On récupère la liste de toutes les ips blacklistés soft
|
||||
bl_soft_ips = set(
|
||||
str(ip) for ips in
|
||||
[
|
||||
machine['ipHostNumber'] for machine in self.blacklisted_machines() if machine['ipHostNumber'] and reduce(lambda x,y: x or y, ( ip.value in base.netaddr.IPNetwork(n) for n in base.config.NETs['all'] for ip in machine['ipHostNumber']))
|
||||
if set([bl.value['type'] for bl in machine.blacklist_actif() ]).intersection(base.config.blacklist_sanctions_soft)
|
||||
]
|
||||
for ip in ips
|
||||
)
|
||||
|
||||
self.ipset['blacklist']['soft'].restore(bl_soft_ips)
|
||||
print OK
|
||||
|
||||
if table == 'filter':
|
||||
pretty_print(table, chain)
|
||||
self.add(table, chain, '-p tcp --dport 80 -m set --match-set %s src -j ACCEPT' % self.ipset['blacklist']['soft'] )
|
||||
self.add(table, chain, '-p tcp --sport 80 -m set --match-set %s dst -j ACCEPT' % self.ipset['blacklist']['soft'] )
|
||||
self.add(table, chain, '-p tcp -d 10.231.136.4 --dport 3128 -m set --match-set %s src -j ACCEPT' % self.ipset['blacklist']['soft'] )
|
||||
self.add(table, chain, '-p tcp -s 10.231.136.4 --sport 3128 -m set --match-set %s dst -j ACCEPT' % self.ipset['blacklist']['soft'] )
|
||||
print OK
|
||||
|
||||
if table == 'nat':
|
||||
pretty_print(table, chain)
|
||||
for net in base.config.NETs['all']:
|
||||
self.add(table, chain, '-d %s -j RETURN' % net)
|
||||
self.add(table, chain, '-p tcp --dport 80 -m set --match-set %s src -j DNAT --to-destination 10.231.136.4:3128' % self.ipset['blacklist']['soft'] )
|
||||
print OK
|
||||
|
||||
if apply:
|
||||
self.apply(table, chain)
|
||||
return chain
|
||||
|
||||
def blacklist_upload_maj(self, ip_list):
|
||||
self.blacklist_upload(fill_ipset=True)
|
||||
# for ip in ip_list:
|
||||
# machine = self.conn.search(u"ipHostNumber=%s" % ip)
|
||||
# # Est-ce qu'il y a des blacklists pour upload parmis les blacklists de la machine
|
||||
# if machine and set([bl.value['type'] for bl in machine[0].blacklist_actif() ]).intersection(blacklist_bridage_upload):
|
||||
# try: self.ipset['blacklist']['upload'].add(ip)
|
||||
# except IpsetError: pass
|
||||
# else:
|
||||
# try: self.ipset['blacklist']['upload'].delete(ip)
|
||||
# except IpsetError: pass
|
||||
|
||||
def blacklist_upload(self, table=None, fill_ipset=False, apply=False):
|
||||
"""Les gens blacklistés ne sont plus prioritaires (classe de qos commune)"""
|
||||
chain = 'BLACKLIST_UPLOAD'
|
||||
|
||||
if fill_ipset:
|
||||
anim('\tRestoration de l\'ipset %s' % self.ipset['blacklist']['upload'])
|
||||
# On récupère la liste de toutes les ips blacklistés pour upload
|
||||
bl_upload_ips = set(
|
||||
str(ip) for ips in
|
||||
[
|
||||
machine['ipHostNumber'] for machine in self.blacklisted_machines()
|
||||
if set([bl.value['type'] for bl in machine.blacklist_actif() ]).intersection(base.config.blacklist_bridage_upload)
|
||||
]
|
||||
for ip in ips
|
||||
)
|
||||
|
||||
self.ipset['blacklist']['upload'].restore(bl_upload_ips)
|
||||
print OK
|
||||
|
||||
if table == 'mangle':
|
||||
pretty_print(table, chain)
|
||||
# Classification pour les blacklists upload
|
||||
self.add(table, chain, '-o %s -m set --match-set %s src -j CLASSIFY --set-class 1:11' % (dev['out'], self.ipset['blacklist']['upload']))
|
||||
|
||||
print OK
|
||||
|
||||
if apply:
|
||||
self.apply(table, chain)
|
||||
return chain
|
||||
|
||||
def reseaux_non_routable(self, table=None, fill_ipset=False, apply=False):
|
||||
"""Bloque les réseaux non routables autres que ceux utilisés par le crans"""
|
||||
chain = 'RESEAUX_NON_ROUTABLES'
|
||||
|
||||
if fill_ipset:
|
||||
anim('\tRestoration de l\'ipset reseaux_non_routable')
|
||||
allowed = [ net for nets in base.config.NETs.values() for net in nets if utils.NetInNets(net, base.config.firewall.reseaux_non_routables) ]
|
||||
self.ipset['reseaux_non_routable']['allow'].restore(allowed)
|
||||
self.ipset['reseaux_non_routable']['deny'].restore(base.config.firewall.reseaux_non_routables)
|
||||
print OK
|
||||
|
||||
if table == 'filter':
|
||||
pretty_print(table, chain)
|
||||
self.add(table, chain, '-m set --match-set %s src -j RETURN' % self.ipset['reseaux_non_routable']['allow'])
|
||||
self.add(table, chain, '-m set --match-set %s dst -j RETURN' % self.ipset['reseaux_non_routable']['allow'])
|
||||
self.add(table, chain, '-m set --match-set %s src -j DROP' % self.ipset['reseaux_non_routable']['deny'])
|
||||
self.add(table, chain, '-m set --match-set %s dst -j DROP' % self.ipset['reseaux_non_routable']['deny'])
|
||||
print OK
|
||||
|
||||
if apply:
|
||||
self.apply(table, chain)
|
||||
return chain
|
||||
|
||||
def filtrage_ports_maj(self, ip_lists):
|
||||
self.filtrage_ports('filter', apply=True)
|
||||
|
||||
def filtrage_ports(self, table=None, apply=False):
|
||||
"""Ouvre les ports vers et depuis les machines du réseau crans"""
|
||||
chain = 'FILTRAGE-PORTS'
|
||||
|
||||
def format_port(port):
|
||||
port = str(port)
|
||||
if port.endswith(':'):
|
||||
port = '%s65535' % port
|
||||
if port.startswith(':'):
|
||||
port = '0%s' % port
|
||||
return port
|
||||
|
||||
def add_ports(ip, machine, proto, sens):
|
||||
self.add(
|
||||
table,
|
||||
chain,
|
||||
'-p %s -%s %s -m multiport --dports %s -j RETURN' % (
|
||||
proto,
|
||||
(sens=='out' and 's') or (sens == 'in' and 'd'),
|
||||
ip,
|
||||
','.join( format_port(port) for port in machine['port%s%s' % (proto.upper(), sens)])
|
||||
)
|
||||
)
|
||||
|
||||
if table == 'filter':
|
||||
pretty_print(table, chain)
|
||||
for net in base.config.NETs['adherents'] + base.config.NETs['wifi-adh'] + base.config.NETs['personnel-ens']:
|
||||
for proto in base.config.firewall.ports_default.keys():
|
||||
if base.config.firewall.ports_default[proto]['output']:
|
||||
self.add(table, chain, '-p %s -s %s -m multiport --dports %s -j RETURN' % (proto, net, ','.join( format_port(port) for port in base.config.firewall.ports_default[proto]['output'])))
|
||||
if base.config.firewall.ports_default[proto]['input']:
|
||||
self.add(table, chain, '-p %s -d %s -m multiport --dports %s -j RETURN' % (proto, net, ','.join( format_port(port) for port in base.config.firewall.ports_default[proto]['input'])))
|
||||
|
||||
for machine in self.machines():
|
||||
for ip in machine['ipHostNumber']:
|
||||
if 'portTCPout' in machine.attrs.keys():
|
||||
add_ports(ip, machine, 'tcp', 'out')
|
||||
if 'portUDPout' in machine.attrs.keys():
|
||||
add_ports(ip, machine, 'udp', 'out')
|
||||
if 'portTCPin' in machine.attrs.keys():
|
||||
add_ports(ip, machine, 'tcp', 'in')
|
||||
if 'portUDPin' in machine.attrs.keys():
|
||||
add_ports(ip, machine, 'udp', 'in')
|
||||
|
||||
self.add(table, chain, '-j REJECT')
|
||||
print OK
|
||||
|
||||
if apply:
|
||||
self.apply(table, chain)
|
||||
return chain
|
||||
|
||||
def limitation_debit(self, table=None, run_tc=False, apply=False):
|
||||
"""Limite le débit de la connexion selon l'agréement avec l'ENS"""
|
||||
chain = 'LIMITATION-DEBIT'
|
||||
|
||||
debit_max = base.config.firewall.debit_max
|
||||
uplink_speed = '1024mbit'
|
||||
|
||||
if table == 'mangle':
|
||||
pretty_print(table, chain)
|
||||
# Pas de QoS vers/depuis la zone ENS
|
||||
self.add(table, chain, '-d 138.231.0.0/16 -s 138.231.0.0/16 -j RETURN')
|
||||
|
||||
# Idem pour le ftp
|
||||
self.add(table, chain, '-d 138.231.136.98 -j RETURN')
|
||||
self.add(table, chain, '-s 138.231.136.98 -j RETURN')
|
||||
|
||||
# Idem vers OVH pour le test de la connection de secours
|
||||
self.add(table, chain, '-d 91.121.84.138 -j RETURN')
|
||||
self.add(table, chain, '-s 91.121.84.138 -j RETURN')
|
||||
|
||||
# Classification par defaut pour tous les paquets
|
||||
for net in base.config.NETs['all']:
|
||||
self.add(table, chain, '-o %s -s %s -j CLASSIFY --set-class 1:10' % (dev['out'], net))
|
||||
self.add(table, chain, '-o %s -d %s -j CLASSIFY --set-class 1:10' % (dev['fil'], net))
|
||||
self.add(table, chain, '-o %s -d %s -j CLASSIFY --set-class 1:10' % (dev['wifi'], net))
|
||||
|
||||
# Classification pour les appartements
|
||||
for net in base.config.NETs['personnel-ens']:
|
||||
self.add(table, chain, '-o %s -d %s -j CLASSIFY --set-class 1:3' % (dev['app'], net))
|
||||
self.add(table, chain, '-o %s -s %s -j CLASSIFY --set-class 1:2' % (dev['out'], net))
|
||||
|
||||
# Classification pour la voip
|
||||
self.add(table, chain, '-d sip.crans.org -j CLASSIFY --set-class 1:12')
|
||||
self.add(table, chain, '-s sip.crans.org -j CLASSIFY --set-class 1:12')
|
||||
print OK
|
||||
|
||||
if run_tc:
|
||||
anim('\tApplication des commandes tc')
|
||||
for int_key in ['out', 'fil', 'wifi']:
|
||||
try:
|
||||
utils.tc('qdisc del dev %s root' % dev[int_key])
|
||||
except utils.TcError:
|
||||
pass
|
||||
utils.tc('qdisc add dev %s root handle 1: htb r2q 1' % dev[int_key])
|
||||
utils.tc("class add dev %s parent 1: classid 1:1 "
|
||||
"htb rate %s ceil %s" % (dev[int_key], uplink_speed, uplink_speed))
|
||||
utils.tc("class add dev %s parent 1:1 classid 1:2 "
|
||||
"htb rate %skbps ceil %skbps" % (dev[int_key], debit_max, debit_max))
|
||||
|
||||
# Classe par defaut
|
||||
utils.tc('class add dev %s parent 1:2 classid 1:10 '
|
||||
'htb rate %skbps ceil %skbps prio 1' % (dev[int_key], debit_max, debit_max))
|
||||
utils.tc('qdisc add dev %s parent 1:10 '
|
||||
'handle 10: sfq perturb 10' % dev[int_key])
|
||||
|
||||
# Classe par pour la voip
|
||||
utils.tc('class add dev %s parent 1:2 classid 1:12 '
|
||||
'htb rate %skbps ceil %skbps prio 0' % (dev[int_key], debit_max, debit_max))
|
||||
utils.tc('qdisc add dev %s parent 1:12 '
|
||||
'handle 12: sfq perturb 10' % dev[int_key])
|
||||
|
||||
#Classe des decos upload
|
||||
utils.tc('class add dev %s parent 1:2 classid 1:11 '
|
||||
'htb rate 60kbps ceil 60kbps prio 1' % dev['out'])
|
||||
utils.tc('qdisc add dev %s parent 1:11 '
|
||||
'handle 11: sfq perturb 10' % dev['out'])
|
||||
|
||||
for int_key in ['app']:
|
||||
try:
|
||||
utils.tc('qdisc del dev %s root' % dev[int_key])
|
||||
except TcError:
|
||||
pass
|
||||
utils.tc('qdisc add dev %s root handle 1: htb r2q 1' % dev[int_key])
|
||||
|
||||
utils.tc("class add dev %s parent 1: classid 1:1 "
|
||||
"htb rate 128kbps ceil 128kbps" % dev[int_key])
|
||||
|
||||
# Classe pour l'upload des appartements
|
||||
utils.tc("class add dev %s parent 1:1 classid 1:2 "
|
||||
"htb rate 128kbps ceil 128kbps" % dev[int_key])
|
||||
utils.tc('qdisc add dev %s parent 1:2 '
|
||||
'handle 2: sfq perturb 10' % dev[int_key])
|
||||
|
||||
# Classe pour le download des apparetments
|
||||
utils.tc("class add dev %s parent 1: classid 1:3 "
|
||||
"htb rate %skbps ceil %skbps" % (dev[int_key], debit_max/10, debit_max/2))
|
||||
utils.tc('qdisc add dev %s parent 1:3 '
|
||||
'handle 3: sfq perturb 10' % dev[int_key])
|
||||
|
||||
print OK
|
||||
|
||||
if apply:
|
||||
self.apply(table, chain)
|
||||
return chain
|
||||
|
||||
|
108
gestion/gen_confs/firewall4/routeur.py
Normal file
108
gestion/gen_confs/firewall4/routeur.py
Normal file
|
@ -0,0 +1,108 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
import utils
|
||||
import base
|
||||
|
||||
from utils import pretty_print, OK, anim
|
||||
from base import dev
|
||||
|
||||
class firewall(base.firewall):
|
||||
|
||||
def __init__(self):
|
||||
super(self.__class__, self).__init__()
|
||||
|
||||
self.reloadable.update({
|
||||
'portail_captif_route' : self.portail_captif_route,
|
||||
'portail_captif' : self.portail_captif,
|
||||
})
|
||||
|
||||
self.use_ipset.extend([])
|
||||
self.use_tc.extend([])
|
||||
|
||||
def raw_table(self):
|
||||
table = 'raw'
|
||||
super(self.__class__, self).raw_table()
|
||||
return
|
||||
|
||||
def mangle_table(self):
|
||||
table = 'mangle'
|
||||
super(self.__class__, self).mangle_table()
|
||||
return
|
||||
|
||||
def filter_table(self):
|
||||
table = 'filter'
|
||||
super(self.__class__, self).filter_table()
|
||||
|
||||
chain = 'FORWARD'
|
||||
self.flush(table, chain)
|
||||
self.add(table, chain, '-j %s' % self.portail_captif_route(table))
|
||||
return
|
||||
|
||||
def nat_table(self):
|
||||
table = 'nat'
|
||||
super(self.__class__, self).raw_table()
|
||||
|
||||
chain = 'PREROUTING'
|
||||
self.add(table, chain, '-j %s' % self.portail_captif(table))
|
||||
|
||||
chain = 'POSTROUTING'
|
||||
self.add(table, chain, '-j %s' % self.portail_captif_route(table))
|
||||
return
|
||||
|
||||
def portail_captif_route(self, table=None, apply=False):
|
||||
"""PNAT les (ip,port) à laisser passer à travers le portail captif"""
|
||||
chain = 'CAPTIF-ROUTE'
|
||||
|
||||
if table == 'filter':
|
||||
pretty_print(table, chain)
|
||||
for ip in base.config.accueil_route.keys():
|
||||
for type in base.config.accueil_route[ip].keys():
|
||||
if type in ['udp', 'tcp']:
|
||||
self.add(table, chain, '-p %s -d %s -m multiport --dports %s -j ACCEPT' % (type, ip, ','.join(base.config.accueil_route[ip][type])))
|
||||
self.add(table, chain, '-p %s -s %s -m multiport --sports %s -j ACCEPT' % (type, ip, ','.join(base.config.accueil_route[ip][type])))
|
||||
self.add(table, chain, '-j REJECT')
|
||||
print OK
|
||||
|
||||
if table == 'nat':
|
||||
pretty_print(table, chain)
|
||||
#intranet et wiki pour le vlan accueil
|
||||
for ip in base.config.accueil_route.keys():
|
||||
for type in base.config.accueil_route[ip].keys():
|
||||
if type in ['udp', 'tcp']:
|
||||
for net in base.config.NETs['accueil']:
|
||||
self.add(table, chain, '-s %s -p %s -d %s -m multiport --dports %s -j MASQUERADE' % (net, type, ip, ','.join(base.config.accueil_route[ip][type])))
|
||||
for net in base.config.NETs['isolement']:
|
||||
self.add(table, chain, '-s %s -p %s -d %s -m multiport --dports %s -j MASQUERADE' % (net, type, ip, ','.join(base.config.accueil_route[ip][type])))
|
||||
for net in base.config.NETs['personnel-ens']:
|
||||
self.add(table, chain, '-i %s -s %s -j MASQUERADE' % (dev['app'], net))
|
||||
print OK
|
||||
|
||||
if apply:
|
||||
self.apply(table, chain)
|
||||
return chain
|
||||
|
||||
def portail_captif(self, table=None, apply=False):
|
||||
"""Redirige vers le portail captif"""
|
||||
chain = 'PORTAIL-CAPTIF'
|
||||
|
||||
if table == 'nat':
|
||||
pretty_print(table, chain)
|
||||
for ip in base.config.accueil_route.keys():
|
||||
for type in base.config.accueil_route[ip].keys():
|
||||
if type in ['udp', 'tcp']:
|
||||
self.add(table, chain, '-p %s -d %s -m multiport --dports %s -j RETURN' % (type, ip, ','.join(base.config.accueil_route[ip][type])))
|
||||
|
||||
for net in base.config.NETs['isolement']:
|
||||
self.add(table, chain, '-p tcp -s %s --destination-port 80 -j DNAT --to-destination 10.52.0.10' % net)
|
||||
|
||||
for net in base.config.NETs['accueil']:
|
||||
self.add(table, chain, '-p tcp -s %s --destination-port 80 -j DNAT --to-destination 10.51.0.10' % net)
|
||||
self.add(table, chain, '-p udp -s %s --dport 53 -j DNAT --to 10.51.0.10' % net)
|
||||
self.add(table, chain, '-p tcp -s %s --dport 53 -j DNAT --to 10.51.0.10' % net)
|
||||
print OK
|
||||
|
||||
if apply:
|
||||
self.apply(table, chain)
|
||||
return chain
|
||||
|
||||
|
251
gestion/gen_confs/firewall4/utils.py
Normal file
251
gestion/gen_confs/firewall4/utils.py
Normal file
|
@ -0,0 +1,251 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
import os
|
||||
import sys
|
||||
|
||||
if '/usr/scripts/' not in sys.path:
|
||||
sys.path.append('/usr/scripts/')
|
||||
|
||||
import syslog
|
||||
import subprocess
|
||||
|
||||
from gestion.affich_tools import anim, OK, cprint
|
||||
from gestion.iptools import AddrInNet, NetSubnets, IpSubnet, NetInNets
|
||||
|
||||
import lc_ldap.shortcuts
|
||||
import lc_ldap.objets
|
||||
import lc_ldap.attributs
|
||||
|
||||
squeeze = os.uname()[2] < '3'
|
||||
|
||||
#: Chaines par défaut d'iptables
|
||||
default_chains = ['INPUT', 'OUTPUT', 'FORWARD', 'PREROUTING', 'POSTROUTING']
|
||||
#: Tables d'iptables
|
||||
tables = ['raw', 'mangle', 'filter', 'nat']
|
||||
|
||||
|
||||
def pretty_print(table, chain):
|
||||
"""Affiche quelle chaine est en train d'être construite dans quelle table de NetFilter"""
|
||||
anim('\t%s dans %s' % (chain, table))
|
||||
|
||||
|
||||
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 tc(cmd, block=True):
|
||||
""" Interface de tc """
|
||||
params = ['/sbin/tc']
|
||||
params.extend(cmd.split())
|
||||
p = subprocess.Popen(params , stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
if block:
|
||||
status = p.wait()
|
||||
stdoutdata, stderrdata = p.communicate()
|
||||
if status:
|
||||
raise TcError(' '.join(params), status, stdoutdata + stderrdata)
|
||||
return stdoutdata + stderrdata
|
||||
|
||||
class firewall_tools(object) :
|
||||
"""Classe de base du pare-feu implémentant l'association mac-ip (pour les machines filaires) et les blacklists hard"""
|
||||
|
||||
def machines(self):
|
||||
"""Renvois la liste de toutes les machines"""
|
||||
if self._machines:
|
||||
return self._machines
|
||||
self._machines, self._adherents = self.conn.allMachinesAdherents()
|
||||
return self._machines
|
||||
|
||||
def adherents(self):
|
||||
"""Renvois la liste de tous les adhérents"""
|
||||
if self._adherents:
|
||||
return self._adherents
|
||||
self._machines, self._adherents = self.conn.allMachinesAdherents()
|
||||
self._adherents = [ adh for adh in self._adherents if adh.paiement_ok ]
|
||||
return self._adherents
|
||||
|
||||
def blacklisted_machines(self):
|
||||
"""Renvois la liste de toutes les machines ayant une blackliste actives"""
|
||||
if self._blacklisted_machines:
|
||||
return self._blacklisted_machines
|
||||
self._blacklisted_machines = [ machine for machine in self.machines() if machine.blacklist_actif() ]
|
||||
return self._blacklisted_machines
|
||||
|
||||
def blacklisted_adherents(self, excepts=[]):
|
||||
"""Renvois la liste de tous les adhérents ayant une blackliste active"""
|
||||
if self._blacklisted_adherents and self._blacklisted_adherents_type == set(excepts):
|
||||
return self._blacklisted_adherents
|
||||
self._blacklisted_adherents = filter(lambda adh: adh.blacklist_actif(excepts), self.adherents())
|
||||
self._blacklisted_adherents_type = set(excepts)
|
||||
return self._blacklisted_adherents
|
||||
|
||||
def add(self, table, chain, rule):
|
||||
"""Ajoute la règle ``rule`` à la chaine ``chain`` dans la table ``table``"""
|
||||
if not chain in self.chain_list[table]:
|
||||
self.chain_list[table].append(chain)
|
||||
self.rules_list[table][chain]=[]
|
||||
self.rules_list[table][chain].append(rule)
|
||||
|
||||
def delete(self, table=None, chain=None):
|
||||
"""Supprime ``chain`` de ``table`` si fournis, sinon supprime ``table``, sinon toutes les tables"""
|
||||
if not table:
|
||||
for table in tables:
|
||||
self.delete(table, chain)
|
||||
if not chain:
|
||||
for chain in self.chain_list[table]:
|
||||
self.delete(table, chain)
|
||||
|
||||
if not chain in self.chain_list[table]:
|
||||
return
|
||||
if not chain in default_chains:
|
||||
self.chain_list[table].remove(chain)
|
||||
del(self.rules_list[table][chain])
|
||||
else:
|
||||
self.rules_list[table][chain]=[]
|
||||
|
||||
def flush(self, table=None, chain=None):
|
||||
"""Vide ``chain`` dans ``table`` si fournis, sinon vide toutes
|
||||
les chaines de ``table``, sinon vide toutes les chaines de toutes les tables"""
|
||||
if not table:
|
||||
for table in tables:
|
||||
self.flush(table, chain)
|
||||
if not chain:
|
||||
for chain in self.chain_list[table]:
|
||||
self.flush(table, chain)
|
||||
if not chain in self.chain_list[table]:
|
||||
self.chain_list[table].append(chain)
|
||||
self.rules_list[table][chain]=[]
|
||||
|
||||
def restore(self, table=None, chains=[], noflush=False):
|
||||
"""Restores les règles du pare-feu dans le noyau en appelant ``iptables-restore``.
|
||||
Si ``table`` est fournis, on ne restore que table.
|
||||
Si ``noflush`` n'est pas à ``True`` tout le contenu précédent est supprimé"""
|
||||
str=self.format(chains)
|
||||
f=open('/tmp/ipt_rules', 'w')
|
||||
f.write(str)
|
||||
f.close()
|
||||
params = ['/sbin/iptables-restore']
|
||||
if noflush:
|
||||
params.append('--noflush')
|
||||
if table and table in ['raw', 'mangle', 'filter', 'nat']:
|
||||
params.append('--table')
|
||||
params.append(table)
|
||||
p = subprocess.Popen(params , stdin=subprocess.PIPE)
|
||||
p.communicate(input=str)
|
||||
|
||||
def apply(self, table, chain):
|
||||
"""Applique les règles de ``chain`` dans ``table``"""
|
||||
if not chain in self.chain_list[table]:
|
||||
return
|
||||
self.restore(table, [chain], noflush=True)
|
||||
self.delete(table, chain)
|
||||
|
||||
def format(self, chains=[]):
|
||||
"""Transforme la structure interne des règles du pare-feu en celle comprise par ``iptables-restore``"""
|
||||
str = ''
|
||||
for table in self.chain_list.keys():
|
||||
str += '*%s\n' % table
|
||||
for chain in self.chain_list[table]:
|
||||
if not chains or chain in chains or chain in default_chains:
|
||||
str += ':%s %s [0:0]\n' % (chain, chain in default_chains and 'ACCEPT' or '-')
|
||||
for chain in self.chain_list[table]:
|
||||
if not chains or chain in chains :
|
||||
for rule in self.rules_list[table][chain]:
|
||||
str += '-A %s %s\n' % (chain, rule)
|
||||
str += 'COMMIT\n'
|
||||
return str
|
||||
|
||||
def reload(self, func_name):
|
||||
if squeeze and self.reloadable[func_name] in self.use_ipset:
|
||||
anim('\tVidage de %s' % self.reloadable[func_name]())
|
||||
for table in ['raw', 'mangle', 'filter', 'nat']:
|
||||
self.flush(table, self.reloadable[func_name]())
|
||||
self.restore(noflush=True)
|
||||
print OK
|
||||
|
||||
for table in ['raw', 'mangle', 'filter', 'nat']:
|
||||
self.reloadable[func_name](table)
|
||||
if self.reloadable[func_name] in self.use_ipset:
|
||||
self.reloadable[func_name](fill_ipset=True)
|
||||
if self.reloadable[func_name] in self.use_tc:
|
||||
self.reloadable[func_name](run_tc=True)
|
||||
|
||||
anim('\tRestoration d\'iptables')
|
||||
self.restore(noflush=True)
|
||||
print OK
|
||||
|
||||
def __init__(self):
|
||||
#initialisation des structures communes : récupération des ipset
|
||||
if os.getuid() != 0:
|
||||
from affich_tools import coul
|
||||
sys.stderr.write(coul("Il faut être root pour utiliser le firewall\n", 'gras'))
|
||||
sys.exit(1)
|
||||
|
||||
# Connection à la base ldap
|
||||
self.conn = lc_ldap.shortcuts.lc_ldap_admin(user=u'firewall')
|
||||
|
||||
self.reloadable = {}
|
||||
|
||||
self.use_ipset = []
|
||||
self.use_tc = []
|
||||
|
||||
self._machines = None
|
||||
self._adherents = None
|
||||
self._blacklisted_machines = None
|
||||
self._blacklisted_adherents = None
|
||||
|
||||
self.chain_list={
|
||||
'raw':['OUTPUT', 'PREROUTING'],
|
||||
'mangle':['INPUT', 'OUTPUT', 'FORWARD', 'PREROUTING', 'POSTROUTING'],
|
||||
'filter':['INPUT', 'OUTPUT', 'FORWARD'],
|
||||
'nat':['OUTPUT', 'PREROUTING', 'POSTROUTING']
|
||||
}
|
||||
self.rules_list = {
|
||||
'raw': { 'OUTPUT':[], 'PREROUTING':[] },
|
||||
'mangle':{'INPUT':[], 'OUTPUT':[], 'FORWARD':[], 'PREROUTING':[], 'POSTROUTING':[]},
|
||||
'filter':{'INPUT':[], 'OUTPUT':[], 'FORWARD':[]},
|
||||
'nat':{'OUTPUT':[], 'PREROUTING':[], 'POSTROUTING':[]}
|
||||
}
|
||||
|
||||
self.ipset={}
|
||||
|
||||
|
||||
|
||||
def start(self):
|
||||
"""Démarre le pare-feu : génère les règles, puis les restore"""
|
||||
anim('\tChargement des machines')
|
||||
self.machines()
|
||||
self.blacklisted_machines()
|
||||
print OK
|
||||
|
||||
if squeeze:
|
||||
anim('\tVidage du pare-feu')
|
||||
self.restore()
|
||||
print OK
|
||||
|
||||
self.raw_table()
|
||||
self.mangle_table()
|
||||
self.filter_table()
|
||||
self.nat_table()
|
||||
|
||||
anim('\tRestoration d\'iptables')
|
||||
self.restore()
|
||||
print OK
|
||||
return
|
||||
|
||||
def stop(self):
|
||||
"""Vide les règles du pare-feu"""
|
||||
self.delete()
|
||||
self.restore()
|
||||
return
|
||||
|
||||
def restart(self):
|
||||
"""Alias de :py:func:`start`"""
|
||||
self.start()
|
||||
return
|
||||
|
109
gestion/gen_confs/firewall4/zamok.py
Normal file
109
gestion/gen_confs/firewall4/zamok.py
Normal file
|
@ -0,0 +1,109 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
import utils
|
||||
import base
|
||||
import pwd
|
||||
|
||||
from utils import pretty_print, OK, anim
|
||||
from base import dev
|
||||
|
||||
class firewall(base.firewall):
|
||||
|
||||
def __init__(self):
|
||||
super(self.__class__, self).__init__()
|
||||
|
||||
self.reloadable.update({
|
||||
'admin_vlan' : self.admin_vlan,
|
||||
'blacklist_output' : self.blacklist_output,
|
||||
})
|
||||
|
||||
self.use_ipset.extend([])
|
||||
self.use_tc.extend([])
|
||||
|
||||
|
||||
def raw_table(self):
|
||||
table = 'raw'
|
||||
|
||||
super(self.__class__, self).raw_table()
|
||||
|
||||
return
|
||||
|
||||
def mangle_table(self):
|
||||
table = 'mangle'
|
||||
|
||||
super(self.__class__, self).mangle_table()
|
||||
|
||||
return
|
||||
|
||||
def filter_table(self):
|
||||
table = 'filter'
|
||||
|
||||
super(self.__class__, self).filter_table()
|
||||
|
||||
chain = 'OUTPUT'
|
||||
self.add(table, chain , '-d 224.0.0.0/4 -j DROP')
|
||||
admin_vlan_chain = self.admin_vlan(table)
|
||||
self.add(table, chain, '-m state --state RELATED,ESTABLISHED -j ACCEPT')
|
||||
for net in base.config.NETs['adm']:
|
||||
self.add(table, chain, '-d %s -j %s' % (net, admin_vlan_chain))
|
||||
self.add(table, chain, '-o lo -j ACCEPT')
|
||||
self.add(table, chain, '-j %s' % self.blacklist_output(table))
|
||||
|
||||
return
|
||||
|
||||
def nat_table(self):
|
||||
table = 'nat'
|
||||
|
||||
super(self.__class__, self).raw_table()
|
||||
return
|
||||
|
||||
def admin_vlan(self, table=None, apply=False):
|
||||
chain='ADMIN-VLAN'
|
||||
|
||||
if table == 'filter':
|
||||
pretty_print(table, chain)
|
||||
# ldap et dns toujours joinable
|
||||
self.add(table, chain, '-p tcp --dport ldap -j ACCEPT')
|
||||
self.add(table, chain, '-p tcp --dport domain -j ACCEPT')
|
||||
self.add(table, chain, '-p udp --dport domain -j ACCEPT')
|
||||
|
||||
# Pour le nfs (le paquet à laisser passer n'a pas d'owner)
|
||||
self.add(table, chain, '-d nfs.adm.crans.org -j ACCEPT')
|
||||
|
||||
for user in base.config.adm_users:
|
||||
try: self.add(table, chain, '-m owner --uid-owner %d -j ACCEPT' % pwd.getpwnam(user)[2])
|
||||
except KeyError: print "Utilisateur %s inconnu" % user
|
||||
|
||||
for adh in self.conn.search(u"(|(droits=%s)(droits=%s))" % (utils.lc_ldap.attributs.nounou, utils.lc_ldap.attributs.apprenti)):
|
||||
self.add(table, chain, '-m owner --uid-owner %s -j RETURN' % adh['uidNumber'][0])
|
||||
|
||||
# Rien d'autre ne passe
|
||||
self.add(table, chain, '-j REJECT --reject-with icmp-net-prohibited')
|
||||
print OK
|
||||
|
||||
if apply:
|
||||
self.apply(table, chain)
|
||||
return chain
|
||||
|
||||
def blacklist_maj(self, ips):
|
||||
self.blacklist_output('filter', apply=True)
|
||||
self.blacklist_hard_maj(ips)
|
||||
|
||||
def blacklist_output(self, table=None, apply=False):
|
||||
"""Empêche les gens blacklisté d'utiliser zamok comme relaie"""
|
||||
chain='BLACKLIST-OUTPUT'
|
||||
|
||||
if table == 'filter':
|
||||
pretty_print(table, chain)
|
||||
self.add(table, chain, '-d 127.0.0.1/8 -j RETURN')
|
||||
for net in base.config.NETs['all']:
|
||||
self.add(table, chain, '-d %s -j RETURN' % net)
|
||||
for adh in self.blacklisted_adherents(['paiement']):
|
||||
if 'uidNumber' in adh.attrs.keys():
|
||||
self.add(table, chain, '-m owner --uid-owner %s -j REJECT' % adh['uidNumber'][0])
|
||||
print OK
|
||||
|
||||
if apply:
|
||||
self.apply(table, chain)
|
||||
return chain
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue