lalahop

Configuration rapide d’iptables pour les réseaux insecures

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.

Les commentaires sont fermés