## page was renamed from CransTechnique/FireWall = Le pare-feu = Le pare-feu (en anglais firewall) se place entre la machine qui a accès à Internet et Internet lui-même. Il protège l'une de l'autre et inversement en limitant les types de connexions qui ont le droit d'entrer et de sortir. <> :s règles sont générées par un script, qui termine en remplaçant le set de règles courant par celui qu'il a généré. Cette opération est atomique (on utilise {{{iptables-restore}}}, donc il n'y a pas de phase pendant laquelle tout est ouvert (ou fermé) le temps que la régénération se termine. La table est généré à partir des infos de re2o, cf [[CransTechnique/Services/Re2oServices|re2o-services]] == Le nouveau parefeu == === Utilisation === Il se lance avec {{{/var/local/re2o-services/firewall/main.py restart}}} (potentiellement avec l'option --force) et se coupe avec l'instruction {{{stop}}}. Celle dernière injectera un set de règles vide, avec policy ACCEPT. === Principes === ==== Architecture générale ==== Le script est une classe {{{iptables}}} qui a différentes sous-fonctions selon ce qu'on veut générer. Le début contient les fonctions de base, d'intialisation des chaînes iptables, etc. {{{#! 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.conn = shortcuts.lc_ldap_readonly() […] 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) }}} On remarque donc déjà qu'il y a 6 attributs principaux à notre classe, filter4, filter6, nat4, nat6, mangle4 et mangle6. Ainsi, au moment de l'injection des règles qui est faite au démarrage du parefeu, (iptables-restore) : {{{#! 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) }}} Très simplement donc, on remet les règles ensembles pour le parefeu 6 d'un coté, le parefeu 4 de l'autre, et on restore. Savoir si on veut générer que le 4, le 6 ou les 2 est un argument de la fonction en question, on verra un peu plus tard comment c'est fait. ==== Ajout des chaines ==== L'idée a été de coder un parefeu unifié. Ainsi, on essaye de faire en sorte d'avoir le plus possible des tables compatibles avec iptables et ip6tables. Par exemple, l'acceptation des connexions déjà établies sera le même en v4 ou en v6 {{{#! 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 }}} Ainsi l'appel à {{{init_filter(subtable)}}} va initialiser la sous table ESTABLISHED-CONN dans la chaîne filter. Mais, étant donné que cette chaine n'existe pas (rappellez-vous, c'est filter4 ou filter6), il va l'ajouter dans les 2. Idem pour {{{jump_all_trafic}}}, qui va attraper tout le trafic sur toutes les interfaces, en v4 ou en v6, dans le forward ou dans l'input, et l'envoyer vers la sous-table ESTABLISHED-CONN. Enfin, la dernière règle peuple la sous-table avec une règle acceptant tout le trafic déjà établi. ==== Génération du firwall en v4 , v6 ou les 2 ? ==== Vous l'avez compris, on ne va pas choisir les mêmes chaines suivant si c'est un routeur v4, un routeur v6 ou encore zamok. Regardons comment est appelé {{{restore-iptables}}} : {{{#! 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') }}} Ca parait clair, on regarde donc self.roles pour savoir si c'est un v4, un v6 ou les 2, et on restore. Mais comment faire la différence par exemple entre le parefeu v6 de zamok et le parefeu v6 de la VM ipv6 du routage ? ==== Sélection des rôles ==== Les rôles sont spécifiés dans {{{firewall_config.py}}} installé par Bcfg2 ({{{/var/local/bcfg2/Python/var/local/re2o-services/firewall/firewall_config.py}}} Exemple : {{{ ─( 15:34:36 )─< /var/local/re2o-services/firewall >───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────(git)─[firewall/crans]─[ 0 ]─ jacomme@gulp $ cat firewall_config.py #! role = ['routeur4'] ### Specify each interface role interfaces_type = { 'routable' : ['eno1.1', 'ens1f0.21', 'ens1f0.22', 'ens1f0.23', 'ens1f0.24'], 'sortie' : ['ens1f0.26', 'ens1f0.1132'], 'admin' : ['eno1.2', 'eno1.3'] } ### Specify nat settings: name, interfaces with range, and global range for nat ### WARNING : "interface_ip_to_nat' MUST contain /24 ranges, and ip_sources MUST ### contain /16 range nat = [ { 'name' : 'Wifi', 'interfaces_ip_to_nat' : { 'ens1f0.26' : '185.230.76.0/24', 'eno1.1' : '138.231.144.0/24', 'ens1f0.1132' : '138.231.144.0/24', }, 'ip_sources' : '10.53.0.0/16' }, { 'name' : 'Filaire', 'interfaces_ip_to_nat' : { 'ens1f0.26' : '185.230.77.0/24', 'eno1.1' : '138.231.145.0/24', 'ens1f0.1132' : '138.231.145.0/24', }, 'ip_sources' : '10.54.0.0/16' } ] }}} On spécifie donc ici les roles, ne pas mettre 4 ou 6 générera un parefeu v4 et v6. On spécifie également les interfaces non routables, routables, et si il y a lieu l'interface de sortie vers le monde extérieur. Ne pas mettre de roles générera un parefeu minimal, cf plus bas. ==== Application des rôles ==== Regardons donc gen_filter : {{{#! 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() }}} On initialise donc ici les tables FILTER, FORWARD et OUTPUT de filter, en v4 et en v6. On ajoute des règles générales (accept established, filter_icmp) communes à tous les parefeu. Enfin, on appelle la fonction self."role" avec l'option filter, pour l'ensemble des roles définis pour self. ==== Sélections des chaînes à ajouter ==== Exemple pour le rôle routeur6 (role donc du routeur ipv6), on appelle {{{base_filter}}} (une factorisation de plusieurs règles, vous le trouverez dessous) et différentes fonctions necessaires, tel que le filtrage ports en v6, la limitation des connexions ssh, etc. {{{#! def routeur6(self, table): """Methode appelé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": self.log() else: pass }}} Base filter est appelée par pas mal de roles, (zamok, routeur etc) c'était donc plus joli de tout factoriser. {{{#! 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() }}} Voilà,''a priori'', là on a tout pour générer notre fichier iptables. Evidemment, se référer aux docstrings pour voir ce que chaque fonction fait. == Cahier des charges == === Fonctions de base commmunes === ==== Filter ==== * Acceptation de toute connexion déjà établie * Acceptation du ping v4 et v6 en input et forward * ping ⊊ ICMP. Réfléchir à si on veut tout l'ICMP (ne pas oublier que les "ça a pas marché", c'est aussi de l'ICMP, mais pas du ping). -- [[Wiki20-100]] <> * Blocage des connexions entrantes vers les réseaux admin (adm, admbornes etc) sur l'interface correspondante * Oui. Mais je pense qu'il manque la fin de la phrase. -- [[Wiki20-100]] <> * DROP du traffic illégitime venant de l'extérieur (broadcast et multicast qu'on n'est pas censés recevoir, IP non routables, etc.) * Blacklist hard des connexions entrantes et sortantes des utilisateurs blacklistés * Filtrage des ports en v4 et v6 sur les interfaces d'entrée de la zone crans * Limitation des connexions ssh entrantes pour éviter les attaques par bruteforce * Limitation des connexions tcp et udp en input (limit connexions dstip) afin d'éviter le flood de connexions (cf attaque sur le wiki par un adhérent) === Fonctions supplémentaires du parefeu de sortie (odlyd, sable et ipv6) === ==== Filter ==== * Blacklist hard des utilisateurs blacklistés en forward * Filtrage des ports en v4 et v6 sur les interfaces de sortie de la zone crans * Limitation des connexions ssh en forward pour éviter les attaques bruteforce * Pas sûr que ce soit vraiment notre soucis -- [[Wiki20-100]] <> * Limitation des connexions sortantes tcp/udp pour éviter le flood d'une machine vérolée vers l'éxtérieur (limit connexions srcip) * Pareil -- [[Wiki20-100]] <> ==== Nat ==== * NAT des ip privées filaires et wifi en v4 ==== Mangle ==== * Log de toutes les nouvelles connexions sur les interfaces de sortie et routables === Fonctions supplémentaires du parefeu adhérents === ==== Filter ==== * Interdiction de l'output vers adm pour non nounous/apprentis * Interdiction de l'output vers odlyd pour les non à jour de cotiz (todo) == Legacy - pour les divers parefeu du crans == <> ---- * CatégorieCrans * CatégoriePagePublique