Guide de survie TCP/IP : OpenSSH, Netcat et Socat

Portrait de fira

Vous avez probablement déjà été dans une situation plutôt inconfortable où votre environnement joue contre vous. Besoin de transférer un fichier entre deux machines pas accessibles directement ou sans utiliser SCP... SSH sur une machine au ClubNix pour imprimer un rapport depuis le Wi-Fi de l'ESIEE... Accéder à un site web sur port 8443 (la planif!) alors que seul le 80 est ouvert... Connecter entre elles deux machines derrière des NATs différents... Devoir streamer de la vidéo en urgence sur un port bloqué par le hostspot du coin à travers un VPN SSH... Bref, n'importe quelle situation complètement loufoque et improbable d'un jour lambda et parfaitement normal.

La bonne nouvelle, c'est que la plupart des outils pour résoudre ce genre de problèmes existent déjà sur votre distribution *nix préférée !
Mieux encore, dans la plupart des cas, un simple accès SSH est plus que suffisant pour faire à peu près n'importe quoi.
Nous allons voir comment utiliser quelques unes des fonctionnalités de ces outils pour vous simplifier la vie, et pourquoi pas apprendre quelques trucs au passage !

Les outils

OpenSSH

OpenSSH est l'implémentation standard de SSH fournie sur la plupart des distributions Linux. Dans la forme que tout le monde connait, on peut l'utiliser pour simplement ouvrir un shell sur une machine distante par un tunnel chiffré. Ce qui peut être un peu plus intéressant est que ce tunnel chiffré peut aussi servir de base pour beaucoup d'autres choses, par exemple de la redirection de ports, fenêtres, applications, ou même mettre en place un tunnel IP, donc une sorte de VPN.
Pour une utilisation de base de SSH, il suffit d'indiquer l'utilisateur et hôte distant séparés d'une arobase.

Netcat

Le "Couteau suisse TCP/IP", netcat permet de manipuler des flux de données par TCP ou UDP, et peut être très utile pour des scripts ou utilisé en conjonction avec d'autres applications. Il en existe deux implémentations communes, celle de OpenBSD et celle de GNU, qui varient légèrement d'un point de vue de la syntaxe. J'utilise ici la version d'OpenBSD.
La syntaxe de base est d'indiquer à quel hôte distant et port se connecter, en indiquant si nécessaire des options de connexion en plus.

Socat

Une sorte de remake de Netcat avec beaucoup plus de fonctionnalités, mais moins courant. Il permet d'effectuer des opérations plutôt complexes en juste une ligne. Plus d'informations sur la page officielle.
Socat est beaucoup plus complexe que les deux autres outils, mais fonctionne globalement en spécifiant deux flux (qui peuvent être des fichiers, connexions, sockets, ou autre..), et en transférant des données de l'un à l'autre.

Les protocoles

Avant de commencer, faisons un rapide point sur les protocoles couramment utilisés que nous allons manipuler. Il est beaucoup plus simple de comprendre le fonctionnement des outils en sachant comment ils communiquent.

IP aka. Internet Protocol

Le protocole le plus utilisé sur Internet, IPv4 (ou v6) permet d'envoyer des données d'une machine à une autre en utilisant leur adresse IP (par exemple, 147.215.81.100 pour le club !). Il suffit d'envoyer un message IP à votre routeur, et il se chargera de l’emmener au bon endroit. Ici, c'est tout ce qui nous intéresse.

TCP

La plupart du temps, on ne veut pas envoyer un seul message, mais établir une connexion pour communiquer. C'est ce que fait TCP : il permet de négocier plusieurs connexions avec des machines distantes pour leur envoyer des données.
Le premier axe important est le multiplexage : plusieurs applications peuvent vouloir communiquer en même temps ! Pour cela on utilise des numéros ports (jusqu'à 65535) : chaque connexion porte un port source aléatoire, et un de destination. Ainsi votre OS peut différencier la connexion au site web du club (vers le port 80 ou 443) de celle au serveur Jabber (port 5222).

L'autre point important est la négociation de connexion. Ceci est fait en utilisant le three-way handshake (ou triple poignée de main) : le client envoie une demande de connexion au serveur, qui lui répond pour la valider, avant de renvoyer un message pour confirmer qu'elle est établie. C'est sur ce principe que les pare-feu (surtout NAT) reposent pour ne permettre que les connexions sortantes. Le protocole TCP impose aussi d'autres contraintes, par exemple en demandant confirmation de la réception des messages, contrôlant le flot d'envoi des messages, ou vérifiant leur intégrité et ordre de réception, mais ce n'est pas très important pour nous pour l'instant.

UDP

TCP fait bien son travail, mais pour certaines applications, par exemple les jeux en réseau, ou la vidéo et la voix, il en fait trop !
UDP est beaucoup plus simple, et ne permet que le multiplexage (par les numéros de ports) et la vérification des messages (un checksum). C'est un protocole dit sans-état : la connexion n'est pas explicitement ouverte ou fermée, on envoie juste des informations. Les applications peuvent ensuite gérer ces messages comme elles l'entendent.

Quelques commandes pratiques de base

Copier des fichiers avec OpenSSH

Scénario : Vous faites des changements en local dans votre IDE et voulez les envoyer sur un serveur.
La commande SCP marche plus ou moins comme un simple cp : source, puis destination, avec des options similaires. Il faut juste indiquer l'utilisateur et nom d'hôte en plus, et la copie est faite a partir de la Home.

Exemples:

	# Copie le fichier /home/fira/stuff.conf vers /etc/stuff.conf sur le serveur:
	scp ~/stuff.conf root@serveur:/etc/
	# Récupère le dossier public_html de votre home sur le serveur distant:
	scp -r fira@serveur:public_html/ /srv/backup/siteweb/

Forwarding X11 par SSH

Scénario : Vous êtes dans un des clubs de l'ESIEE et devez imprimer votre rapport de TP à rendre pour hier. Pas de chance, les 5 salles infos de 50xx sont prises ! Il va falloir aller jusque là-bas et expliquer à Bureau que vous voulez squatter sa salle pour imprimer un rapport comme les 5 autres boulets qui attendent devant l'imprimante.
Solution : imprimons-le d'ici. En bonus, il sera déjà prêt en arrivant devant l'imprimante.

Q: Hein ?! COMMENT ?! Quelle est cette sorcellerie ?
C'est simple : vous avez déjà accès aux postes par SSH.
OpenSSH possède une option qui permet de faire passer le canal de données de X11, qui gère l'affichage, dans le tunnel. Ainsi l'affichage de l'application sera transporté directement jusqu'à une fenêtre sur votre machine.
Bien sur, on pourrait imprimer le rapport directement depuis la ligne de commande mais.. Franchement, qui veut lire la doc ?

	# Utiliser -Y demande le transport du canal X11
	fira@clubnix$ ssh -fY loyauf@gemini2.esiee.fr firefox

Q: Hein? C'est tout?
Oui. Cette commande lancera "firefox" sur gemini2, en nous faisant parvenir l'interface graphique à travers le tunnel.
Q: Et, si j'ai bien compris, ça marche pour virtuellement n'importe quelle application ?
Oui.
Q: Mais c'est génial !
Oui !

Attention cependant.
En utilisant cette option, la connexion graphique est faite directement à travers le tunnel, ce qui est pratique mais pas forcément des plus adaptés. Pour des connexions lentes, ou par Internet de particulier, cela risque d'être très lent, et peut même planter toute la connexion (les paquets envoyés par SSH sont prioritaires en termes de QoS).
Il existe pour ces cas là d'autres alternatives tels les VNC ou RDP (qui requièrent un serveur spécifique), NoMachine NX (qui requiert une installation globale et un serveur SSH), ou xpra (qui peut s'utiliser à travers SSH, en simple utilisateur, et intègre directement les fenêtres sans bureau virtuel. Magique.)

Proxy avec OpenSSH

Scénario : vous avez besoin d'accéder à un site web ou une application qui est bloqué depuis votre point d'accès (par exemple, accéder à un site interne depuis chez soi), mais avez un accès SSH sur une des machines qui peut, elle, lui accéder.

Rien de plus simple:

 
	# Crée un proxy SOCKS sur le port 9050
	fira@laptop$ ssh -NfD 9050 fira@clubnix.fr

Les options -Nf lancent ssh en arrière plan, ce qui permet de fermer la fenêtre. Pour l’arrêter, il faudrait ensuite le killer, par exemple avec htop ou autre. L'option -D crée un proxy SOCKS4 à travers le tunnel SSH : il faut alors configurer l'application pour utiliser "localhost" comme proxy sur le port 9050.
Par exemple pour Firefox, en suivant : Editer > Préférences > Avancé > Réseau > Connexion > Préférences - cocher "Configuration manuelle", entrer le nom d'hôte et port sur la ligne SOCKS. Voilà.

Reverse SSH ou passage de port inversé

Scénario : vous avez temporairement besoin d’accéder à distance à un serveur derrière pare-feu pour votre projet à rendre demain matin !

Pas de panique, OpenSSH est là :

	# Fait un tunnel entre le port 22 de cette machine et le 2222 de l'hôte distant
	fira@serveur$ ssh -NfR 2222:localhost:22 fira@maison.org
 
	# A la maison:
	fira@maison$ ssh -p 2222 localhost
	Last login: Sat Jun   8 90:01:42 2013
	fira@serveur$

Attention: cette méthode n'est pas pratique de façon permanente : si jamais serveur perd la connexion à maison, le tunnel sera coupé et il faudra le relancer. Des outils tels que autossh, en y ajoutant une authentification par clé publique, permettent de se reconnecter automatiquement.

Passage de ports

On peut aussi faire l'inverse: demander à ce qu'un port local soit passé à une machine distante.

	# Redirige notre port 2222 vers le port 22 de traxix en partant de clubnix.fr
	fira@laptop$ ssh -NfL 2222:traxix:22 fira@clubnix.fr
	fira@laptop$ ssh -p 2222 localhost
	Last login: Sun Jun  42 11:12:13 2014
	fira@traxix$

Pour les cas plus compliqués

Les commandes que nous avons vu plus haut sont très pratiques, mais elles ont tous en commun le fait d'utiliser OpenSSH. Certes, ça fonctionne, mais ce n'est pas toujours quelque chose de disponible ou l'outil le plus adapté. Nous allons maintenant voir comment nous débrouiller pour manœuvrer sans.
Attention: Rappelez vous que si vous utilisez GNU netcat, certaines options peuvent être différentes. Vous n'avez par exemple généralement pas besoin de -p

Copier un fichier ou transporter un flux avec Netcat

Netcat permet de transporter des flux de données comme n'importe quelle autre commande, au travers d'une connexion par TCP ou UDP.
Exemples pratiques :

 # Se préparer à recevoir des données par TCP sur le port 8888 et les stocker dans un fichier
	fira@serveur$ nc -l -p 8888 > important.conf
	# On les envoie depuis l'autre côté
	fira@laptop$ nc serveur 8888 < stuff.txt
 
	# La même chose avec socat:
	fira@serveur$ socat TCP-LISTEN:8888 FILE:important.conf,creat
	fira@laptop$ socat FILE:stuff.conf TCP:serveur:8888
 
	# Pour visualiser des logs en temps réel en les envoyant aux personnes se connectant sur le port 8888
	fira@serveur$ tail -f /var/log/messages | grep -i dhcp | nc -kl 8888
	fira@laptop$ nc serveur 8888
	May 15 11:55:07 serveur dhcpd[12337]: Server is going haywire!
	May 15 11:58:08 serveur dhcpd[12337]: Server is giving random leases to everyone!
	May 15 11:59:42 serveur dhcpd[12337]: We're doomed ! I'll just die now.

Broadcasteur de logs cheap avec socat

Si vous avez essayé le dernier exemple, vous aurez peut-être remarqué que si il fonctionne, les données ne sont envoyées qu'à une personne à la fois.
On pourrait répliquer la fonctionnalité de logging réseau de la plus part des vrais loggueurs simplement avec socat, en les envoyant à tout le monde sur le réseau local par exemple.
Pour cela on peut utiliser des paquets UDP de broadcast:

	root@serveur# socat -u OPEN:/var/log/messages,rdonly,ignoreeof UDP-DATAGRAM:192.168.0.255:514,broadcast

Relai de connexion avec socat

Scénario : Vous avez besoin d’accéder à un service web mais se connecter directement avec l'application ne fonctionne pas pour une raison X ou Y (par exemple, Firefox n'accepte pas les adresses IPv6 de lien local).
Solution :

	fira@laptop$ socat TCP4-LISTEN:8080,fork TPC6:[fe80::a3e:8eff:fe3c:e893%eth0]:80
	fira@laptop$ wget localhost:8080
	(...)
	2013-06-10 19:06:09 (485 KB/s) - ‘index.html’ saved [74817/74817]
 

Ça fonctionne aussi pour forcer une application à utiliser un proxy de façon transparente, bien que des utilitaires comme Proxychains soient bien sûr plus adaptés.

	# Se connecter à clubnix.fr:80 à travers proxy:9050
	fira@laptop$ socat TCP-LISTEN:8080,fork SOCKS4:proxy:clubnix.fr:80,socksport=9050
	fira@laptop$ wget localhost:8080
	Connecting to localhost (localhost)|127.0.0.1|:8080... connected.
	HTTP request sent, awaiting response... 301 Moved Permanently
	(...)
 

Connecter deux machines derrière des NAT avec Socat et une troisième box

Pour cela on peut passer par un point d'échange au milieu accessible aux deux. Il suffit d'indiquer à socat que nous voulons connecter deux sockets d'écoute :

	fira@middlebox$ socat TCP-LISTEN:9000,fork TCP-LISTEN:9001

Lorsqu'une connexion arrive sur le port 9000, socat attendra ensuite une connexion sur le port 9001 pour transférer des données entre les deux. Ceci peut permettre de communiquer par exemple par netcat... ou de forwarder vers d'autres ports et serveurs avec les commandes vues plus haut.

Connecter deux machines derrière des NAT directement par Hole Punching

Cette méthode est beaucoup plus compliquée et repose sur le fonctionnement inhérent aux firewalls NAT. En tentant d'initier une connexion depuis les deux côtés à la fois et en manipulant les ports sources et destination de façon appropriée, il est possible d'obtenir une connexion directe entre ces deux machines malgré le NAT. Cette technique est utilisée par des clients P2P modernes tels Skype et ne nécessite un serveur central (ou opérateur humain!) que pour passer les connections.
Des explications plus détaillées sur cette technique ne manquent pas sur internet, voici par exemple un article sur linuxjournal.com.

Un VPN en 10 minutes avec OpenSSH

Scénario : Vous avez un accès root et ssh sur une machine chez vous et voulez accéder à son réseau local.
Ici la machine "laptop" (utilisant le routeur local 192.168.0.254) veut se connecter à la machine "remote", accessible par SSH sur l'IP 88.88.88.88 (elle peut être derrière un NAT, ou non). Nous allons utiliser OpenSSH pour créer un tunnel et préparer une configuration de routage basique.
Attention : ceci est plus un concept pour apprendre le fonctionnement des outils, une solution avec un vrai client VPN est bien sur plus simple. Ne vous lancez pas dans ces manipulations sans un minimum de compréhension de IP.

Préparation

Sur la machine distante, il faut activer les logins root & les tunnels IP dans la configuration de SSH (/etc/ssh/sshd_config) avec "PermitRootLogin yes" et "PermitTunnel yes". Attention: il faut ensuite redémarrer SSH.

Sur la machine locale, il peut être utile de désactiver les utilitaires de gestion du réseau tel NetworkManager car il risque de modifier les routes.
Nous configurons ensuite les interfaces manuellement :

	root@laptop# ip link set dev wlan0 up
	# Dans le cas du Wi-Fi, il faut s'associer avec le point d'accès
	root@laptop# wpa_supplicant -B -Dwext -iwlan0 -c/etc/wpa_supplicant/wpa_supplicant.conf
	# On démarre le client DHCP avec -G pour qu'il ne modifie pas les routes
	root@laptop# dhcpcd -B -G wlan0
	# Mais on ajoute quand meme une route par défaut pour l'instant:
	root@laptop# ip route add default via 192.168.0.254 dev wlan0

Mise en place du tunnel

	# Crée des interfaces tunnel tun3 des deux côtés de la connexion
	root@laptop# ssh -Nf -w 3:3 root@serveur
	# On doit activer et configurer ces interfaces, des deux côtés:
	root@laptop# ip link set dev tun3 up
	root@laptop# ip addr add dev tun3 scope host 192.168.254.2 peer 192.168.254.1
	root@remote# ip link set dev tun3 up
	root@remote# ip addr add dev tun3 scope host 192.168.254.1 peer 192.168.254.2
 
	# Par précaution, on place une route explicite pour acceder au serveur, et éviter que le tunnel essaie de se connecter à travers lui même
	root@laptop# ip route add 88.88.88.88/32 via 192.168.0.254 dev wlan0
 
	# Les deux machines devraient maintenant pouvoir se joindre a travers le tunnel SSH
	root@laptop# ping 192.168.254.1
	PING 192.168.254.1 (192.168.254.1) 56(84) bytes of data.
	64 bytes from (...)
 

Configuration du routage

Cette partie est plus embettante car elle dépend de la configuration de votre machine distante. Pour l'exemple je vais admettre un simple PC de bureau sans règles de firewall ou routage particulières.
Nous allons donc configurer un NAT sur la machine distante pour relayer les connexions. Il serait possible de plutôt envoyer des paquets Ethernet par une interface tap (avec une option dans SSH ou en utilisant socat, pour être de façon complétement transparente sur le même réseau) mais cela veut dire changer les paramètres de bridge de la connexion actuelle (ce que personne ne veut faire a distance).

	root@remote# iptables -t nat -A POSTROUTING -j MASQUERADE
	root@remote# sysctl net.ipv4.ip_forward=1
 
	# Il n'y a plus qu'à configurer les routes sur notre PC local pour que le traffic passe par le VPN !
	root@laptop# ip route replace default via 192.168.254.1
 

Limitations

Ce type de configuration n'est évidemment pas optimale pour beaucoup de raisons (configuration manuelle, insécurité, inefficacité des programmes, utilisation de TCP, un VPN traditionnel utilise généralement UDP, fragmentation des paquets...). Pour un vrai VPN, vous voudrez sans doute utiliser une alternative telle OpenVPN ou tinc (à noter que tinc et ssh peuvent être multiplexés avec sslh pour être tous les deux accessibles sur le même port).

Conclusion

Avec un accès sur quelques machines Linux et des outils disponibles sur presque n'importe quelle distribution, on peut facilement se débrouiller pour acceder aux services dans des situations autrement impossibles.
Il est possible d'effectuer des fonctions complexes en utilisant socat, et un bon nombre en sont détaillées dans sa page de manuel.
L'utilisation de ces commandes est aussi très instructif concernant le fonctionnement et les possibilités offertes par les protocoles utilisés les plus couramment.

Ressources supplémentaires

  1. "SSH Trickery @ Nick Moore's Blog"
  2. Chainer des proxys avec Proxychains
  3. Introduction aux NATs dans le contexte de netfilter
  4. Un article complet sur la technique du "Hole Punching"
  5. Page d'accueil du projet Tor pour l'anonimité sur Internet
  6. Le manuel de Socat donne beaucoup d'exemples d'utilisation
Domaine: 

Commentaires

Portrait de romain

On ne trouve nulle part ailleurs des informations aussi bien rassemblés et en français, bravo