#!/usr/bin/env python3 from configparser import ConfigParser import socket from re2oapi import Re2oAPIClient from jinja2 import Environment, FileSystemLoader import requests import base64 import json import subprocess import socket import argparse import firewall_config import sys import os path =(os.path.dirname(os.path.abspath(__file__))) config = ConfigParser() config.read(path+'/config.ini') api_hostname = config.get('Re2o', 'hostname') api_password = config.get('Re2o', 'password') api_username = config.get('Re2o', 'username') api_client = Re2oAPIClient(api_hostname, api_username, api_password, use_tls=False) client_hostname = socket.gethostname().split('.', 1)[0] class iptables: def __init__(self): self.nat4 = "\n*nat" self.mangle4 = "\n*mangle" self.filter4 = "\n*filter" self.nat6 = "\n*nat" self.mangle6 = "\n*mangle" self.filter6 = "\n*filter" self.subnet_ports = api_client.list("firewall/subnet-ports/") self.interface_ports = api_client.list("firewall/interface-ports/") self.verbose = False self.action = None self.export = False self.role = getattr(firewall_config, 'role', None) self.interfaces_settings = getattr(firewall_config, 'interfaces_type', None) self.nat_settings = getattr(firewall_config, 'nat', None) def commit(self, chain): self.add(chain, "COMMIT\n") def commit_filter(self): self.add("filter4", "COMMIT\n") self.add("filter6", "COMMIT\n") def commit_mangle(self): self.add("mangle4", "COMMIT\n") self.add("mangle6", "COMMIT\n") def commit_nat(self): self.add("nat4", "COMMIT\n") self.add("nat6", "COMMIT\n") def add(self, chain, value): setattr(self, chain, getattr(self, chain) + "\n" + value) def add_in_subtable(self, chain, subtable, value): if '4' in chain: self.add(chain, "-A " + subtable + " " + value) elif '6' in chain: self.add(chain, "-A " + subtable + " " + value) else: self.add(chain + '4', "-A " + subtable + " " + value) self.add(chain + '6', "-A " + subtable + " " + value) def init_filter(self, subchain, decision="ACCEPT", mode='all'): if mode == 'all' or mode == '4': self.add("filter4", ":" + subchain + " " + decision) if mode == 'all' or mode == '6': self.add("filter6", ":" + subchain + " " + decision) def init_nat(self, subchain, decision="ACCEPT", mode='all'): if mode == 'all' or mode == '4': self.add("nat4", ":" + subchain + " " + decision) if mode == 'all' or mode == '6': self.add("nat6", ":" + subchain + " " + decision) def init_mangle(self, subchain, decision="ACCEPT", mode='all'): if mode == 'all' or mode == '4': self.add("mangle4", ":" + subchain + " " + decision) if mode == 'all' or mode == '6': self.add("mangle6", ":" + subchain + " " + decision) def jump(self, chain, subchainA, subchainB): self.add(chain, "-A " + subchainA + " -j " + subchainB) def jump_all_trafic(self, chain, subchainA, subchainB, mode='all'): if mode == 'all' or mode == '4': self.add(chain + '4', "-A " + subchainA + " -j " + subchainB) if mode == 'all' or mode == '6': self.add(chain + '6', "-A " + subchainA + " -j " + subchainB) def jump_traficfrom(self, chain, interface, subchainA, subchainB, mode='all'): if mode == 'all' or mode == '4': self.add(chain + '4', "-A " + subchainA + " -i " + interface + " -j " + subchainB) if mode == 'all' or mode == '6': self.add(chain + '6', "-A " + subchainA + " -i " + interface + " -j " + subchainB) def jump_traficto(self, chain, interface, subchainA, subchainB, mode='all'): if mode == 'all' or mode == '4': self.add(chain + '4', "-A " + subchainA + " -o " + interface + " -j " + subchainB) if mode == 'all' or mode == '6': self.add(chain + '6', "-A " + subchainA + " -o " + interface + " -j " + subchainB) def atomic_add(self, chain, subtable, command, mode='4'): command_to_send = "-t %s -I %s 1 %s" % (chain, subtable, command) if mode == 'all' or mode == '4': command_to_execute = ["sudo","-n","/sbin/iptables"] + command_to_send.split() if mode == 'all' or mode == '6': command_to_execute = ["sudo","-n","/sbin/ip6tables"] + command_to_send.split() process = subprocess.Popen(command_to_execute, stdin=subprocess.PIPE, stdout=subprocess.PIPE) def atomic_del(self, chain, subtable, command, mode='4'): command_to_send = "-t %s -D %s %s" % (chain, subtable, command) if mode == 'all' or mode == '4': command_to_execute = ["sudo","-n","/sbin/iptables"] + command_to_send.split() if mode == 'all' or mode == '6': command_to_execute = ["sudo","-n","/sbin/ip6tables"] + command_to_send.split() process = subprocess.Popen(command_to_execute, stdin=subprocess.PIPE, stdout=subprocess.PIPE) def routeur6(self, table): """Methode appellée spécifiquement pour le parefeu v6""" if table == "filter": self.base_filter() if self.verbose: print("Filter : interdit les machines blacklistées en forward") self.blacklist_hard_forward() if self.verbose: print("Filter : filtage ports v6") self.filtrage_ports(ip_type='6') if self.verbose: print("Filter : limit connexions forward") self.limit_ssh_connexion_forward() if self.verbose: print("Filter : Limit connexion src") self.limit_connexion_srcip() elif table == "mangle": if self.verbose: print("Mangle : Mise en place des logs") self.log() if self.verbose: print("Mangle : Réglage correct du MSS") self.mss() else: pass def routeur4(self, table): """Methode appellée spécifiquement pour le parefeu v4""" if table == "filter": self.base_filter() if self.verbose: print("Filter : interdit les machines blacklistées en forward") # self.blacklist_hard_forward() if self.verbose: print("Filter : filtrage ports 4") self.filtrage_ports(ip_type='4') if self.verbose: print("Filter : limit ssh connexion forward") self.limit_ssh_connexion_forward() if self.verbose: print("Filter : limit connexion src ip") self.limit_connexion_srcip() elif table == "mangle": if self.verbose: print("Mangle : Mise en place des logs") self.log() if self.verbose: print("Mangle : Réglage correct du MSS") self.mss() elif table == "nat": for nat_to_do in self.nat_settings: if self.verbose: print("Nat : priv" + nat_to_do['name']) self.nat_prive_ip(nat_to_do) def portail(self, table): if table == "filter": self.base_filter() if self.verbose: print("Filter : autorisation des ip en sortie") self.captif_autorized_ip() if table == "nat": if self.verbose: print("Nat : nat et captures les connexions du portail masquerade") self.nat_connexion_portail() self.capture_connexion_portail() def users(self, table): """Securisation d'un serveur avec comptes d'utilisateurs""" if table == 'filter': #self.blacklist_output() self.base_filter() if self.verbose: print("Filter : Forbid admin vlan for users") self.forbid_adm() else: pass def radius(self, table): if table == 'filter': if self.verbose: print("Filter : acceptation des connexions uniquement des serveurs federez") self.accept_freerad_from_server() else: pass def base_filter(self): if self.verbose: print("Filter : reseaux non routables") self.reseaux_non_routables() if self.verbose: print("Filter : bl hard") #self.blacklist_hard() if self.verbose: print("Filter : connexion input") if self.verbose: print("Limitation des connexions") self.limit_ssh_connexion_input() self.limit_connexion_dstip() def gen_filter(self, empty=False): self.init_filter("INPUT") self.init_filter("FORWARD") self.init_filter("OUTPUT") if not empty: if self.verbose: print("Filter : icmp") self.filter_icmp() if self.verbose: print("Filter : icmpv6") self.filter_icmpv6() if self.verbose: print("Filter : accept established") self.accept_established() for role in self.role: if hasattr(self, role): getattr(self, role)('filter') self.commit_filter() def filtrage_ports(self, ip_type='4', subtable='FILTRAGE-PORTS'): """Filtrage ports en entrée/sortie du réseau""" if ip_type == '4': chain = "filter4" else: chain = "filter6" self.init_filter(subtable, decision="-") for interface in self.interfaces_settings['sortie']: self.jump_traficto("filter", interface, "FORWARD", subtable, mode=ip_type) self.jump_traficfrom("filter", interface, "FORWARD", subtable, mode=ip_type) def add_general_rule(ports, ip_type, chain, subtable, subnet, protocol, direction): """Règles générales, fonction de factorisation""" if ip_type == '4': self.add_in_subtable(chain, subtable, """-m iprange --%s-range %s-%s -p %s -m multiport --dports %s -j RETURN""" % (direction, subnet["domaine_ip_start"], subnet["domaine_ip_stop"], protocol, ports)) if ip_type == '6': self.add_in_subtable(chain, subtable, """-%s %s -p %s -m multiport --dports %s -j RETURN""" % (direction[0], subnet["complete_prefixv6"], protocol, ports)) #Ajout des règles générales for subnet in self.subnet_ports: if subnet["ouverture_ports"]: if subnet["ouverture_ports"]["tcp_ports_in"]: ports = ','.join(rule["show_port"] for rule in subnet["ouverture_ports"]["tcp_ports_in"]) add_general_rule(ports, ip_type, chain, subtable, subnet, 'tcp', 'dst') if subnet["ouverture_ports"]["tcp_ports_out"]: ports = ','.join(rule["show_port"] for rule in subnet["ouverture_ports"]["tcp_ports_out"]) add_general_rule(ports, ip_type, chain, subtable, subnet, 'tcp', 'src') if subnet["ouverture_ports"]["udp_ports_in"]: ports = ','.join(rule["show_port"] for rule in subnet["ouverture_ports"]["udp_ports_in"]) add_general_rule(ports, ip_type, chain, subtable, subnt, 'udp', 'dst') if subnet["ouverture_ports"]["udp_ports_out"]: ports = ','.join(rule["show_port"] for rule in subnet["ouverture_ports"]["udp_ports_out"]) add_general_rule(ports, ip_type, chain, subtable, subnet, 'udp', 'src') def add_specific_rule(ports, ip_type, chain, interface, subnet, protocol, direction): """Règles spécifique, fonction de factorisation""" if ip_type == '4': self.add_in_subtable(chain, subtable, """-%s %s -p %s -m multiport --dports %s -j RETURN""" % (direction[0], interface['ipv4'], protocol, ports)) if ip_type == '6': for ipv6_addr in interface['ipv6']: self.add_in_subtable(chain, subtable, """-%s %s -p %s -m multiport --dports %s -j RETURN""" % (direction[0], ipv6_addr['ipv6'], protocol, ports)) for interface in self.interface_ports: ports = ','.join([port_list['show_port'] for dict_ports in interface["port_lists"] for port_list in dict_ports["tcp_ports_in"]]) if ports: add_specific_rule(ports, ip_type, chain, interface, subnet, 'tcp', 'dst') ports = ','.join([port_list['show_port'] for dict_ports in interface["port_lists"] for port_list in dict_ports["tcp_ports_out"]]) if ports: add_specific_rule(ports, ip_type, chain, interface, subnet, 'tcp', 'src') ports = ','.join([port_list['show_port'] for dict_ports in interface["port_lists"] for port_list in dict_ports["udp_ports_in"]]) if ports: add_specific_rule(ports, ip_type, chain, interface, subnet, 'udp', 'dst') ports = ','.join([port_list['show_port'] for dict_ports in interface["port_lists"] for port_list in dict_ports["udp_ports_out"]]) if ports: add_specific_rule(ports, ip_type, chain, interface, subnet, 'udp', 'src') #Rejet du reste self.add_in_subtable(chain, subtable, """-j REJECT""") def accept_freerad_from_server(self, subtable='RADIUS-SERVER'): """Accepte uniquement le trafique venant des serveurs radius federez""" self.init_filter(subtable, decision="-") for interface in self.interfaces_settings['sortie']: self.jump_traficfrom("filter", interface, "INPUT", subtable) for server in self.config_firewall.radius_server: self.add_in_subtable("filter4", subtable, """-s %s -p %s -m multiport --dports %s -j ACCEPT""" % (server['ipaddr'], server['protocol'], ','.join(server['port']))) self.add_in_subtable("filter6", subtable, """-s %s -p %s -m multiport --dports %s -j ACCEPT""" % (server['ip6addr'], server['protocol'], ','.join(server['port']))) self.add_in_subtable("filter", subtable, """-j REJECT""") def reseaux_non_routables(self, subtable='ADM-NETWORK'): """Bloc le trafic vers les réseaux non routables""" self.init_filter(subtable, decision="-") for interface in self.interfaces_settings['admin']: self.jump_traficto("filter", interface, "FORWARD", subtable) self.add_in_subtable("filter", subtable, """-j REJECT""") def captif_autorized_ip(self, subtable='FILTRE-IP-PORTAIL'): """Autorise les ip whitelistées sur le portail captif accueil""" self.init_filter(subtable, decision="-") self.jump_all_trafic("filter", "FORWARD", subtable, mode='4') for ip in self.config.accueil_route.keys(): if 'tcp' in self.config.accueil_route[ip]: self.add_in_subtable("filter4", subtable, """-p tcp -d %s -m multiport --dports %s -j ACCEPT""" % (ip, ','.join(self.config.accueil_route[ip]['tcp']))) if 'udp' in self.config.accueil_route[ip]: self.add_in_subtable("filter4", subtable, """-p udp -d %s -m multiport --dports %s -j ACCEPT""" % (ip, ','.join(self.config.accueil_route[ip]['udp']))) self.add_in_subtable("filter4", subtable, """-j REJECT""") def capture_connexion_portail(self, subtable="PORTAIL-CAPTIF-REDIRECT"): """Nat les connexions derrière l'ip de la machine du portail""" self.init_nat(subtable, decision="-") for interface in self.interfaces_settings['routable']: self.jump_traficfrom("nat", interface, "PREROUTING", subtable, mode='4') for ip in self.config.accueil_route.keys(): if 'tcp' in self.config.accueil_route[ip]: self.add_in_subtable("nat4", subtable, """-p tcp -d %s -m multiport --dports %s -j RETURN""" % (ip, ','.join(self.config.accueil_route[ip]['tcp']))) if 'udp' in self.config.accueil_route[ip]: self.add_in_subtable("nat4", subtable, """-p udp -d %s -m multiport --dports %s -j RETURN""" % (ip, ','.join(self.config.accueil_route[ip]['udp']))) self.add_in_subtable("nat4", subtable, """-p udp -s %(ip)s/16 --dport 53 -j DNAT --to %(ip)s""" % {'ip' :self.config_firewall.portail['accueil']}) self.add_in_subtable("nat4", subtable, """-p tcp -s %(ip)s/16 --dport 53 -j DNAT --to %(ip)s""" % {'ip' : self.config_firewall.portail['accueil']}) self.add_in_subtable("nat4", subtable, """-p tcp -s %(ip)s/16 --dport 80 -j DNAT --to %(ip)s""" % {'ip' : self.config_firewall.portail['accueil']}) self.add_in_subtable("nat4", subtable, """-p tcp -s %(ip)s/16 --dport 80 -j DNAT --to %(ip)s""" % {'ip' : self.config_firewall.portail['isolement']}) def nat_connexion_portail(self, subtable="PORTAIL-CAPTIF-NAT"): """Nat les connexions derrière l'ip de la machine du portail""" self.init_nat(subtable, decision="-") for interface in self.interfaces_settings['sortie']: self.jump_traficto("nat", interface, "POSTROUTING", subtable, mode='4') for ip in self.config.accueil_route.keys(): if 'tcp' in self.config.accueil_route[ip]: self.add_in_subtable("nat4", subtable, """-p tcp -d %s -m multiport --dports %s -j MASQUERADE""" % (ip, ','.join(self.config.accueil_route[ip]['tcp']))) if 'udp' in self.config.accueil_route[ip]: self.add_in_subtable("nat4", subtable, """-p udp -d %s -m multiport --dports %s -j MASQUERADE""" % (ip, ','.join(self.config.accueil_route[ip]['udp']))) def accept_established(self, subtable='ESTABLISHED-CONN'): """Accepte les connexions déjà établies""" self.init_filter(subtable, decision="-") self.jump_all_trafic("filter", "FORWARD", subtable) self.jump_all_trafic("filter", "INPUT", subtable) self.add_in_subtable("filter", subtable, """-m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT""") def filter_icmpv6(self, subtable='ICMPV6'): self.init_filter(subtable, decision="-", mode='6') self.jump_all_trafic("filter", "INPUT", subtable, mode='6') self.jump_all_trafic("filter", "FORWARD", subtable, mode='6') self.jump_all_trafic("filter", "OUTPUT", subtable, mode='6') self.add_in_subtable("filter6", subtable, """-p icmpv6 -m icmp6 --icmpv6-type echo-request -j ACCEPT""") self.add_in_subtable("filter6", subtable, """-p icmpv6 -m icmp6 --icmpv6-type echo-reply -j ACCEPT""") self.add_in_subtable("filter6", subtable, """-p icmpv6 -m icmp6 --icmpv6-type destination-unreachable -j ACCEPT""") self.add_in_subtable("filter6", subtable, """-p icmpv6 -m icmp6 --icmpv6-type packet-too-big -j ACCEPT""") self.add_in_subtable("filter6", subtable, """-p icmpv6 -m icmp6 --icmpv6-type ttl-zero-during-transit -j ACCEPT""") self.add_in_subtable("filter6", subtable, """-p icmpv6 -m icmp6 --icmpv6-type parameter-problem -j ACCEPT""") def filter_icmp(self, subtable='ICMP'): self.init_filter(subtable, decision="-", mode='4') self.jump_all_trafic("filter", "FORWARD", subtable, mode='4') self.jump_all_trafic("filter", "OUTPUT", subtable, mode='4') self.jump_all_trafic("filter", "INPUT", subtable, mode='4') self.add_in_subtable("filter4", subtable, """-p icmp -j ACCEPT""") def limit_ssh_connexion_input(self, subtable='LIMIT-SSH-INPUT'): self.init_filter(subtable, decision="-") for interface in self.interfaces_settings['routable']: self.jump_traficfrom("filter", interface, "INPUT", subtable) self.add_in_subtable("filter", subtable, """-p tcp --dport ssh -m state --state NEW -m recent --name SSH-INPUT --set""") self.add_in_subtable("filter", subtable, """-p tcp --dport ssh -m state --state NEW -m recent --name SSH-INPUT --update --seconds 120 --hitcount 10 --rttl -j DROP""") def limit_ssh_connexion_forward(self, subtable='LIMIT-SSH-FORWARD'): self.init_filter(subtable, decision="-") for interface in self.interfaces_settings['sortie']: self.jump_traficfrom("filter", interface, "FORWARD", subtable) self.add_in_subtable("filter", subtable, """-p tcp --dport ssh -m state --state NEW -m recent --name SSH-FORWARD --set""") self.add_in_subtable("filter", subtable, """-p tcp --dport ssh -m state --state NEW -m recent --name SSH-FORWARD --update --seconds 30 --hitcount 10 --rttl -j DROP""") def limit_connexion_srcip(self, subtable='LIMIT-CONNEXION-SRCIP'): self.init_filter(subtable, decision="-") for interface in self.interfaces_settings['sortie']: self.jump_traficto("filter", interface, "FORWARD", subtable) self.add_in_subtable("filter", subtable, """-p udp -m hashlimit --hashlimit-upto 400/sec --hashlimit-burst 800 --hashlimit-mode srcip --hashlimit-name LIMIT_UDP_SRCIP_CONNEXION -j RETURN""") self.add_in_subtable("filter", subtable, """-p udp -m hashlimit --hashlimit-upto 5/hour --hashlimit-burst 5 --hashlimit-mode srcip --hashlimit-name LIMIT_UDP_SRCIP_CONNEXION_LOG -j LOG --log-prefix "CONNEXION_LIMIT_UDP" """) self.add_in_subtable("filter", subtable, """-p udp -j REJECT""") self.add_in_subtable("filter", subtable, """-p tcp -m hashlimit --hashlimit-upto 2000/min --hashlimit-burst 4000 --hashlimit-mode srcip --hashlimit-name LIMIT_TCP_SRCIP_CONNEXION -m state --state NEW -j RETURN""") self.add_in_subtable("filter", subtable, """-p tcp -m hashlimit --hashlimit-upto 5/hour --hashlimit-burst 5 --hashlimit-mode srcip --hashlimit-name LIMIT_TCP_SRCIP_CONNEXION_LOG -m state --state NEW -j LOG --log-prefix "CONNEXION_LIMIT_TCP_SRCIP " """) self.add_in_subtable("filter", subtable, """-p tcp -m state --state NEW -j REJECT""") self.add_in_subtable("filter", subtable, """-m hashlimit --hashlimit-upto 400/sec --hashlimit-burst 800 --hashlimit-mode srcip --hashlimit-name LIMIT_OTHER_SRCIP_CONNEXION -j RETURN""") self.add_in_subtable("filter", subtable, """-m hashlimit --hashlimit-upto 5/hour --hashlimit-burst 5 --hashlimit-mode srcip --hashlimit-name LIMIT_OTHER_SRCIP_CONNEXION_LOG -j LOG --log-prefix "CONNEXION_LIMIT " """) self.add_in_subtable("filter", subtable, """-j REJECT""") def limit_connexion_dstip(self, subtable='LIMIT-CONNEXION-DSTIP', cible='INPUT'): self.init_filter(subtable, decision="-") for interface in self.interfaces_settings['sortie']: self.jump_traficfrom("filter", interface, "FORWARD", subtable) for interface in self.interfaces_settings['routable']: self.jump_traficfrom("filter", interface, "INPUT", subtable) self.add_in_subtable("filter", subtable, """-p udp -m hashlimit --hashlimit-upto 400/sec --hashlimit-burst 800 --hashlimit-mode srcip --hashlimit-name LIMIT_UDP_DSTIP_CONNEXION -j RETURN""") self.add_in_subtable("filter", subtable, """-p udp -m hashlimit --hashlimit-upto 5/hour --hashlimit-burst 5 --hashlimit-mode srcip --hashlimit-name LIMIT_UDP_DSTIP_CONNEXION_LOG -j LOG --log-prefix "CONNEXION_LIMIT_UDP" """) self.add_in_subtable("filter", subtable, """-p udp -j REJECT""") self.add_in_subtable("filter", subtable, """-p tcp -m hashlimit --hashlimit-upto 2000/min --hashlimit-burst 4000 --hashlimit-mode srcip --hashlimit-name LIMIT_TCP_DSTIP_CONNEXION -m state --state NEW -j RETURN""") self.add_in_subtable("filter", subtable, """-p tcp -m hashlimit --hashlimit-upto 5/hour --hashlimit-burst 5 --hashlimit-mode srcip --hashlimit-name LIMIT_TCP_DSTIP_CONNEXION_LOG -m state --state NEW -j LOG --log-prefix "CONNEXION_LIMIT_TCP_DSTIP " """) self.add_in_subtable("filter", subtable, """-p tcp -m state --state NEW -j REJECT""") self.add_in_subtable("filter", subtable, """-m hashlimit --hashlimit-upto 400/sec --hashlimit-burst 800 --hashlimit-mode srcip --hashlimit-name LIMIT_OTHER_DSTIP_CONNEXION -j RETURN""") self.add_in_subtable("filter", subtable, """-m hashlimit --hashlimit-upto 5/hour --hashlimit-burst 5 --hashlimit-mode srcip --hashlimit-name LIMIT_OTHER_DSTIP_CONNEXION_LOG -j LOG --log-prefix "CONNEXION_LIMIT " """) self.add_in_subtable("filter", subtable, """-j REJECT""") def blacklist_hard_forward(self, subtable='BLACKLIST-HARD'): """Blacklist les machines en forward, à appliquer sur les routeurs de sortie""" for interface in self.interfaces_settings['routable']: self.jump_traficfrom("filter", interface, "FORWARD", subtable) def blacklist_hard(self, subtable='BLACKLIST-HARD'): """Génération de la chaine blackliste hard, blackliste des mac des machines bl""" self.init_filter(subtable, decision="-") for interface in self.interfaces_settings['routable']: self.jump_traficfrom("filter", interface, "INPUT", subtable) for machine in self.conn.allMachines(): if machine.blacklist_actif() and set(bl['type'] for bl in machine.blacklist_actif()).intersection(self.config.blacklist_sanctions) and machine['macAddress'] and machine['macAddress'][0].value != '': self.add_in_subtable("filter", subtable, """-m mac --mac-source %s -j REJECT""" % machine['macAddress'][0].value) def blacklist_output(self, subtable='BLACKLIST-OUTPUT'): """Génération de la chaine blackliste output, meme idée que si dessus sauf que ici on filtre les users uid sur un serveur et non leurs ip""" self.init_filter(subtable, decision="-") for interface in self.interfaces_settings['routable']: self.jump_traficto("filter", interface, "OUTPUT", subtable) for user in self.conn.search(u'(&(uidNumber=*)(!(droits=nounou))(!(droits=apprenti))(|(objectClass=adherent)(objectClass=club)))', sizelimit=10000): if user.blacklist_actif(): self.add_in_subtable("filter", subtable, """-m owner --uid-owner %s -j REJECT""" % user['uidNumber'][0].value) def forbid_adm(self, subtable='ADMIN-VLAN'): """Interdit aux users non admin de parler sur les vlans admin""" self.init_filter(subtable, decision="-") for interface in self.interfaces_settings['admin']: self.jump_traficto("filter", interface, "OUTPUT", subtable) for user in self.conn.search(u'(&(uidNumber=*)(!(droits=nounou))(!(droits=apprenti))(|(objectClass=adherent)(objectClass=club)))', sizelimit=10000): self.add_in_subtable("filter", subtable, """-m owner --uid-owner %s -j REJECT""" % user['uidNumber'][0].value) def gen_nat(self, empty=False): """Génération de la chaine nat""" self.init_nat("PREROUTING") self.init_nat("INPUT") self.init_nat("OUTPUT") self.init_nat("POSTROUTING") if not empty: for role in self.role: if hasattr(self, role): getattr(self, role)('nat') self.commit_nat() def log(self, subtable='LOGALL'): """Logage des packet sur les interfaces choisies""" self.init_mangle(subtable, decision="-") self.jump_all_trafic("mangle", "PREROUTING", subtable) self.add_in_subtable("mangle", subtable, '-m state --state NEW -j LOG --log-prefix "LOG_ALL " ') def mss(self, subtable='MSS'): """Reglage correct du MSS pour éviter les problèmes de MTU""" self.init_mangle(subtable, decision="-") self.jump_all_trafic("mangle", "POSTROUTING", subtable) self.add_in_subtable("mangle", subtable, '-p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu') def nat_prive_ip(self, nat_type): """Nat filaire en v4""" subtable = "CONNEXION-NAT-" + nat_type['name'].upper() self.init_nat(subtable, decision="-") self.jump_all_trafic("nat", "POSTROUTING", subtable) nat_prive_ip_plage = nat_type['ip_sources'] for nat_ip_range in range(1, 26): range_name = 'nat' + nat_prive_ip_plage.split('.')[1] + '_' + str("%02d" % nat_ip_range ) self.init_nat(range_name, decision="-") self.add_in_subtable("nat", subtable, '-s ' + '.'.join(nat_prive_ip_plage.split('.')[:2]) + '.' + str(nat_ip_range) + '.0/24 -j ' + range_name) for nat_ip_range in range(1, 26): range_name = 'nat' + nat_prive_ip_plage.split('.')[1] + '_' + str("%02d" % nat_ip_range) for nat_ip_subrange in range(16): subrange_name = range_name + '_' + str(hex(nat_ip_subrange)[2:]) self.init_nat(subrange_name, decision="-") self.add_in_subtable("nat", range_name, '-s ' + '.'.join(nat_prive_ip_plage.split('.')[:2]) + '.' + str(nat_ip_range) + '.' + str(nat_ip_subrange*16) + '/28 -j ' + subrange_name) for nat_private_ip in range(256): ip_src = '.'.join(nat_prive_ip_plage.split('.')[:2]) + '.' + str(nat_ip_range) + '.' + str(nat_private_ip) + '/32' port_low = 10000 + 2000*(nat_private_ip%26) port_high = port_low + 1999 subrange_name = range_name + '_' + str(hex(nat_private_ip//16)[2:]) # On nat for interface, pub_ip_range in nat_type['interfaces_ip_to_nat'].items(): ip_nat = '.'.join(pub_ip_range.split('.')[:3]) + '.' + str(10*(nat_ip_range - 1) + nat_private_ip//26) self.add_in_subtable("nat", subrange_name, '-s %s -o %s -p tcp -j SNAT --to-source %s' % (ip_src, interface, ip_nat + ':' + str(port_low) + '-' + str(port_high))) self.add_in_subtable("nat", subrange_name, '-s %s -o %s -p udp -j SNAT --to-source %s' % (ip_src, interface, ip_nat + ':' + str(port_low) + '-' + str(port_high))) # On nat tout ce qui match dans les règles et qui n'est pas du tcp/udp derrière la première ip publique unused (25*10) + 1 # Ne pas oublier de loguer ce qui sort de cette ip for interface, pub_ip_range in nat_type['interfaces_ip_to_nat'].items(): self.add_in_subtable("nat", subtable, '-s ' + nat_prive_ip_plage + ' -o %s -j SNAT --to-source ' % (interface,) + '.'.join(pub_ip_range.split('.')[:3]) + '.250') def gen_mangle(self, empty=False): """Génération de la chaine mangle""" self.init_mangle("PREROUTING") self.init_mangle("INPUT") self.init_mangle("FORWARD") self.init_mangle("OUTPUT") self.init_mangle("POSTROUTING") if not empty: for role in self.role: if hasattr(self, role): getattr(self, role)('mangle') self.commit_mangle() def restore_iptables(self, mode='4'): """Restoration de l'iptable générée""" if mode == '6': global_chain = self.nat6 + self.filter6 + self.mangle6 command_to_execute = ["sudo","-n","/sbin/ip6tables-restore"] else: global_chain = self.nat4 + self.filter4 + self.mangle4 command_to_execute = ["sudo","-n","/sbin/iptables-restore"] process = subprocess.Popen(command_to_execute, stdin=subprocess.PIPE, stdout=subprocess.PIPE) process.communicate(input=global_chain.encode('utf-8')) if self.export: print(global_chain) def complete_flush_iptables(self, mode='4'): """Insère un parefeuv6 vide, appellé par l'arrét du parefeu""" self.restore_iptables(mode=mode) def do_action(self): """Effectue l'action demandée""" if self.action == "start" or self.action == "restart": self.reload() elif self.action == "stop": self.flush() else: raise NotImplementedError("Action non reconnu, actions valides : start, stop ou restart") def reload(self): """Recharge le parefeu""" self.gen_mangle() self.gen_nat() self.gen_filter() if any('6' in role for role in self.role): self.restore_iptables(mode='6') return if any('4' in role for role in self.role): self.restore_iptables(mode='4') return self.restore_iptables(mode='6') self.restore_iptables(mode='4') def flush(self): """Vide la chaine iptables, ou ip6tables suivant le role du serveur""" self.gen_mangle(empty=True) self.gen_nat(empty=True) self.gen_filter(empty=True) if any('6' in role for role in self.role): self.complete_flush_iptables(mode='6') if any('4' in role for role in self.role): self.complete_flush_iptables(mode='4') self.restore_iptables(mode='6') self.restore_iptables(mode='4') def add_port(self, ports, ip_cible, sens, protocole, subtable='FILTRAGE-PORTS', mode='4'): """Ajout atomique d'une ouverture de port ports : liste des ports à ouvrir sens : source ou destination protocole : tcp ou udp ip cible : ip où s'applique l'ouverture""" if sens == "source": self.atomic_add("filter", subtable, """-%s %s -p %s -m multiport --dports %s -j RETURN""" % ('s', ip_cible, protocole, ','.join(self.format_port(port) for port in ports)), mode=mode) if sens == "destination": self.atomic_add("filter", subtable, """-%s %s -p %s -m multiport --dports %s -j RETURN""" % ('d', ip_cible, protocole, ','.join(self.format_port(port) for port in ports)), mode=mode) def add_in_blacklist_hard(self, mac, subtable='BLACKLIST-HARD', mode='4'): """Ajoute la mac à la blacklist""" self.atomic_add("filter", subtable, """-m mac --mac-source %s -j REJECT""" % mac, mode=mode) def del_in_blacklist_hard(self, mac, subtable='BLACKLIST-HARD', mode='4'): """Retire la mac de la blacklist""" self.atomic_del("filter", subtable, """-m mac --mac-source %s -j REJECT""" % mac, mode=mode) def run(args): table = iptables() if args.verbose: table.verbose = True table.action = args.action table.export = args.export table.do_action() if __name__ == '__main__': parser = argparse.ArgumentParser() parser.add_argument("-v", "--verbose", help="increase output verbosity", action="store_true") parser.add_argument("-e", "--export", help="export le contenu du parefeu", action="store_true") parser.add_argument("action", help="Mode reconnus : start, stop ou restart", default="restart") args = parser.parse_args() for arg in sys.argv: if arg=="--force": run(args) for service in api_client.list("services/regen/"): if service['hostname'] == client_hostname and \ service['service_name'] == 'firewall' and \ service['need_regen']: run(args) api_client.patch(service['api_url'], data={'need_regen': False})