Table des matières
Nous allons voir, rapidement, comment configurer le noyau, avec iptables, lors de l'utilisation d'un réseau insecure. L'objectif premier est d'éviter la fuite de trafic réseau en dehors de notre tunnel SSH.
Savoir quels flux doivent circuler
En entrée
- Tous les paquets appartenant à une connexion que nous avons ouverte doivent pouvoir entrer.
- Tous les paquets relatifs à une connexion que nous avons ouverte doivent pouvoir entrer (c'est notamment le cas de certains messages d'erreur ICMP).
- Facultatif : les paquets DHCP doivent pouvoir passer si vous utilisez ce protocole.
- Tous les paquets en provenance de la boucle locale (127.0.0.1) doivent pouvoir passer (le port forwarding via SSH fait partit de cette catégorie dans le sens où SSH doit être autorisé à recevoir des paquets sur un port précis (6666 dans mon cas)).
En sortie
- Tous les paquets à destination de la boucle locale (127.0.0.1) doivent pouvoir passer (là aussi, le port forwarding via SSH fait partit de cette catégorie dans le sens où les programmes doivent être autorisés à émettre en direction de ce port et SSH doit être autorisé à émettre depuis ce port vers les programmes).
- Tous les paquets à destination du port tcp/443 (https) du serveur web hébergeant le portail captif doivent passer à condition que le programme soit cURL (afin de faire de la connexion automatique, nous y reviendrons dans un prochain billet).
- Tous les paquets à destination du port 53 (tcp ou udp, voir billet précédent) du résolveur DNS du réseau insecure doivent passer à condition que le programme émetteur soit ssh ou scp (afin de résoudre le nom de domaine de votre serveur SSH).
- Facultatif : les paquets DHCP doivent pouvoir passer si vous utilisez ce protocole.
- Tous les paquets à destination du port tcp/22 sont autorisés si ils sont émis par ssh.
Comment savoir quel est le programme émetteur ?
Je suis certain que d'autres outils sont plus adaptés néanmoins je souhaite utiliser uniquement iptables dans le cas suivant.
La théorie
Iptables possède un module "owner" qui permet de réaliser un traitement sur un paquet selon l'uid ou le gid du programme qui a créé la socket. Sur les noyaux non SMP destinés aux systèmes n'ayant qu'un seul processeur avec un seul core, iptables propose d'autres options : traiter un paquet selon le pid du programme qui a ouvert la socket, le sid ou encore selon le chemin du programme sur le disque (cmd-owner).
Cette dernière option est celle qu'il nous faut. Mais comme je suis sur un noyau SMP, elle n'est pas présente. Il va donc falloir ruser.
Il faudrait que nos programmes aient un uid ou un gid unique qui permettrait de les reconnaitre ... bingo ! On va utiliser le setgid bit ! On aurait pu utiliser le setuid bit mais je pense que le gid bit aurai moins d'effets collatéraux.
Nous allons créer un groupe que nous appellerons "net". Nous allons ensuite modifier le groupe auquel appartient l'exécutable pour qu'il corresponde a "net". Puis nous activerons le setgid bit. Comme ça, le programme ne sera plus lancé avec le groupe de l'utilisateur qui le lancera mais bien avec le groupe "net". Iptables pourra donc reconnaitre ses sockets et laisser passer ses paquets.
Limites de cette méthode :
- Elle est fastidieuse : pour chaque programme dont nous voulons qu'il accède à un service réseau, il faut changer son groupe d'appartenance et activer le setgid bit.
- À chaque fois qu'un programme autorisé est mis à jour, le chown/chmod est perdu et le programme se retrouve donc bloqué par iptables. Il faut donc regarder attentivement son apt-get upgrade et s'en souvenir le jour où le programme ne marche plus.
- Dans le cas où il y a plusieurs utilisateurs sur la machine, des utilisateurs peuvent laisser fuiter des informations en compilant un programme et en changeant le groupe d'appartenance et en leur activant le setgid bit. Pour ceux qui en doute, rappelons qu'il y a deux personnes autorisées à utiliser chmod et chown sur un fichier donné : root et le propriétaire du fichier. Il faut quand même relativiser ce point là.
La pratique
On créer le groupe :
sudo groupadd net
|
Pour chaque programme que vous voulez autoriser :
Il faut d'abord trouver le chemin vers l'exécutable de ce programme (ici scp) :
whereis -b scp |
Ensuite, il faut changer le groupe du fichier :
sudo chown root:net /usr/bin/scp |
Puis, il faut activer le setgid bit :
sudo chmod g+s /usr/bin/scp |
Écrire les règles iptables correspondantes
En entrée
Tous les paquets appartenant à une connexion que nous avons ouverte doivent pouvoir entrer.
Tous les paquets relatifs à une connexion que nous avons ouverte doivent pouvoir entrer.
sudo iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT |
Facultatif : les paquets DHCP doivent pouvoir passer si vous utilisez ce protocole.
sudo iptables -A INPUT -p udp -m state --state NEW -m udp --sport 67 --dport 68 -j ACCEPT |
Tous les paquets en provenance de la boucle locale (127.0.0.1) doivent pouvoir passer
sudo iptables -A INPUT -i lo -j ACCEPT |
Tout le reste est ignoré :
sudo iptables -P INPUT DROP sudo iptables -P FORWARD DROP |
En sortie
Tous les paquets à destination de la boucle locale (127.0.0.1) doivent pouvoir passer.
sudo iptables -A OUTPUT -d 127.0.0.1/32 -j ACCEPT |
Tous les paquets à destination du port 443 (https) du serveur web (10.2.2.1) hébergeant le portail captif doivent passer à condition que le programme soit cURL.
sudo iptables -A OUTPUT -d 10.2.2.1/32 -p tcp -m tcp --dport 443 -m owner --gid-owner net -j ACCEPT |
Tous les paquets à destination du port 53 du résolveur DNS (10.2.2.4) du réseau insecure doivent passer à condition que le programme émetteur soit ssh ou scp.
sudo iptables -A OUTPUT -d 10.2.2.4/32 -p udp -m udp --dport 53 -m owner --gid-owner net -j ACCEPT sudo iptables -A OUTPUT -d 10.2.2.4/32 -p tcp -m tcp --dport 53 -m owner --gid-owner net -j ACCEPT |
Tous les paquets à destination du port 22 sont autorisés si ils sont émis par ssh.
sudo iptables -A OUTPUT -p tcp -m tcp --dport 22 -m owner --gid-owner net -j ACCEPT |
Facultatif : les paquets DHCP doivent pouvoir passer si vous utilisez ce protocole.
sudo iptables -A OUTPUT -p udp -m state --state NEW -m udp --sport 68 --dport 67 -j ACCEPT |
Tout le reste est ignoré :
sudo iptables -P OUTPUT DROP |
Conserver les règles
Pour sauvegarder les règles :
sudo iptables-save > reglesiptables |
Voici ce que l'on obtient pour ce billet :
# Generated by iptables-save v1.4.12.2 on Tue Apr 3 16:52:20 2012 *filter :INPUT DROP [36146:2386529] :FORWARD DROP [0:0] :OUTPUT DROP [224:17520] -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT -A INPUT -i lo -j ACCEPT -A INPUT -p udp -m state --state NEW -m udp --sport 67 --dport 68 -j ACCEPT -A OUTPUT -d 127.0.0.1/32 -j ACCEPT -A OUTPUT -d 10.2.2.1/32 -p tcp -m tcp --dport 443 -m owner --gid-owner 1001 -j ACCEPT -A OUTPUT -d 10.2.2.4/32 -p udp -m udp --dport 53 -m owner --gid-owner 1001 -j ACCEPT -A OUTPUT -d 10.2.2.5/32 -p udp -m udp --dport 53 -m owner --gid-owner 1001 -j ACCEPT -A OUTPUT -d 10.2.2.4/32 -p tcp -m tcp --dport 53 -m owner --gid-owner 1001 -j ACCEPT -A OUTPUT -d 10.2.2.5/32 -p tcp -m tcp --dport 53 -m owner --gid-owner 1001 -j ACCEPT -A OUTPUT -p tcp -m tcp --dport 22 -m owner --gid-owner 1001 -j ACCEPT -A OUTPUT -p udp -m state --state NEW -m udp --sport 68 --dport 67 -j ACCEPT COMMIT # Completed on Tue Apr 3 16:52:20 2012 |
Pour les restaurer avant de vous connecter physiquement au réseau insecure, il faudra taper :
sudo iptables-restore < reglesiptables |
Charger les règles au boot
ÉDIT du 30/12/2013 à 12h30 : Cette partie est inutile : iptables-persistent est disponible dans les dépôts Debian et fait le job. Fin de l'édit
Cela peut être utile si vous passez plus de temps sur un réseau insecure que sur un réseau secure.
Copions les règles sauvegardées par iptables-save dans /etc/init.d :
sudo cp reglesiptables /etc/init.d/reglesiptables |
Créons un script /etc/init.d/loadIptables :
sudo vi /etc/init.d/loadIptables |
Donnons-lui le contenu suivant :
#!/bin/bash ### BEGIN INIT INFO # Provides: loadIptables # Required-Start: # Required-Stop: # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # X-Interactive: false # Short-Description: Start/stop iptables rules ### END INIT INFO iptables-restore < /etc/init.d/reglesiptables |
Donnons les droits d'exécution à ce script :
sudo chmod +x /etc/init.d/loadIptables |
Demander l'exécution de ce script au boot :
sudo update-rc.d loadIptables defaults
|
Une autre manière de faire serait d'utiliser le fichier /etc/network/interfaces et l'option "pre-up". Exemple :
allow-hotplug eth0 iface eth0 inet dhcp pre-up iptables-restore < /etc/network/reglesiptables |
Lisez le man interfaces pour plus d'informations.
A propos d'IPv6
Si vous voulez être sûr qu'aucune fuite n'aura lieu, vous pouvez configurer le noyau dans ce sens. Le principe est le même que celui exposé ci-dessus à l'exceptions des commandes (iptables => ip6tables, iptables-save => ip6tables-save, etc.) et des IP, évidemment 😉 .
Exemple de fichiers ip6tables :
# Generated by ip6tables-save v1.4.12.2 on Tue Apr 3 16:56:30 2012 *filter :INPUT DROP [16:1280] :FORWARD DROP [0:0] :OUTPUT DROP [18:1152] COMMIT # Completed on Tue Apr 3 16:56:30 2012 |
Notes
- Certaines parties de ce billet sont inspirées (voir plus) de la documentation Ubuntu francophone : Iptables.
- La configuration proposée ci-dessus n'est pas très agressive puisqu'en définitive tous les programmes qui feront partit du groupe "net" et qui auront le setgid bit d'activé, auront accès aux ports tcp/443, tcp/22, udp/53, tcp/53 même si ils n'en ont pas besoin. On aurait pu être plus agressif et créer un groupe pour cURL et un groupe pour ssh/scp afin que cURL n'ai accès qu'aux ports tcp/443, udp/53 et tcp/53 et ssh/scp uniquement aux ports tcp/22, udp/53 et tcp/53. Néanmoins cela complexifie les règles iptables pour un gain en sécurité pas franchement évident.
- Certains d'entre vous remarquerons que les règles iptables sont incomplètes : il manque par exemple la gestion des messages d'erreur ICMP. Je sais que ce n'est pas l'idéal d'ignorer les paquets ICMP mais néanmoins je suis pas le seul puisqu'après vérification, la passerelle de mon réseau insecure bloque les paquets ICMP. Donc, sur le réseau, il ne reste plus que les paquets ICMP des machines faisant parties du réseau insecure, mais comme je ne communique jamais avec ces machines, j'ai peu de chance qu'elles m'envoient un message ICMP utile.