La configuration de l'adressage IP sous Ubuntu est concentrée dans le fichier
/etc/network/interfaces
.
Ce fichier contient la liste des interfaces du système et leur configuration IP. Dans sa forme la plus simple, et la plus fréquente, la configuration d'une interface se présente sous cette forme :
iface <interface> inet static address <adresse_ip> netmask <masque> gateway <passerelle_par_defaut>
La passerelle_par_defaut ne devrait apparaitre que sur le configuration d'une seule interface.
Dans ce fichier, on trouve aussi la directive
auto
, suivie de noms d'interfaces. Elle
permet d'indiquer au système d'activer cette interface au
boot.
On privilégiera, autant que faire se peut, l'adressage statique
(iface eth0 inet static
) à l'adressage DHCP
(iface eth0 inet dhcp
). Certains serveurs
(Samba) ne nécessitent pas forcément une adresse statique, mais utiliser des
adresses statiques permet de filtrer plus efficacement les flux entre machines.
La directive pre-up
peut être adjointe à la
configuration d'une interface. Nous l'utilserons plus bas pour charger les
règles de filtrage iptables
.
Le détail et toutes les possibilités du fichier de configuration
/etc/network/interfaces
sont donnés dans la page de man
d'interfaces
(man 5 interfaces
).
# ## Interface de loopback # démarrage au boot des interfaces auto lo eth0 # définition loopback iface lo inet loopback # définition eth0 iface eth0 inet static address 192.168.0.33 netmask 255.255.255.0 gateway 213.245.116.99
Le fichier /etc/iftab
a une importance lorsque le machine
possède plusieurs cartes réseau. Il permet au système de savoir quel nom
(eth0
, eth1
, ...)
il doit affecter à une interface physique.
Le fichier est composé de lignes sous la forme
<device> mac <mac>
Par exemple :
eth0 mac 00:12:79:59:8D:38 eth1 mac 00:12:79:59:8D:56
Grâce à cette association statique entre l'adresse MAC de la carte et le device, on peut être sûr que les périphériques réseau seront conformes à ce qui est prévu. Si ce fichier n'est pas renseigné, on ne peut absolument pas être sûr qu'une carte réseau prendra un nom d'interface particulier. L'ordre de détection de ces interfaces pouvant varier au boot, on sera alors sur une règle premier arrivé, premier servi.
La résolution DNS est gérés sous les systèmes Unix par une librairie particulière : le resolver. Ce resolver est principalement configuré dans deux fichiers : un fichier de configuration générale dans lequel on liste les DNS, et un fichier contenant les associations statiques.
Ce fichier contient la liste des serveurs DNS que le resolver interrogera pour effectuer une résolution. Le resolver commencera par interroger le premier. S'il ne reçoit aucune réponse, il interrogera le deuxième, etc... Il ne faudra pas confondre aucune réponse et réponse négative (i.e. « Ce nom d'hôte n'existe pas. »). Une réponse négative est un réponse, les autres serveurs ne seront donc pas interrogés.
On pourra spécifier jusqu'a trois serveurs DNS avec le mot clef
nameserver
.
Le mot clef search
permet de spéficier les
domaines de recherche qui seront utilisés comme suffixe pour
qualifier un nom d'hôte incomplet. Par exemple, si l'on demande au resolver
de trouver l'adresse IP de machine
, il devra transformer
ce nom en nom d'hôte valide, complètement qualifié. Pour cela, il ajoutera successivement
(et uniquement si nécessaire) la valeur configurée dans search
et effectura une résolution, jusqu'a ce qu'une réponse positive soit reçue.
Afin d'éviter les confusions malheureuses, notamment en utilisant un navigateur Web[5], on évitera de recourir à cette
valeur et on utilisera des noms complètement qualifiés.
La directive domain
permet de spécifier
le domaine de l'hôte local et de qualifier automatiquement les noms ne contenant pas de « . ».
Si cette directive est absente, le résolveur essayera de déterminer automatiquement cette valeur
à partir du nom d'hôte complet de la machine.
domain
et search
étant mutuellement exclusifs, on préfèrera l'utilisation de domain
, mais
on essayera si possible de toujours utiliser des noms d'hôte complètement qualifiés.
# # /etc/resolv.conf # # search exemple.lan example.org # domain example.lan. nameserver 190.62.57.12 nameserver 174.70.56.72 # #
Ce fichier contient les associations statiques IP/nom. Il est en général interrogé avant le serveur DNS[6]. Il est constitué d'entrées sous la forme :
adresse_ip nom
Un des usage les plus intéressants de fichier est d'y insérer les adresses des hôtes qui sont sous nôtre responsabilité administrative. Cela permet d'éviter de recourir à un serveur DNS pour résoudre les noms de nos machines et ainsi de réduire les possibilités d'attaque de type « Man In The Middle ». Beaucoup d'autres usages sont possibles : masquer un vrai nom DNS, créer des noms DNS uniquement accessibles à la machine (pour tester des virtualhosts (Section 4.5.3, « Site par défaut et VirtualHosts ») sur le serveur web local par exemple), etc...
# # /etc/hosts # 127.0.0.1 localhost 192.168.17.139 ubuntu.exemple.lan vhost1.exemple.lan vhost2.exemple.lan 192.168.17.254 gw passerelle gw.exemple.lan 192.168.17.1 alice alice.exemple.lan # #
Le filtrage s'entend généralement par l'élimination des paquets entrants sur un réseau ou un dispositif. Ce n'est qu'en partie exact. Le filtrage, c'est l'application d'une politique à la totalité du traffic : le traffic entrant (qu'il soit à destination de la machine ou devant être relayé par elle) appellé ingress, ainsi que le traffic sortant (qu'il soit émis ou simplement transmis par la machine), nommé egress.
On met souvent l'emphase sur le filtrage du traffic ingress, à destination de la machine ou devant être routé par elle vers un réseau interne. A l'évidence, les paquets entrant contiennent du traffic potentiellement dangereux. Mais il ne faut pas sous-estimer l'importance du filtrage egress : le traffic que l'on envoie à destination du reste du monde. Ce trafic est aussi potentiellement dangereux : des machines de mon réseau local peuvent être infectées par des vers ou compromises. Le filtrage egress permet de limiter au maximum le périmètre des dégats.
La configuration ci-dessous est un squelette de base pour protéger un serveur.
Il faudra l'adapter (en ajoutant des règles sur la chaîne
FORWARD
) dans le cas d'un routeur.
La politique par défaut utilisée est DROP
pour
les trois chaînes INPUT
,
OUTPUT
, et
FORWARD
.
Les règles utilisées découpent le trafic sur des chaines représentant les
protocoles ICMP, UDP et TCP (respectivement
ICMP_*
,
UDP_*
et
TCP_*
) ainsi que le sens du traffic
(*_IN
pour le traffic entrant,
*_OUT
pour le traffic sortant).
Par exemple, la chaîne TCP_OUT
contient les
règles autorisant le traffic TCP en sortie.
# # On ne traite que filter # *filter # # Création et remise à zéro des chaînes # :INPUT DROP [0:0] :FORWARD DROP [0:0] ❶ :OUTPUT DROP [0:0] :DROP_ME - [0:0] ❷ :ICMP_IN - [0:0] ❸ :ICMP_OUT - [0:0] ❹ :STATEFUL - [0:0] ❺ :TCP_IN - [0:0] ❻ :TCP_OUT - [0:0] ❼ :TCP_INLIMITS - [0:0] ❽ :TCP_SYNLIMITS - [0:0] ❾ :UDP_IN - [0:0] ❿ :UDP_OUT - [0:0] (11) #
❶ | Politiques par défaut des chaînes INPUT, OUTPUT et FORWARD. |
❷ | Chaîne prenant en charge le rejet du paquet et enenvoi vers
|
❺ | Cette chaîne acceptera les paquets liés à une connexion existante (plus exactement, à une entrée existante dans la table conntrack). |
❸ ❻ ❿ | Création des chaînes dédiées au traitement en entrée des paquets tcp, udp et icmp. |
❹ ❼ (11) | Création des chaînes dédiées au traitement en sortie des paquets tcp, udp et icmp. |
❽ | Chaîne qui recevra les règles de limitation de trafic TCP. |
❾ | Chaîne qui recevra les règles de limitation d'ouvertures de connexions TCP entrantes. |
# # ###################################### # INPUT Dispatch ❶ # ###################################### # -A INPUT -i lo -j ACCEPT -A INPUT -m state --state INVALID -j DROP_ME -A INPUT -s 127.0.0.0/255.0.0.0 -i ! lo -j DROP_ME -A INPUT -p tcp -j TCP_IN -A INPUT -p udp -j UDP_IN -A INPUT -p icmp -j ICMP_IN # # ###################################### # OUTPUT Dispatch ❷ # ###################################### # -A OUTPUT -o lo -j ACCEPT -A OUTPUT -p udp -j UDP_OUT -A OUTPUT -p tcp -j TCP_OUT -A OUTPUT -p icmp -j ICMP_OUT -A OUTPUT -j STATEFUL -A OUTPUT -j REJECT ❸ # # ###################################### # STATEFUL : accepte les paquets liés à une connexion existante ❹ # ###################################### # -A STATEFUL -m state --state RELATED,ESTABLISHED -j ACCEPT -A STATEFUL -j RETURN ... # ###################################### # DROP_ME : la chaîne qui jette en laissant des traces dans les logs ❺ # Par défaut, cette chaîne poubellise les paquets en silence # En changeant la dernière règle par les deux commentées, on notifie la source # du rejet du paquet, c'est plus conforme à la norme. Après, chacun décide s'il # vaut mieux se conformer à la norme avec du trafic qui n'a pas lieu d'être... # --limit permet d'éviter de mettre la machine à genoux en cas de déni # de service # ###################################### # -A DROP_ME -p tcp -m limit --limit 10/min -j LOG --log-prefix "DROP:" --log-level 6 -A DROP_ME -p udp -m limit --limit 10/min -j LOG --log-prefix "DROP:" --log-level 6 ## On pourra utiliser REJECT si l'on souhaite être poli ##-A DROP_ME -p tcp -j REJECT --reject-with tcp-reset ##-A DROP_ME -p udp -j REJECT --reject-with icmp-port-unreachable -A DROP_ME -j DROP #
❺ | Chaîne prenant en charge le rejet du paquet et enenvoi vers
|
❹ | Cette chaîne acceptera les paquets liés à une connexion existant (plus exactement, à une entrée existante dans la table conntrack). |
❶ | Dispatch des paquets entrants vers les chaînes de traitement. |
❷ | Dispatch des paquets sortants vers les chaînes de traitement. |
❸ |
Permet de rejeter les paquets avant qu'ils n'atteignent la politique par default.
Cette dernière étant |
# # ###################################### # ICMP entrant ❶ # ###################################### # -A ICMP_IN -j STATEFUL -A ICMP_IN -p icmp -m icmp --icmp-type 3 -j ACCEPT -A ICMP_IN -p icmp -m icmp --icmp-type 11 -j ACCEPT -A ICMP_IN -p icmp -m icmp --icmp-type 0 -j ACCEPT -A ICMP_IN -p icmp -m icmp --icmp-type 8 -m limit --limit 5/sec -j ACCEPT # # ###################################### # ICMP sortant ❷ # ###################################### # -A ICMP_OUT -j STATEFUL -A ICMP_OUT -p icmp -m icmp --icmp-type 8 -j ACCEPT # # ###################################### # TCP entrant ❸ # Il faudra ouvrir des ports au fil de l'eau # lors de la mise en place de # services TCP (ssh, apache, ...). # ###################################### # -A TCP_IN -j TCP_INLIMITS -A TCP_IN -j STATEFUL -A TCP_IN -p tcp -m tcp ! --tcp-flags SYN,RST,ACK SYN -m limit --limit 10/min -j LOG --log-prefix "TCP_IN:" --log-level 6 -A TCP_IN -p tcp -m tcp ! --tcp-flags SYN,RST,ACK SYN -j DROP # Ajouter les rêgles ici lors de l'installation de services TCP si ces services # doivent être ouverts # # ###################################### # TCP sortant ❹ # Cette machine initie des connexions HTTP vers fr.archive.ubuntu.com # et security.ubuntu.com pour les mises à jour # ###################################### # -A TCP_OUT -j STATEFUL -A TCP_OUT -p tcp -m tcp ! --tcp-flags SYN,RST,ACK SYN -m limit --limit 10/min -j LOG --log-prefix "TCP_OUT:" --log-level 6 -A TCP_OUT -p tcp -m tcp ! --tcp-flags SYN,RST,ACK SYN -j DROP ❺ -A TCP_OUT -p tcp -d 194.2.0.36 --dport 80 -j ACCEPT -A TCP_OUT -p tcp -d 82.211.81.138 --dport 80 -j ACCEPT -A TCP_OUT -p tcp -d 91.189.88.31 --dport 80 -j ACCEPT # # ###################################### # UDP entrant ❻ # L'appel à STATEFUL suffit pour accepter les réponses DNS # Il faudra cependant ouvrir des ports au fil de l'eau lors de la mise en place # de services UDP (DNS, NTP par exemple). # ###################################### # -A UDP_IN -j STATEFUL # Ajouter les rêgles ici lors de l'installation de services UDP si ces services # doivent être ouverts # # ###################################### # UDP sortant ❼ # -remplacer SERVEUR_DNS par le serveur DNS et répéter la ligne pour chacun des # serveurs (primaire, secondaire, etc...) # -remplacer SERVEUR_NTP par l'adresse IP du serveur NTP si ce protocol est # utilisé # ###################################### # -A UDP_OUT -p udp -m udp -j STATEFUL -A UDP_OUT -d 192.168.0.254 -p udp -m udp --dport 53 -j ACCEPT ## Remplacer SERVEUR_DNS1, SERVEUR_DNS2 et SERVEUR_NTP par les bonnes valeurs ##-A UDP_OUT -d SERVEUR_DNS1 -p udp -m udp --dport 53 -j ACCEPT ##-A UDP_OUT -d SERVEUR_DNS2 -p udp -m udp --dport 53 -j ACCEPT ##-A UDP_OUT -d SERVEUR_NTP -p udp -m udp --dport 123 -j ACCEPT ## Dans le cas surprenant ou le serveur serait en DHCP ## -A UDP_OUT -p udp -m udp --sport 68 --dport 67 -j ACCEPT # COMMIT #
❶ ❸ ❻ | Ajouter dans cette partie les règles permettant respectivement d'accepter les paquets icmp/tcp/udp en entrée. Les paquets retour de connexions initiées par la machine sont normalement pris en charge par la chaîne stateful et ne nécessitent pas d'être explicitement autorisés ici. |
❷ ❹ ❼ | Ajouter dans cette partie les règles permettant respectivement d'accepter les paquets icmp/tcp/udp en sortie. Les paquets retour de connexions initiées par des clients sont normalement pris en charge par la chaîne stateful et ne nécessitent pas d'être explicitement autorisés ici. |
❺ |
Les paquets TCP qui ne sont pas liés à une connexion existante (donc qui ne sont pas
matchés par la chaîne |
La chaîne TCP_INLIMITS
permet d'appliquer des règles de
limitation de trafic avant tout traitement. On l'utilisera plus loin pour protéger
des services mais on peut déja limiter globalement le taux des connexions entrantes.
Nous utiliserons le module iptables ipt_limit
pour cela. Ce système de limitation fonctionne comme un sac de billes.
Dans les règles ci-dessous, chaque ouverture de connexion consomme une bille.
Lorsque le sac sera vide, la connexion sera rejetée pour prévenir l'expéditeur
et il faudra attendre que le sac se remplisse avant de pouvoir en accepter une autre.
Les paramètres --limit-burst
et
--limit
d'ipt_limit
controllent respectivement le nombre de billes maximum que la sac contient
(on commence avec un sac plein) et la vitesse de remplissage de ce sac. Par exemple,
si l'on souhaite avoir un « sac » de 10 connexions, se rechargeant à la vitesse
d'un par seconde, nou utiliserions :
# # ###################################### # Limitation des connexions TCP entrantes # Les connexions trop nombreuses sont rejetées. # ###################################### # # Si la connexion est dans les limites fixées, on retourne d'ou l'on vient -A TCP_SYNLIMITS -p tcp -m tcp --syn -m limit --limit 1/sec --limit-burst 10 -j RETURN # # Sinon, on loggue # -A TCP_SYNLIMITS -m limit --limit 1/min -j LOG --log-prefix "TCP_SYNLIMITS:" --log-level 6 -A TCP_SYNLIMITS -j REJECT #
Ces règles pourront être sauvées dans le fichier
/etc/network/iptables
et chargées avant la configuration
de l'interface au boot grâce à la directive pre-up du
fichier de configuration /etc/network/interfaces
:
iface eth0 inet static address 192.168.0.33 netmask 255.255.255.0 gateway 213.245.116.99 pre-up iptables-restore < /etc/network/iptables
Pour modifier les règles, il suffit d'éditer le fichier
/etc/network/iptables
et de les réappliquer avec
iptables-restore < /etc/network/iptables
.
Attention, si les règles sont modifiées à distance (via ssh par exemple),
le pire est à craindre en cas d'erreur, d'autant plus que la probabilté qu'une
erreur se glisse dans vos règles est proportionelle à la distance qui vous sépare de la machine.
Il vaut donc mieux prendre quelques précautions si l'on est pas sur place.
Dans ce cas, il sera préférable d'éditer un fichier temporaire
(/etc/network/iptables.test
par exemple).
Avant de tester ces règles avec
iptables-restore < /etc/network/iptables.test
,
il faudra programmer un reboot de la machine. Si l'application des règles ne
nous enferme pas à l'extérieur de la machine, nous pourrons annuler le reboot
et renommer le fichier en /etc/network/iptables
.
Dans le cas contraire, nous decrons attendre quelques minutes que la machine
redémarre avec son ancienne configuration de pare-feu avant de nous relogguer
et d'examiner le fichier pour y trouver l'erreur.
oper@ubuntu:~$sudo bash
root@ubuntu:~#shutdown -r +2 &
[1] 4227 root@ubuntu:~# Broadcast message from oper@ubuntu (/dev/pts/0) at 0:39 ... The system is going down for reboot in 2 minutes! root@ubuntu:~#iptables-restore < /etc/network/iptables.test
❶ root@ubuntu:~#shutdown -c
shutdown: Shutdown cancelled [1]+ Done shutdown -r +5 root@ubuntu:~#mv /etc/network/iptables.test /etc/network/iptables
root@ubuntu:~#logout
oper@ubuntu:~$
❶ | Après cette commande, si nous sommes figés ou éjectés de la machine, il ne reste plus qu'à attendre... |