Comprendre la redirection de port (Port Forwarding)

Avant d'aller plus loin il est important de bien comprendre ce qu'est le "port forwarding".

Nous avons vu qu'il y avait en jeu :

  1. - l'application cliente

  2. - l'application serveur

  3. - le client ssh

  4. - le serveur ssh

Nous allons voir la redirection locale '-L' et distante '-R'. La différence vient du sens de la connexion. Dans le relayage local, le client tcp et le client ssh sont sur la même machine. Dans le relayage distant ou "remote", le client ssh est sur la même machine que le serveur tcp.

Dans la démarche qui suit, on utilise un client M0 et deux serveurs M1 et M2. On considère que sur chaque serveur fonctionne un serveur ssh. On considère aussi que sur chaque machine on dispose d'un compte d'accès sur chaque machine identique avec les clés publiques installées. Cela évite une authentification à chaque commande ou l'utilisation de l'option '-l' pour préciser le compte à utiliser sur le serveur.

Redirection locale de port (-L Local)

Si on considère ces 3 machines : M0, la machine cliente, M1 et M2, des serveurs. La syntaxe de la commande est la suivante :

ssh -L port-local:HOSTNAME:port-distant machine-distante

la commande est passée sur M0.

M0:$ ssh -L 1234:HOSTNAME:80 M1

La commande ouvre une connexion entre le M0:1234 vers HOSTNAME:80 en utilisant le serveur sshd M1:22 (actif sur M1 et sur le port 22). Tout dépend de la valeur que va prendre HOSTNAME. HOSTNAME indique la machine distante sur lequel s'opére la connexion.

Si HOSTNAME est localhost :

M0:$ ssh -L 1234:localhost:80 M1

Ici localhost indique l'adresse de loopback de la machine distante, c'est à dire ici M1 et sur laquelle tourne le serveur sshd. C'est donc M0:1234 qui est tunnélé vers le port M1:80 en utilisant le service M1:22.

Si HOSTNAME est M1 :

M0:$ ssh -L 1234:M1:80 M1 :-/

La connexion est identique mais utilisera l'adresse de la M1 (interface réseau ) plutôt que l'interface lo.

Si HOSTNAME est M0 :

M0:$ ssh -L 1234:M0:80 M1

La commande connecte M1 sur M0:80.

Si HOSTNAME est M2 :

M0:$ ssh -L 1234:M2:80 M1

Il y aura une connexion (un tunnel créé) entre M0 et M1 mais la redirection est effectuée entre M1:1234 et M2:80 en utilisant M1:22. Les transactions sont chiffrées entre M0 et M1, mais pas entre M1 et M2, sauf si un second tunnel ssh est créé entre M1 et M2.

Les redirections de ports ne sont accessibles en théorie que pour les processus locaux (localhost) (pour nous M0). Si la redirection était possible pour des clients (MX ou MY) ou des requêtes distantes qui viendraient se connecter su rM0:1234 dans notre exemple, ces clients pourraient être surpris de se voir re-routés. (Sans parler de risques pour la sécurité car cela signifie que d'autres personnes pourraient utiliser à notre insu le tunnel que nous venons de créer. Prenons un exemple :

MO$ ssh -L 1234:M1:80 M1

les requêtes locales passées sur M1:1234 sont redirigées vers M1:80, mais des requêtes passées d'autres machines sur M0:1234 ne seront pas, par défaut redirigées.

Il est possible toutefois de passer outre avec l'option '-g' qui autorise des clients distants à se connecter à des ports locaux redirigés.

MO$ ssh -g -L 1234:M2:80 M1

La commande "lynx http://M0:1234" lancé à partir d'une machine tierce (MX ou MY) redirigera vers M2:80.

Une utilisation de cette fonction sera vue en application un peu plus loin.

Redirection distante de ports (-R Remote)

Ici le client ssh et le serveur TCP sont du même côté. La syntaxe de la commande est la suivante :

ssh -R port-distant:HOSTNAME:port-local  machine-distante

Le port distant est donc sur la machine distante (remote)

On reprend les mêmes machines que précédemment :

M0:$ ssh -R 1234:HOSTNAME:80 M1

Ici M0 à le serveur TCP, il sert donc de relais entre une connexion M1:1234 et HOSTNAME:80. La connexion est chiffrée entre M1 et M0. Le chiffrement entre M0 et HOSTNAME dépend de la liaison mise en place.

Si HOSTNAME est localhost, alors on a :

M0:$ ssh -R 1234:localhost:80 M1

Cela ouvre une connexion depuis M1:1234 vers M0:80 car localhost correspond, ici, à M0. Si un utilisateur passe une requête sur M1:1234, elle sera redirigée vers M0:80.

Si HOSTNAME est M2, on a :

M0:$ ssh -R 1234:M2:80 M1

Cela ouvre une connexion entre M0 et M1:22, mais les requêtes allant de M1:1234 sont redirigés sur M2:80. Le canal est chiffré entre M1 et M0, mais pas entre M0 et M2.

Enfin dernière remarque, il est possible de passer en paramètre le compte à utiliser sur le serveur qui fait tourner le serveur sshd.

Cette option permet par exemple de donner, à des machines distantes, un accès à un service sur une machine inaccessible autrement.

Schéma de redirection distante de ports

Figure 13.3. Schéma du fonctionnement

Schéma du fonctionnement

Exemple de cas d'utilisation

SSH permet de "tunneler" des protocoles applicatifs via la retransmission de port. Lorsque vous utilisez cette technique, le serveur SSH devient un conduit crypté vers le client SSH.

Cela représente un des plus importants intérêt de SSH. Permettre la création de tunnels "sûrs" pour des protocoles de transports "non sûrs". Ce principe est utilisé pour des applications comme pop, imap, mais également pour des applications X Window.

La retransmission de port mappe (redirige) un port local du client vers un port distant du serveur. SSH permet de mapper (rediriger) tous les ports du serveur vers tous les ports du client.

Pour créer un canal de retransmission de port TCP/IP entre 2 machines qui attend les connexions sur l'hôte local, on utilise la commande suivante :

ssh -L port-local:HOSTNAME:port-distant nomutilisateur@nomhôte

Une fois que le canal de retransmission de port est en place entre les deux ordinateurs, vous pouvez diriger votre client (POP par exemple) pour qu'il utilise le port local redirigé et non plus vers le port distant avec une transmission en clair.

Nous avons bien vu les options '-L' et '-R'. De nombreuses autres options sont utilisables, par exemple :

-L     # Forwarder un port local vers un port distant sur une machine  distante
-R     # Forwarder un port distant vers un port local sur la machine locale
-N     # Ne pas exécuter de commande distante
-f     # Excute le programme en tâche de fond
-l     # Passer en paramètre le login de connexion
-g     # Autoriser des machines distantes à se connecter 
       # sur des ports locaux exportés

Voyons comment créer un tunnel pour le service d'émulation VT par exemple. On va utiliser un port local compris entre 1024 et 65535 qui sont réservés pour des applications utilisateurs. On va prendre par exemple le port local 1025. Pour créer un tunnel on va utiliser la commande :

M0:$  ssh -L 1025:localhost:23 mlx@M1.foo.org

Ici on utilise (précise) que le compte utilisé sur la machine distante sera M1.foo.org. Vous devez faire cela si le nom du compte que vous utilisez sur le client diffère de celui que vous utilisez sur le serveur.

On crée ici un tunnel entre le port local 1025 et le port distant 23. Le tunnel étant créé il ne reste plus qu'à utiliser le port local 1025 avec un telnet adresse_de_la_machine 1025

Le fait de quitter ssh ferme la session tunnelée.

Il est également possible d'ouvrir une session temporaire pour un temps donné. Cela évite d'avoir à fermer les connexions. Dans la commande :

M0:$  ssh -f  -L 1025:localhost:23 mlx@M1.foo.org sleep 10

L'option -f, met ssh en tâche de fond. La session sera fermée automatiquement au bout du temps déterminé, seulement si aucun processus n'utilise le canal à ce moment.

Le principe peut être appliqué à d'autres services. Voici comment utiliser un canal sécurisé vers une application (ici un webmail) qui ne l'est pas. (En fait dans la réalité, je vous rassure, l'application présentée sur l'image offre toutes les options de connexion en mode sécurisé. L'exemple donné est pris pour illustrer le propos.)

# On crée un tunnel entre le port local et le webmail en utilisant 
# le compte mlx@foo.org
M0:$ ssh  -N -f -L 2038:M1:80 mlx@foo.org

Figure 13.4. Tunnel HTTP

Tunnel HTTP

On peut également sans risque aller sur sa zone public_html en toute sécurité.

lynx http://localhost:2038/~mlx

La connexion se fait sur la machine locale et sur le port forwardé. Ni le compte utilisateur utilisé, ni le mot de passe ne transitent en clair.

Avec la machine sur lequel le test est réalisé, les commandes :

M0:$ ssh  -N -f -L 2038:M1:80 mlx@foo.org
et 
M0:$ ssh  -N -f -L 2038:localhost:80 mlx@foo.org

ne produisent pas la même chose.

En effet :

M0:$ ssh  -N -f -L 2038:M1:80 mlx@foo.org

fait référence à une adresse IP qui est redirigée par un Vhost Apache.

M0:$ ssh  -N -f -L 2038:localhost:80 mlx@foo.org

fait référence à l'adresse de loopback, ici c'est le serveur par défaut (typiquement sur une debian /var/www/) qui est consulté. Si vous avez plusieurs Vhosts, il vous faudra créer un tunnel par Vhost.

De la même façon, on peut l'utiliser pour une session ftp.

M0:$ ssh  -N -f -L 2039:M1:21 mlx@foo.org
M0:$ ftp localhost 2039
Connected to localhost.
220 ProFTPD 1.2.5rc1 Server (Debian) [M1]
Name (localhost:mlx): mlx
331 Password required for mlx.
Password:
230 User mlx logged in.
Remote system type is UNIX.
Using binary mode to transfer files.

Attention, dans ce dernier exemple, il n'y a que l'authentification qui est chiffrée car le port 20 (ftp-data) n'est pas forwardé.

X and Port Forwarding

Le déport d'application graphique fonctionne sur le même principe. Le client (/etc/ssh/ssh_config), doit avoir l'option "X11Forward" activée pour les systèmes distants. Le serveur (/etc/ssh/sshd_config), doit avoir l'option "X11Forwarding" d'activée. Il est possible de tunneler des applications graphiques dans ssh.

# Sur le client /etc/ssh/ssh_config
ForwardX11 yes

# Sur le serveur /etc/ssh/sshd_config
X11Forwarding yes

Voyons comment utiliser une application graphique distante :

M0:$  ssh -C  mlx@M1.foo.org
Enter passphrase for key '/home/mlx/.ssh/id_dsa':
M0:$ xclock &
[1] 7771

L'horloge distante s'affiche. L'option "-C", active la compression.

Vous pouvez tout mettre sur la même ligne de commande, avec l'option "-f", qui "fork" l'application demandée.

M0:$  ssh -C -f  mlx@M1.foo.org xclock

Notez le prompt, ici l'ouverture de session a été effectuée en tâche de fond (-f). Pour fermer le canal ssh, il suffit de fermer l'application.

Automatisation de tâches SSH

Dans la mesure où il est possible de passer des commandes à distances sur un serveur, sans avoir à saisir de mot de passe ou de "passphrase", on peut envisager l'automatisation de tâches entre machines et dans des tunnels, comme par exemple les sauvegardes ou la synchronisation de fichiers. Il suffira pour cela de créer un compte associé à la tâche, lui créer une clé privée et exporter sa clé publique sur les différentes machines.

Attention toutefois, les manipulations sur les serveurs requièrent un accès root. Cela signifie que votre compte opérateur, devra avoir un accès root sur le serveur ce qui n'est jamais très bon.

Vous aurez intérêt à réfléchir à la façon de réaliser le traitement sans que ce compte opérateur ait besoin du compte "super utilisateur".

Si vous avez un serveur ayant un dm (Desktop Manager) comme gdm par exemple, disponible sur le réseau vous pouvez lancer les émulations X Window des clients en appelant le serveur. Par exemple sur le client faire :

X -query x.y.z.t :u
ou encore 
X -broadcast :u

avec u pouvant varier de 0 à 5, suivant que vous voulez avoir la session graphique sur F7...F12. Pour le système vc7 que vous utilisez par défaut avec CTRL ALT F7 correspond au premier "display" :0.