#!/usr/bin/env python # -*- coding: utf-8 -*- import os import sys import socket 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() #: 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, 'blacklists' : self.blacklists, } self.use_ipset = [self.blacklist_hard, self.test_mac_ip, self.blacklists] 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 blacklists(self, table=None, fill_ipset=False, apply=False): """Recharge toutes les blacklists""" self.blacklist_hard(table=table, fill_ipset=fill_ipset, apply=apply) 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['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: # On récupère la liste de toutes les ips blacklistés hard bl_hard_ips = self.blacklisted_ips(config.blacklist_sanctions, config.NETs['all']) anim('\tRestoration de l\'ipset %s' % self.ipset['blacklist']['hard']) 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 mac_ip_append(self, mac, ip): machine = {'macAddress':[mac], 'ipHostNumber': [ip]} self.test_mac_ip_dispatch(lambda set, data: self.ipset['mac_ip'][set].add(data), machine) def mac_ip_remove(self, mac, ip): machine = {'macAddress':[mac], 'ipHostNumber': [ip]} self.test_mac_ip_dispatch(lambda set, data: set.ipset['mac_ip'][set].delete(data), machine) 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'] if '' in machine['macAddress'] : return 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_du_routeur)) 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_soyouz = self.conn.search(u"host=soyouz.adm.crans.org")[0]['ipHostNumber'][0] self.add(table, chain, '-m mac -s %s --mac-source %s -j RETURN' % (ip_soyouz, config.mac_du_routeur)) self.add(table, chain, '-m mac -s %s --mac-source %s -j RETURN' % (ip_soyouz, 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'] if '' in machine['macAddress'] : return 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'] if '' in machine['macAddress'] : return 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_du_routeur)) # 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]))