lalahop
Categorie: Administration réseau

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.

Proxychains : le retour

Table des matières

Dans un de mes billets sur proxychains, j'indiquais n'avoir jamais eu de soucis avec ce logiciel. Il a fallu que j'écrive ça pour en avoir. Je vais apporter une solution aux deux problèmes que j'ai rencontré. Je vous conseille de lire le billet précédent afin de comprendre ce dont je parle ici.

ERROR: ld.so: object 'libproxychains.so.3' from LD_PRELOAD cannot be preloaded: ignored.

Voici à quelle erreur je suis maintenant confronté. La solution exposée sur nombre de forums, a savoir créer un lien symbolique ne fonctionne pas chez moi.

En observant un peu, en lançant un programme proxychainé depuis un shell par exemple, on remarque que cette erreur apparaît lors de la résolution des noms de domaine. On se demande alors si l'erreur ne viendrait pas du script chargé de la résolution des noms de domaine, /usr/lib/proxychains3/proxyresolv. On l'ouvre et on remarque cette ligne :

export LD_PRELOAD=libproxychains.so.3

Si on en croit les forums qui conseillent de créer un lien symbolique, il faut créer le-dit lien car le chemin vers la libproxychains.so.3 serait codé en dur dans le code de proxychains et dans proxyresolv. C'est le déclic : et si nous mettons le chemin absolu vers la libproxychains.so.3 dans le script proxyresolv, cela corrige-t-il le problème ?

On cherche l'emplacement de la libproxychains.so.3 :

sudo find / -name "libproxychains.so.3"

On la trouve dans le répertoire /usr/lib, quelle surprise.

On modifie donc proxyresolv en remplaçant :

export LD_PRELOAD=libproxychains.so.3

par

export LD_PRELOAD=/usr/lib/libproxychains.so.3

Nous venons de résoudre le premier problème. Je ne sais pas expliquer pourquoi cela ne fonctionne plus subitement du jour au lendemain.

Les requêtes DNS ne sont plus proxychainées

En effet, je me suis aperçu que proxyresolv s'adresse bien au serveur définit dans sa variable "DNS_SERVER" mais sans passer par le tunnel SSH. Merci à mon netfilter bien configuré qui a évité les fuites vers le réseau local.

J'ai donc pensé à faire une redirection du port local tcp/53 vers le port tcp/53 de mon résolveur DNS. Cette redirection s'ajoute à ma redirection dynamique. Concrètement cela donne :

sudo ssh -ND6666 -L53:resolveurDNSDNS:53 login@server

Ensuite, il suffit de définir 127.0.0.1 comme "DNS_SERVER" dans le script proxyresolv et le problème sera résolu.

Pour ceux qui ne veulent pas lancer ssh avec les droits root, il y a un moyen de déporter les droits root sur un autre programme :

sudo apt-get install socat
ssh -ND6666 -L5300:resolveurDNS:53 login@server
sudo socat TCP4-LISTEN:53,reuseaddr,fork TCP4:127.0.0.1:5300

Explication : proxyresolv va envoyer sa requête sur le port tcp/53. socat va la relayer sur le port 5300. SSH va récupérer la requête, la faire transiter dans le tunnel et l'envoyer sur le port tcp/53 de la machine resolveurDNS.

Au lieu d'utiliser socat, on peut se la jouer netfilter ce qui évite de devoir créer des scripts pour lancer socat automatiquement et de donner des droits inutiles a ssh/socat :

ssh -ND6666 -L5300:serveurDNS:53 login@server
sudo iptables -t nat -A OUTPUT -s 127.0.0.1 -d 127.0.0.1 -p tcp --dport 53 -j REDIRECT --to-port 5300

Pour ceux qui se demande pourquoi définir 127.0.0.1 comme résolveur DNS dans proxyresolv et non pas dans /etc/resolv.conf : d'une part car cela empêcherait ssh de résoudre le nom de la machine avec laquelle il doit communiquer, dans le cas où votre serveur ssh a une ip dynamique. D'autre part, les autres applications fonctionnent très bien donc pourquoi les sanctionner ?

Pour ceux qui s'étonne de voir le protocole DNS agir sur le protocole TCP, sachez que cela avait été prévu par les RFC. Les administrateurs de réseaux qui bloquent le port tcp/53 ne sont donc pas consciencieux/informés. Néanmoins, une requête de résolution doit être effectuée sur TCP uniquement dans le cas où la réponse excède 512 octets (mais la taille autorisé via UDP a été augmentée dans le RFC 2671 afin de prendre en charge DNSSEC).

Nous faisons donc une entorse aux règles en faisant passer toutes nos requêtes, même celles qui n'excèdent pas la taille maximale, par le port tcp/53. Relativisons en disant que d'une part, comme SSH permet le port forwarding uniquement sur le protocole TCP, nous sommes obligé de passer via TCP à un moment donné. D'autre part, dés que l'on tente de masquer son trafic et/ou de contourner un firewall, on transgresse forcement les standards (on se souviens du TCP over TCP du billet précédent).

Pour ceux qui veulent étudier une solution alternative : Tunnelling UDP packets through SSH. Vous constaterez que cette méthode impose aussi un passage via TCP et qu'elle est plus complexe à mettre en place et à automatiser.

SSH, redirection dynamique, proxychains et DNS

Table des matières

Ce billet fait suite à celui-ci : SSH : du port forwarding au VPN bon marché. J'y évoquais les problématiques de fuite des requêtes de résolution de nom via le protocole DNS. Mais je n'ai pas abordé un point sur lequel je m'interroge depuis quelques temps : je me demandais si les requêtes de résolution étaient envoyées au résolveur DNS défini dans le fichier /etc/resolv.conf du client SSH ou à celui défini dans le fichier du serveur SSH. Il paraît presque évident que c'est le résolveur définit sur le serveur qui sera utilisé.

Ayant sévèrement planté mon WRT54GL, j'en ai profité pour le flasher avec la déclinaison brcm2.4 d'OpenWRT. Cette déclinaison étant plus compact, j'ai pu installer tcpdump sur mon routeur et vérifier tout ceci.

Pourquoi ce test ?

Si votre ordinateur reçoit automatiquement l'adresse IP du résolveur DNS par le serveur DHCP du réseau insecure, que ce résolveur fait partit du réseau insecure et en admettant que les requêtes DNS sont envoyées à ce résolveur via votre serveur SSH, alors l'administrateur réseau du réseau insecure pourra faire un croisement entre l'adresse IP qui effectue les résolutions DNS sur le résolveur DNS interne (qui sera celle de votre serveur SSH) et l'adresse IP de destination du trafic SSH (là aussi, ce sera celle de votre serveur). Il pourra donc savoir quels noms de domaine vous avez résolus et par voie de conséquence, ce que vous avez fait sur internet. La redirection dynamique via SSH serait donc moins efficace. Comme déjà dit, il est peu probable que les résolutions de noms se fasse via le résolveur défini dans le fichier /etc/resolv.conf du client, mais cela ne coute rien de s'en assurer.

Mise en place du test

Évidemment, ce test ne sera pas effectué depuis un réseau dont vous n'avez pas confiance ...

D'abord, on installe tcpdump sur le WRT54GL :

opkg update
opkg install tcpdump

Sur la machine de test, on prépare un tsark/wireshark/tcpdump et un Firefox avec un proxy défini dans les paramètres et l'option network.proxy.socks_remote_dns à true dans le about:config.

Note : il n'est pas nécessaire de faire une capture du trafic réseau sur la machine de test si on est déjà convaincu que le trafic passera bien par le tunnel SSH. Je propose ici de faire une capture afin que les septiques puissent vérifier par eux-mêmes ce que je disais dans le billet sus-cité.

Ensuite, on définie un résolveur DNS différent sur le routeur et sur la machine.
Disons que sur la machine, nous utiliserons le résolveur d'OpenDNS : "nameserver 208.67.222.222" à mettre dans le fichier /etc/resolv.conf.
Disons que sur le routeur nous utiliserons le résolveur de Google : "nameserver 8.8.8.8" à mettre dans le fichier /etc/resolv.conf.

On redémarre ensuite dropbear (ou autre serveur SSH) sur le routeur :

/etc/init.d/dropbear/restart

On se déconnecte puis reconnecte au routeur via SSH, en activant la redirection dynamique.

On lance la capture, que nous appellerons capture R par la suite, sur le routeur (voici un tutoriel pour tcpdump):

tcpdump -i eth0.1 not port 22 -s0 -w capture.cap

Explication : tcpdump écoute sur l'interface wan (eth0.1), ne garde pas les messages correspondant à la session SSH, ne tronque pas les paquets et enregistre le trafic dans le fichier capture.cap.

On lance la capture, que nous appellerons capture M par la suite, sur la machine.

Dans Firefox, tentez d'accéder à n'importe quel site, de préférence un site sur lequel vous n'avez pas l'habitude d'aller.

On récupère la capture :

scp root@routeur:/root/capture.cap .

On analyse la capture avec Wireshark (vous pouvez bien sur le faire avec tsark/tcpdump. Dans ce cas là, inutile de rapatrier la capture sur votre machine) :

wireshark capture.cap

Résultat attendu

Capture M ne doit contenir que du trafic SSH et TCP (ack). Capture R doit contenir du trafic HTTP, TCP (ack) et du trafic DNS vers 8.8.8.8 et surtout pas vers 208.67.222.222.

Résultat obtenu

Exactement le résultat attendu. Firefox dirige les requêtes DNS vers le résolveur défini dans le fichier /etc/resolv.conf du serveur SSH au moment où le serveur SSH a été lancé sur la machine (d'où l'intérêt d'avoir redémarré le serveur SSH lors de la mise en place du test).

Le cas proxychains

Pour les applications ne prenant nativement pas en charge les proxy SOCKS et/ou le transfert des requêtes DNS vers ce proxy, il est, comme nous l'avons dit dans l'article sus-cité, nécessaire de faire appel à un wrapper SOCKS (proxychains, tsocks, torsocks, etc.). Le comportement envers les requêtes DNS dépendra du wrapper choisit.

Dans le cas de proxychains, les requêtes sont toutes envoyées au même résolveur : 4.2.2.2.

Vous pensez bien que je trouve cela intolérable : si j'ai installé un résolveur DNS sur mon WRT54GL, c'est pour l'utiliser (pour les raisons évoquées dans l'article linké).

Je me suis donc dis que ce résolveur devait bien être défini dans les sources de proxychains. J'avais dans l'idée de recompiler les sources après avoir changé l'adresse du résolveur utilisé.

J'ai donc téléchargé et décompressé les sources :

wget http://downloads.sourceforge.net/project/proxychains/proxychains/version%203.1/proxychains-3.1.tar.gz?r=&ts=1294602235&use_mirror=mesh
tar -xf proxychains-3.1.tar.gz

On cherche ensuite l'occurrence "4.2.2.2" dans les sources :

cd proxychains-3.1
find ./ -type f -exec bash -c "grep "4.2.2.2" {} && echo {}" \;

Find/grep trouvent un résultat dans le fichier ./proxychains/proxyresolv. On affiche son contenu :

cat ./proxychains/proxyresolv

Je vous quote le passage intéressant en vous invitant quand même à lire la suite du fichier qui est intéressante puisqu'elle permet de comprendre que proxychains résout les noms de domaine avec une méthode toute simple qui fait plaisir à voir :

#!/bin/sh
# This script is called by proxychains to resolve DNS names
 
# DNS server used to resolve names
DNS_SERVER=4.2.2.2

Tout devient clair : proxychains se sert de ce script shell pour résoudre les noms de domaine. Il doit suffire de changer le contenu de la variable DNS_SERVER pour changer de résolveur.

On se dit que ce script ne sera pas compilé et donc qu'il doit y en avoir un exemplaire sur notre machine qui sert au proxychains installé :

sudo find / -type f -name "proxyresolv"

Find nous trouve un résultat : /usr/lib/proxychains3/proxyresolv.

On modifie donc ce fichier en remplaçant 4.2.2.2 par l'adresse du résolveur DNS voulu. Pour ma part, ça sera le résolveur installé sur mon routeur donc 192.168.1.1 (adresse lan par défaut).

On refait ensuite le test effectué précédemment sauf qu'au lieu d'utiliser Firefox, on utilise un programme proxychainé ("proxychains programme").

On voit, dans la capture réseau de R, que c'est bien le résolveur déclaré dans le script /usr/lib/proxychains3/proxyresolv qui est utilisé. Pour ceux qui veulent une preuve de plus : pour peu que vous ayez lancé la commande depuis une console, vous pouvez lire quelquechose comme ça :

|S-chain|-<>-127.0.0.1:6666-<><>-192.168.1.1:53-<><>-OK"

SSH : du port forwarding au VPN bon marché

Table des matières

Quand je suis sur un réseau insecure (wifi, dont la charte précise que des contrôle techniques de bon usage du réseau peuvent être effectués sans préavis, etc. ), je sécurise mes connexions grâce à SSH et à ses fonctionnalités de port forwarding / tunnels. Dans cet article, je souhaite vous faire un retour sur mes découvertes plus ou moins récentes.

Voici une page web qui explique clairement le principe du port forwarding avec SSH et la différence entre une redirection locale (ssh -L) et une redirection distante (ssh -R) : Tynsoe projects.

La redirection locale

Au commencement, j'utilisais une redirection locale afin de rediriger ma navigation internet sur un proxy Squid se trouvant sur un serveur "at home". Donc une bête ligne de commande comme celle-ci suffisait :

ssh -NL8080:localhost:3128 login@host

Ensuite, je configurais mon navigateur pour utiliser le proxy HTTP/HTTPS suivant :

  • Hôte : 127.0.0.1
  • Port : 6666

Et dans les rares cas ou j'avais besoin d'un autre service, je transférais un autre port en même temps :

ssh -NL8080:localhost:3128 -L2200:serveurSSH:22 login@host

Je pouvais alors surfer tranquillement et me connecter à la machine serveurSSH via l'adresse 127.0.0.1:2200

La redirection dynamique

Ensuite, j'ai voulu utiliser plus de services (mail, messagerie instantané, etc.) et j'ai donc trouvé les limites de la redirection locale de ports (transférer tous ces ports à la mano c'est pas top). De plus, mon serveur proxy était en panne. Mais j'ai découvert la redirection dynamique (ssh -D). Du coup, c'est mon fidèle WRT54GL qui me sert de proxy.

Maintenant, une simple ligne de commande suffit pour avoir accès à tous mes services :

ssh -ND 6666 login@host

Ensuite, il faut définir un proxy SOCKS dans les paramètres de chaque application dont vous avez besoin :

  • Hôte : 127.0.0.1
  • Port : 6666

Remplacez 6666 par le port que vous avez choisi lors de l'initialisation de la connexion SSH.

Se pose une première problématique : que faire si l'application ne possède pas une telle option ?
Se pose ensuite une seconde problématique : certains applications n'utilise pas le proxy pour faire transiter certaines informations. C'est typiquement le cas de la résolution d'un nom de domaine via le protocole DNS. A quoi peut bien servir un tunnel si le réseau insecure arrive encore à voir une partie de notre trafic réseau ?

Ces deux questions trouvent la même réponse : il faut utiliser un programme qui capture les "appels réseau" d'un programme et force la redirection de ceux-ci dans le tunnel SSH. Il en existe plusieurs : tsocks, torsocks, proxychains, etc. . Il fait néanmoins faire attention : certains laisse encore passer les fameuses requêtes DNS entre les mailles de leur filet. Personnellement, j'utilise proxychains et je n'ai jamais rencontré de problèmes.

EDIT 09/01/2011 19h35 : Il faut dire aussi que mon choix pour proxychains a été influencé par les différents inconvénients qui apparaissent sur les alternatives :

  • La version de tsocks disponible sur sourceforge laisse fuiter les résolutions DNS, à moins d'appliquer un patch. De plus, je rencontre beaucoup d'erreur de segmentation avec des applications courantes
  • Torsocks me sort une erreur de segmentation pour quelques uns de mes programmes. Néanmoins, il y a quand même des progrès, par rapport à tsocks, sur ce point là.
  • Dsocks me paraissait un peu "brouillon" avec son script python servant de forwardeur DNS. De plus, le script ne fonctionne pas et me lève des exceptions.
  • Socat est une alternative que je n'ai pas essayé mais qui me parait plus que correcte mais lourde à utiliser.

Le fichier de configuration (/etc/proxychains.conf) est d'une simplicité enfantine. Voici le mien (j'ai supprimé quelques commentaires mais je vous invite à les lire dans votre fichier) :

# Strict - Each connection will be done via chained proxies
# all proxies chained in the order as they appear in the list
# all proxies must be online to play in chain
# otherwise EINTR is returned to the app
strict_chain

# Quiet mode (no output from library)
quiet_mode

# Proxy DNS requests - no leak for DNS data
proxy_dns

[ProxyList]
socks5 127.0.0.1 6666

Il ne vous reste plus qu'à utiliser proxychains :

proxychains firefox
proxychains thunderbird
proxychains filezilla
proxychains pidgin
...
sudo proxychains apt-get update/install

Il ne vous reste plus qu'à modifier les raccourcis de votre menu (Gnome, KDE, etc.) afin d'inclure proxychains dans la commande à lancer pour les programmes dont vous souhaitez qu'il passent via le tunnel SSH.

A noter quand même que certains logiciels jouent le jeu et ne laisse pas filer les requêtes DNS, même sans utiliser proxychains. C'est le cas par exemple de Firefox, une fois la valeur "true" passée à l'option booléenne "network.proxy.socks_remote_dns" dans le about:config. Par contre, je n'ai pas constaté d'améliorations en faisant la même manipulation sous Thunderbird.

EDIT 09/01/2011 22h00 : Pour plus d'information sur le transfert des requêtes DNS via un proxy SOCKS, voir cet article : SSH, redirection dynamique, proxychains et DNS.

Vous comprenez que votre meilleur ami ici sera Tsark/Wireshark (ou n'importe quel autre programme d'analyse du trafic réseau), afin de tester qu'aucun flux transite en clair. Il apparait évident de procéder à ces tests avant d'aller dans le réseau insecure. Un iptables correctement paramétré permet également d'éviter les fuites causées par les démons ou par les programmes que l'utilisateur aurait oublié de proxyfier. Voir ce billet à ce sujet : Configuration rapide d’iptables pour les réseaux insecures.

ÉDIT 23/02/2011 22h45 :

Lorsque vous n'utilisez pas votre tunnel SSH, la connexion est fermée automatiquement. Pour maintenir la connexion, il suffit de se rappeler de la notion de keepalive.

Encore une fois, je ne réinventerai pas la roue puisqu'une recherche sur Google vous conduira sur ce blog : Aaron Toponce : Keeping Your SSH Connection Alive.

J'ajouterai cependant que vous pouvez définir le paramètre "ServerAliveInterval" dans le fichier /etc/ssh/ssh_config, ce qui évite de le répéter lors de chaque connexion. Personnellement, je définis un intervalle de 5 minutes soit 300 secondes : c'est amplement suffisant pour maintenir la connexion.

Si toute cette méthode vous semble encore trop contraignante, il vous reste encore la possibilité de vous monter un VPN avec OpenVPN. Je me suis penché sur cette alternative mais, malheureusement, mon WRT54GL n'a pas assez d'espace libre pour accueillir la libssl nécessaire au fonctionnement d'OpenVPN. Donc je n'ai pas creusé le sujet plus loin. Néanmoins, excellents tutoriel existent sur le net.

Un VPN bon marché avec SSH

Il y a quelques jours, je me disais qu'il devait y avoir un moyen de s'amuser avec SSH et les interfaces tun/tap. Je me tourne vers mon ami Google afin d'avoir une idée sur la marche à suivre. Je découvre alors que d'autres personnes ont eu mon idée depuis bien longtemps et notamment les développeurs d'openSSH qui permet, depuis sa version 4.3, de créer un tunnel de niveau 3 grâce aux interfaces virtuelles tun. Si j'étais un "artiste" ou un patent troll, je porterai plainte ... oser me voler mon idée que je n'avais pas à l'époque, c'est quand même scandaleux ! 😉 .

Voici les tutoriels qui m'ont éclairés sur le sujet :

J'ai évidemment voulu tester tout ça, en utilisant la méthode manuelle (sans ifup), avec une machine virtuelle, avant une mise en production sur mon WRT54GL. Il n'y a aucune difficulté et ça fonctionne très bien (avec la machine virtuelle).

Malheureusement, dropbear, le serveur ssh allégé fourni par défaut avec OpenWRT, ne supporte pas ce type de tunnel. Installer OpenSSH sur mon routeur pose le même problème que celui de l'installation d'OpenVPN : la nécessaire libssl est trop lourde.

Donc je suis contraint d'utiliser un tunnel dynamique pour sécuriser mes connexions depuis un réseau insecure. Pour l'instant, cela me convient. Mais il faudra quand même que je songe à greffer une carte mémoire à mon WRT54GL.

Une dernière chose : comme signalé dans les commentaires de Debian Administration ou sur la page de la Community Ubuntu Documentation, la méthode de VPN over SSH n'est pas la meilleure solution du fait que le protocole SSH repose sur TCP et que l'on va faire circuler des trames TCP dans ce tunnel. Cette solution est impeccable quand il n'y a pas de problèmes sur le(s) réseau(x) traversé(s), mais dans le cas d'une perte de paquet, on assiste à un effondrement interne de la connexion. C'est pour cela qu'il veut mieux utiliser un VPN basé sur OpenVPN, configuré en UDP, bien entendu.

Je vous invite à lire ces deux documents, le deuxième étant la traduction, en français, du premier, afin d'avoir plus d'informations sur le sujet :

Note : dans cet article, je me préoccupe des systèmes UNIX/UNIX-like. Mais sous Windows, PuTTY permet la redirection locale, distante et dynamique. Ensuite, il doit exister des équivalents de proxychains, torsocks. Enfin, cygwin doit permettre de monter un VPN over SSH mais je ne me suis pas renseigné sur ce point.

SSH : X11 forwarding

Table des matières

SSH permet d'obtenir bien plus qu'un simple shell distant sécurisé. Entre autre, il permet de déporter, de manière sécurisée bien entendu, l'affichage de toute application graphique, même un bureau complet. Je ne suis pas un fan de l'utilisation d'un serveur X sur une machine ayant un rôle de serveur mais, pourtant, des utilisateurs m'ont récemment demandé la possibilité d'avoir un accès graphique complet à un serveur sous Fedora.

Connexion simple et directe

Par connexion simple et directe, j'entends que le client se connecte directement au serveur, sans serveur SSH intermédiaire.

Voici un schéma pour bien comprendre (source : kuxon.org) :
schema connexion SSH simple et directe

Préparatifs

Si votre client est sous GNU/Linux

Mode X11Trusted ou mode X11Untrusted ?
Pour tout savoir du mode X11 trusted/untrusted, je vous recommande ce site : HSC : SSH et redirection X11. Je vous conseille également la lecture du site suivant, plus récent et peut-être plus accessible : Wednesday Why: Trusted and Untrusted X11 Forwarding with SSH.

Pour résumer : "It should be possible to run almost all clients as untrusted, leaving the trusted category for screencapture and screencast programs, macro recorders, and other specialized utilities." (cf : deuxième lien donné ci-dessus). Il convient donc d'utiliser le mode untrusted (ssh -X) plutôt que trusted (ssh -Y) afin d'augmenter un peu la sécurité.

Mais c'est plus compliqué que cela. Si la ligne "ForwardX11Trusted yes" est présente dans votre fichier ssh_config (coté client donc), alors ssh -X revient au même que ssh -Y : cela correspond au mode trusted. Veuillez donc a ce que la directive "ForwardX11Trusted" soit bien à "no" dans le fichier ssh_config du client et utilisez l'argument -X de SSH sauf si le client X11 n'est pas compatible avec le mode untrusted auquel cas, utilisez l'argument -Y.

Il n'y a rien a installer. Il ne reste plus qu'à vous connecter à votre serveur SSH :

ssh -X login@host
Si votre client est sous Windows

PuTTY permet également le X11 forwarding. Il faut aller dans le menu "SSH" puis dans "X11". Il faut cocher la case "Enable X11 forwarding". Pour choisir le protocole d'authentification en tout connaissance de cause, je vous conseille la lecture de ce site : Configuring PuTTY.

Ensuite, il vous faut une implémentation du serveur X pour Windows. A ma connaissance, il y a Xming ou Cygwin/X.

Ensuite, il ne vous reste plus qu'à initier la connexion au serveur SSH (bouton "Open").

Utiliser des applications graphiques

Forwarder quelques applications

Que votre client soit sous GNU\Linux ou sous Windows, vous pouvez lancer un programme graphique en utilisant cette syntaxe (dans le shell distant bien sûr) :

programme >/dev/null 2>&1 &

Par exemple :

firefox >/dev/null 2>&1 &

Explications : ">/dev/null 2>&1" permet de rediriger la sortie standard et la sortie d'erreur vers /dev/null afin de ne pas être saturé par des messages de debug/erreur. "&" permet de mettre l'application en arrière plan et donc de libérer le terminale et donc de pouvoir taper d'autres commandes (qu'elles lancent un programme graphique ou pas).

Forwarder un environnement graphique complet

Je pense que ce n'est pas la meilleure solution. Cela surcharge le trafic réseau inutilement car vous savez très bien les applications que vous voulez lancer et vous pouvez donc les lancer avec la méthode précédente. Si vous tenez à utiliser cette solution, activer au moins la compression de la connexion, lors de la connexion au serveur SSH, avec l'argument -C de ssh :

ssh -XC login@host

Avec gnome :

gnome-session >/dev/null 2>&1 &

Avec KDE (je n'ai pas testé puisque notre serveur Fedora est équipé de Gnome) :

startkde >/dev/null 2>&1 &

Là aussi, peu d'importance que le client soit sous un Unix ou sous un Windows.

Connexion via un serveur SSH intermédiaire

Il peut arriver que le serveur sur lequel on veut récupérer l'affichage soit derrière un pare-feu et que le seul moyen de traverser le pare-feu soit un autre serveur SSH.

Voici un schéma pour mieux comprendre ce qui va suivre (source : kuxon.org) :
schema connexion SSH complexe

En tout cas, seul l'étape de connexion au serveur change, le reste est identique. Le choix entre le mode trusted/untrusted reste d'actualité, avec les même enjeux qu'avec une connexion SSH directe.

On se connecte d'abord au serveur SSH qui sert de relai (serveur A sur l'image d'illustration) en activant le port forwarding local :

ssh -L2200:serveurB:22 loginA@serveurA

Puis on se connecte au serveur B, en activant le X11 forwarding :

ssh -X loginB@127.0.0.1 -p 2200

Cette commande est à taper sur le client, pas dans le shell distant de A, évidemment. loginA est le login sur le serveur A et loginB ... le login sur serveurB.

Et si on avait 2 serveurs intermédiaires ?

Le principe reste le même :

ssh -L2200:serveurB:22 loginA@serveurA
ssh -L22000:serveurC:22 loginB@127.0.0.1 -p 2200
ssh -XC loginC@127.0.0.1 -p 22000

Par contre, dans ce cas précis, l'empilement des couches réseau est abominable ... mais on a pas tous les jours besoin de se connecter en mode graphique et encore moins en passant via 2 serveurs intermédiaires.

Une dernière chose que vous devez vous demander : pourquoi avoir utiliser les images du site kuxon.org et ne pas appliquer la méthode que ce site propose ? Tout simplement parce que je n'ai pas réussi à faire fonctionner la méthode présentée par ce site.

Je signale également que l'idée d'utiliser le port forwarding afin de déporter l'affichage à travers un deuxième serveur SSH intermédiaire m'est venue à la consultation de cette page web où cette méthode y est écrite, succinctement : UNIX One-Liners.