Une compilation de documentations   { en , fr }

Les tunnels avec OpenSSH

Étiquettes:
Créé en:
Dernière modification:
Auteur:
Xavier Béguin
Version en anglais : OpenSSH tunnels

Quelques définitions

Définition générale d'un tunnel

Un tunnel est une connexion spéciale qui permet de joindre une machine située dans un réseau informatique qui n'est pas accessible directement (comme l'illustre la figure 1 ci-dessous) mais uniquement par l'intermédiaire d'une machine tierce.

Pour joindre cette machine, au lieu de lui envoyer directement les données, on les envoie à l'entrée du tunnel. Elles seront alors encapsulées dans un autre protocole et acheminées vers la machine intermédiaire où se situe la sortie du tunnel. De là, elles seront expédiées vers la machine destinatrice comme si elle avaient été émises depuis la machine intermédiaire. (Ce principe général est illustré par la figure 2 ci-dessous.)

Cette machine intermédiaire située à la périphérie du réseau protégé est souvent appelée une passerelle : suivant le principe du bastion, c'est une machine accessible de l'extérieur du réseau qui a également accès aux machines accessibles uniquement depuis l'intérieur de celui-ci. Son rôle est de permettre un accès limité et authentifié de l'extérieur vers le réseau protégé. L'utilisation d'un tunnel permet ou facilite ce rebond sur la machine passerelle et rendra même cette contrainte quasiment transparente.

Schéma montrant la connexion directe impossible
Figure 1. Exemple d'une connexion directe impossible. [voir la description]
Dans notre exemple, l'application client exécutée sur la machine locale ne peut pas se connecter directement depuis son réseau local vers le serveur distant sur son propre réseau.
Schéma d'un tunnel SSH statique de réexpédition locale
Figure 2. Principe d'un tunnel SSH de réexpédition locale. [voir la description]
Un tunnel est établi entre votre machine locale et la passerelle SSH du réseau distant en précisant un certain service cible distant. L'application client envoie ses données à l'entrée du tunnel sur la machine locale. Les données transitent dans le tunnel vers la passerelle SSH où elles sont ré-émises vers le service cible final.

Distinction entre tunnel statique et tunnel dynamique sous OpenSSH

Comme on l'a précisé dans la définition générale ci-dessus, les données envoyées à l'entrée d'un tunnel sont ensuite réémises depuis sa sortie vers leur destination finale. Parmi les tunnels créés à l'aide du client OpenSSH, on va distinguer deux types de tunnels selon que la destination finale des données est déterminée à la création du tunnel (on parle alors de tunnel statique) ou si l'application qui envoie les données doit préciser elle-même la destination finale de chaque flux de données envoyées (on parlera cette fois de tunnel dynamique).

Tunnel statique

Dans un tunnel statique, les données reçues sont acheminées à l'autre bout du tunnel puis systématiquement envoyées vers la destination (une machine et un port) déterminée lors de la création du tunnel.

Cette solution a l'avantage de rendre le tunnel totalement transparent à l'application qui envoie les données : celle-ci peut les envoyer à l'entrée du tunnel de la même manière qu'elle les enverrait vers la machine de destination. Le tunnel se chargera de les adresser, depuis l'autre bout, vers la destination précisée à sa création. L'inconvénient est bien sûr que l'application ne peut pas les envoyer vers une autre destination, à moins d'utiliser un autre tunnel.

Deux types de tunnels SSH statiques sont possibles avec OpenSSH :

  • dans un tunnel de réexpédition locale (local forwarding tunnel en anglais, illustré dans la figure 2 plus haut), le tunnel est à l'écoute des données entrantes sur la machine où le tunnel SSH a été initié (donc celle où le client SSH a été lancé, qui est souvent votre machine locale), expédie les données vers le serveur SSH auquel le client SSH est connecté et, de là, les envoie vers leur destination finale ;
  • dans un tunnel de réexpédition distante (remote forwarding tunnel en anglais, illustré dans la figure 3 ci-dessous), les données cheminent dans l'autre sens : le tunnel est à l'écoute des données entrantes sur le serveur SSH (la machine à laquelle est connecté le client SSH), expédie les données vers la machine où le client SSH s'exécute et, de là, les envoie vers leur destination finale.
Schéma d'un tunnel SSH statique de réexpédition distante
Figure 3. Principe d'un tunnel SSH de réexpédition distante. [voir la description]
Un tunnel est établi de votre machine locale vers le serveur SSH d'un réseau distant en précisant un certain service cible (accessible depuis votre machine locale). Une application client envoie ses données à la sortie du tunnel sur la machine distante. Les données transitent dans le tunnel vers la machine où le client SSH s'exécute (votre machine locale) où elle sont enfin réémises vers le service cible final.

Tunnel dynamique

Dans le cas d'un tunnel dynamique, le tunnel est créé sans fixer de destination finale pour les données qu'il reçoit en entrée. C'est donc l'application qui envoie les données dans le tunnel qui doit préciser au tunnel vers quelle destination (quelle machine et quel port) les données doivent être réémises.

C'est le protocole SOCKS qui est utilisé pour préciser ces informations de destination au tunnel. Le tunnel dynamique agit alors comme un serveur mandataire SOCKS (ou proxy SOCKS en anglais) qui reçoit des données sur une machine (l'entrée du tunnel) et les réémet depuis une autre machine (la sortie du tunnel).

Schéma d'un tunnel SSH dynamique
Figure 4. Principe d'un tunnel SSH dynamique. [voir la description]
Un tunnel dynamique est créé entre votre machine locale et un serveur SSH sur un réseau distant. L'application client envoie ses données à l'entrée du tunnel sur la machine locale. Les données transitent alors dans le tunnel vers le serveur SSH où elles sont réémises vers le service cible final que l'application client a précisé en utilisant le protocole SOCKS. L'application peut ainsi envoyer à travers le même tunnel des données adressées à différents services.

La possibilité de joindre n'importe quelle machine accessible depuis l'autre bout du tunnel et le fait que, du point de vue de la machine cible finale, la connexion semble provenir de la machine en bout de tunnel (la passerelle SSH dans notre exemple) font que ce type de tunnel ressemble, en termes de fonctionnalités, à un VPN (Virtual Private Network, c'est à dire réseau privé virtuel en français) à la différence que seules les applications configurées pour utiliser ce tunnel/proxy seront redirigées vers ce pseudo-réseau virtuel.

Ouverture d'un tunnel statique

Informations requises

En pratique, pour ouvrir un tunnel SSH qu'on qualifiera de statique (par opposition à un tunnel dynamique décrit plus bas), nous devons fournir trois types d'information :

  • le nom DNS de la machine à laquelle se connecter en SSH (qui sera la passerelle SSH dans notre exemple de connexion à un réseau protégé distant) et où sera située la sortie du tunnel, ainsi que l'identifiant du compte à utiliser sur cette machine. Ces informations vous aurons été normalement fournies par l'administrateur du réseau auquel vous vous connectez lorsqu'il vous a accordé l'autorisation d'y accéder ;
  • le port TCP et l'interface réseau de votre machine locale (où le client OpenSSH est exécuté) où sera située l'entrée du tunnel :
    • le choix de ce port est libre, tant qu'il n'est pas déjà utilisé (et qu'il est supérieur à 1024 si vous utilisez un utilisateur non privilégié, donc autre que root, qui ne peut par définition utiliser que ces ports),
    • vous devrez aussi déterminer sur quelle interface réseau sera ouvert ce port. Ce choix est notamment important en terme de sécurité, car si vous utilisez une interface connectée à un réseau, une machine située sur celui-ci pourra potentiellement envoyer des données dans le tunnel si le port n'est pas protégé par un pare-feu local (voir encadré ci-dessous au sujet de la sécurité). Si aucune interface n'est précisée, par défaut tout se passe comme si le choix de l'adresse de bouclage (l'interface loopback, désignée par l'adresse localhost) avait été fait et le tunnel ne sera accessible qu'aux utilisateurs de votre machine ;
  • le nom DNS ou l'adresse IP et le port TCP de la machine et où seront renvoyées, à partir de la sortie du tunnel, les données ayant transité dans celui-ci : comme on l'a déjà mentionné plus haut dans cette page, ce type de tunnel nécessite de déterminer ces informations à la création du tunnel (c'est pour cette raison qu'on a choisi de qualifier ce type de tunnel de statique).

Note sur la sécurité : notez bien qu'une fois le tunnel ouvert, aucune authentification supplémentaire n'est effectuée lors de la réception des données. Autrement dit, OpenSSH va encapsuler et faire transiter dans le tunnel toutes les données qu'il reçoit sur le port d'entrée du système et les envoyer à la machine cible depuis l'autre côté du tunnel.

Lorsque l'entrée du tunnel est située sur une interface connectée à un réseau, il faut bien garder à l'esprit que toute machine de ce réseau pourra potentiellement envoyer des données dans le tunnel, et donc à la machine cible, si le port d'entrée n'est pas protégé par un pare-feu local.

Il est donc recommandé d'installer l'entrée du système sur l'interface de bouclage localhost (correspondant à l'adresse 127.0.0.1 ou ::1), qui est l'interface utilisée par défaut si aucune n'est précisée à la création du tunnel, car celle-ci n'est accessible qu'aux utilisateurs du système. Même dans ce cas, il faut évaluer les risques que font courir de potentiels utilisateurs du système malveillants.

En tant que créateur d'un accès à distance au réseau protégé de l'autre côté de la passerelle, vous avez la responsabilité de vous assurer de ne pas créer pas un risque de sécurité en donnant à des utilisateurs inconnus un accès à des ressources protégées.

Commande d'ouverture du tunnel

Tunnel de réexpédition locale (local forwarding)

Nous supposerons dans les exemples de ce document que vous disposez d'un compte d'identifiant hkrustofski (d'après mon personnage bien-aimé Herschel Krustofski) accessible en SSH depuis internet sur une machine de nom DNS passerelle.example.org ayant le rôle de passerelle SSH et qui a accès à un réseau protégé dont les machines utilisent le suffixe DNS .example.org.

La commande suivante permet l'ouverture d'un tunnel entre votre machine locale et une machine passerelle à laquelle on se connecte en SSH (et potentiellement située dans un réseau protégé par un pare-feu) pour joindre le port 80 d'une machine appelée host1.example.org :

ssh -N -L 3033:host1.example.org:80 hkrustofski@passerelle.example.org &

Voici une description des trois arguments de la commande ssh :

  • l'option -N (qui est parfaitement facultative, mais que je trouve pratique) est employée ici pour empêcher l'ouverture d'un shell sur la machine distante. Le tunnel sera alors ouvert jusqu'à ce qu'on tue le processus SSH qu'on a lancé ici en tâche de fond (via une commande comme kill %1 dans la même session shell, par exemple). Sans cette option, la connexion exécutera un interpréteur shell sur la machine, et elle se terminera, tout comme le tunnel, lorsque vous fermerez ce shell ;
  • l'option -L est celle qui demande la création du tunnel et précise son entrée et sa sortie généralement sous la forme [adr_locale:]port_local:machine:port_machine:
    • ici, l'entrée s'effectuera sur le port TCP 3033 (ce pourrait être n'importe quel port disponible supérieur à 1024). Comme aucun nom DNS ou adresse IP n'est précisé avant le port, c'est donc comme si avait été indiqué localhost:3033 à la place, ce qui signifie que le tunnel écoutera sur le port TCP 3033 de l'interface locale de votre machine (d'adresse 127.0.0.1 en IPv4, ::1 en IPv6). (On pourrait aussi utiliser *:3033 pour écouter sur toutes les interfaces réseau de la machine.),
    • les données envoyées dans le tunnel (donc, dans notre exemple, reçues sur le port TCP localhost:3033 de votre machine locale) seront transmises à la machine host1.example.org sur le port 80 en passant par la passerelle sur laquelle on se connecte en SSH. Le nom DNS host1.example.org sera résolu à la sortie du tunnel, sur la passerelle SSH (ce nom a d'ailleurs de fortes chances de ne pas être visible en dehors du réseau protégé distant). Notons en passant que si le service que vous cherchez à joindre est situé sur la machine où débouche le tunnel elle-même, vous devrez préciser le nom localhost comme machine de destination pour vous y connecter ; ce nom désignera dans ce cas l'interface locale de la machine distante ;
  • hkrustofski@passerelle.example.org représente bien sûr l'identifiant de votre compte sur la passerelle SSH et son nom DNS. Ce compte doit vous avoir été fourni par l'administrateur de la passerelle. Lors de la connexion, vous devrez vous authentifier à l'aide d'un mot de passe ou d'une clef SSH préalablement mise en place.

Le tunnel créé par cette commande correspond à celui illustré par la figure 2 ci-dessus.

Tunnel de réexpédition distante (remote forwarding)

Pour ouvrir un tunnel fonctionnant dans l'autre sens, c'est à dire qui va transmettre les données envoyées à un port de la machine distante vers un port accessible depuis votre réseau local, il faut utiliser l'option -R en ligne de commande, qui s'utilise de façon identique à -L.

La commande suivante permet l'ouverture d'un tunnel entre une machine distante sur laquelle vous vous connectez en SSH et la machine host2.example.net en passant par votre machine locale (donc potentiellement située sur votre propre réseau interne protégé) :

ssh -N -R3033:host2.example.net:80 hkrustofski@distant.example.org &

Dans l'exemple ci-dessus, l'entrée du tunnel sera établie sur le port TCP 3033 de l'interface de bouclage localhost de la machine passerelle.example.org et permettra à des utilisateurs de la machine distante de se connecter au port 80 de la machine host2.example.org en passant par votre machine locale. Cela permet à des utilisateurs distants de contourner le pare-feu de votre réseau et de se connecter à une machine joignable depuis votre réseau local. Tout comme le tunnel de réexpédition locale (ou local forward, créé avec l'option -L), ce tunnel de réexpédition distante (ou remote forward) contourne les règles de sécurité mises en place sur votre réseau et doit donc être utilisé avec grande précaution.

La figure 3 ci-dessus illustre le tunnel résultant de cette commande.

Utilisation du fichier de configuration du client OpenSSH

Tunnel de réexpédition locale

Si ce tunnel doit être utilisé régulièrement, son utilisation peut être rendue plus facile en ajoutant une entrée dans le fichier de configuration du client d'OpenSSH, ~/.ssh/config. Cette configuration évitera d'avoir à préciser les paramètres du tunnel en ligne de commande.

Un nom de connexion doit être fourni avec l'entrée ajoutée au fichier de configuration pour préciser les paramètres du tunnel (ici ce nom de connexion est simplement tunnel mais à peu près n'importe quel nom pourrait être choisi). Ce tunnel pourra ensuite être ouvert en appelant le client OpenSSH avec le nom de connexion de la même façon qu'on utiliserait un nom de machine. (Notez que l'option IdentityFile utilisée ci-dessous n'est nécessaire que si vous avez besoin de désigner une clef SSH pour la connexion différent de celle du fichier de clef par défaut, qui est quelque chose comme comme ~/.ssh/id_ed25519).

Voici un exemple de configuration (à ajouter à votre fichier ~/.ssh/config) correspondant à l'exemple en ligne de commande donné plus haut :

Host tunnel
  Hostname passerelle.example.org
  User hkrustofski
  IdentityFile ~/.ssh/id_hkrustofki
  LocalForward 3033 host1.example.org:80

Une fois cette configuration ajoutée, la commande suivante ouvrira le tunnel vers la machine passerelle.example.org et il sera configuré de manière identique à la commande ssh donnée en exemple dans la section précédente (l'option -N est ici aussi tout à fait facultative) :

ssh -N tunnel &

Les arguments fournis avec l'option -L utilisée en ligne de commande seront ici précisés avec la directive LocalForward qui prend deux arguments :

  • le premier, [adresse:]port (ici 3033) est l'adresse IP facultative (ou le nom DNS se résolvant en une adresse IP locale du système) et le port TCP de l'entrée du tunnel ;
  • le second, adresse:port est l'adresse IP (ou un nom DNS résolu à la sortie du tunnel) et le port TCP vers lesquels les données ayant transité dans le tunnel seront envoyées à la sortie du tunnel.

Tunnel de réexpédition distante

La directive correspondant à l'option de ligne de commande -R est RemoteForward et fonctionne de manière identique à LocalForward, à la différence que l'entrée du tunnel sera située sur la machine distante (la passerelle SSH dans notre exemple) et sa sortie sur la machine locale.

Le tunnel de réexpédition distante défini en ligne de commande dans l'exemple de la section plus haut peut ainsi être défini avec l'entrée suivante dans le fichier de configuration ~/.ssh/config :

Host tunnel
  Hostname distant.example.org
  User hkrustofski
  IdentityFile ~/.ssh/id_hkrustofki
  RemoteForward 3033 host2.example.net:80

Le tunnel dynamique

Différence vis-à-vis d'un tunnel statique

On l'a vu, un tunnel SSH obtenu avec l'option -L permet uniquement de se connecter à une adresse et un port distants précisés de manière statique à l'ouverture du tunnel.

En revanche, comme on l'a précisé plus haut dans la section des définitions, un tunnel dynamique consiste en une sorte de généralisation de ce type de tunnel SSH : au lieu de préciser lors de la création du tunnel le destinataire de toutes données qui y transiteront, ce sont les applications envoyant des données dans le tunnel qui doivent utiliser le protocole SOCKS pour préciser la machine et le port vers lesquels OpenSSH transmettra ces données à la sortie du tunnel. C'est pour cette raison qu'un tunnel SSH dynamique peut aussi être considéré comme un proxy SOCKS.

Comme dans le cas d'un tunnel statique, pour la machine de destination, tout se passe comme si la connexion avait été initiée sur la machine où se trouve la sortie du tunnel.

Ouverture du tunnel dynamique

Le tunnel dynamique, qui jouera donc aussi le rôle de proxy SOCKS, peut être ouvert à l'aide de l'option -D du client OpenSSH, donc en lançant une commande du type :

ssh -D 1080 hkrustofski@passerelle.example.org

Dans cet exemple, l'option -D ne désigne qu'un port sans préciser l'interface réseau locale à utiliser. C'est donc le port TCP 1080 de l'interface de bouclage (loopback interface, d'adresse 127.0.0.1) qui sera utilisée, comme si l'argument localhost:1080 avait été donné. Pour écouter sur une autre interface réseau de la machine, il faut fournir une adresse IP configurée sur la machine ou un nom DNS qui y corresponde, par exemple -D 192.168.1.27:1080 (préciser * pour écouter sur toutes les interfaces réseau, par exemple -D '*:1080').

On peut aussi utiliser la directive DynamicForward 1080 (qui est l'équivalent de l'option -D de la ligne de commande) dans une entrée du fichier de configuration du client OpenSSH ~/.ssh/config comme dans l'exemple ci-dessous (où aucun nom DNS ou adresse IP d'interface réseau de la machine n'est précisé, ce qui équivaut donc là aussi à préciser DynamicForward localhost:1080) :

Host passerelle.example.org
	User hkrustofski
	DynamicForward 1080

Voici une traduction libre de la documentation relative à cette directive, fournie par la page de manuel ssh_config(5) :

DynamicForward
    Précise qu'un port TCP sur la machine locale doit être
    transféré via le canal sécurisé, et le protocol de l'application
    est ensuite utilisé pour déterminer où se connecter depuis la
    machine distante.

    L'argument doit être [bind_address:]port. (...)

    Actuellement, les protocoles SOCKS4 et SOCKS5 sont acceptés,
    et ssh(1) agira comme un serveur SOCKS. Plusieurs transferts
    peuvent être précisés, et des transferts supplémentaires peuvent
    être donnés en ligne de commande. Seul le superutilisateur peut
    transférer des ports privilégiés.

En voici la version originale, par souci de précision :

DynamicForward
     Specifies that a TCP port on the local machine be forwarded over
     the secure channel, and the application protocol is then used to
     determine where to connect to from the remote machine.

     The argument must be [bind_address:]port. (...)

     Currently the SOCKS4 and SOCKS5 protocols are supported, and
     ssh(1) will act as a SOCKS server.  Multiple forwardings may be
     specified, and additional forwardings can be given on the command
     line.  Only the superuser can forward privileged ports.

Après avoir ouvert ce tunnel, dans notre exemple on pourra ensuite utiliser le proxy SOCKS (acceptant les versions 4 ou 5 de ce protocole) disponible sur le port TCP 1080 de l'interface de bouclage (d'adresse localhost ou 127.0.0.1) avec des applications compatibles avec ce type de proxy (voir ci-dessous).

Utilisation du proxy SOCKS du tunnel dynamique

Usage général : l'exemple de tsocks

Si vous souhaitez utiliser le proxy SOCKS d'un tunnel dynamique OpenSSH avec une application (comme un navigateur web par exemple), vérifiez d'abord dans ses paramètres si celle-ci peut utiliser un proxy de ce type. C'est le cas avec de nombreux navigateurs web, comme Firefox par exemple (dont la configuration avec un proxy SOCKS est décrite plus bas).

Les applications qui ne prévoient pas l'utilisation native d'un proxy SOCKS peuvent généralement être malgré tout utilisées avec un tel proxy (sous GNU/Linux, et probablement aussi sous d'autres systèmes) grâce à des outils comme proxychains ou tsocks qui vont intercepter les appels système relatifs aux connexions réseau effectuées par l'application et de les rediriger de manière transparente vers le proxy configuré.

Avec le programme tsocks par exemple, il faudra préalablement configurer le proxy SOCKS dans son fichier de configuration /etc/tsocks.conf dont voici un exemple minimal :

server = 127.0.0.1
server_type = 5
server_port = 1080

Attention : avec tsocks, la résolution DNS se fait sur votre machine locale, pas de l'autre côté du tunnel. Il faudra donc utiliser les adresses IP pour désigner le service que vous souhaitez joindre à travers le tunnel au lieu du nom DNS si ce nom n'est pas visible depuis votre machine locale.

Après configuration, on peut lancer l'application souhaitée de trois manières (rigoureusement équivalentes en termes de résultats) :

  • en préfixant l'appel à l'exécutable de l'application par la commande tsocks :
    tsocks ssh -v hkrustofski@192.168.102.1
    
  • en modifiant par vous-même la variable d'environnement LD_PRELOAD dans l'environnement de l'application appelée :
    env LD_PRELOAD=/usr/lib/libtsocks.so ssh -v hkrustofski@192.168.102.1
    
  • en lançant la commande « . tsocks -on » (en pas oublier le point du début, synonyme de la fonction source du shell) dans votre shell avant d'y lancer votre application. Elle permet de modifier la variable LD_PRELOAD dans l'environnement du shell courant (environnement dont héritera les applications appelées depuis celui-ci). « . tsocks -off » permet ensuite de supprimer ces changements :
    . tsocks -on
    ssh -v hkrustofski@192.168.102.1
    . tsocks -off
    

(Bien entendu, l'appel à ssh avec tsocks est ici purement démonstratif étant donné que le client OpenSSH peut utiliser le proxy SOCKS directement, comme décrit plus bas.)

Mise à jour : le programme tsocks n'est aujourd'hui plus maintenu officiellement et peut être absent de certains systèmes récents. Il peut être remplacé par d'autres programmes qui fournissent une fonctionnalité similaire ou équivalente comme ts-wrap ou proxychains-ng (ne les ayant pas évalués, je ne peux garantir ni leurs fonctionnalités ni leur qualité).

Utilisation du proxy avec Firefox

Pour utiliser Firefox à travers un proxy SOCKS (c'est à dire pour que toutes ses connexions réseau passent par le proxy), il faut modifier ses paramètres comme ceci (ces instructions sont valables pour Firefox 144 mais peuvent avoir changé dans les versions suivantes) :

  • dans l'onglet Général, descendre tout en bas à la section Paramètres Réseau et cliquer sur le bouton Paramètres ;
  • dans la fenêtre de configuration qui s'ouvre, choisissez l'option Configuration manuelle du proxy, et précisez le nom de votre proxy dans la case Hôte SOCKS et le port dans la case Port de la ligne (dans l'exemple donné ci-dessus, l'hôte est localhost et le port 1080).

Sous Firefox, la résolution DNS est effectuée par défaut à l'autre bout du tunnel, ce qui correspond généralement à ce qu'on souhaite faire. Pour la désactiver, il faut décocher la case « Utiliser un DNS distant lorsque SOCKS v5 est actif » (il existe aussi une case similaire pour SOCKS v4) en bas de la fenêtre de configuration réseau.

Une fois cette configuration effectuée, tout se passe comme si votre navigateur était sur la machine à l'autre bout du tunnel dynamique.

Utilisation transparente du proxy avec SSH

Le client OpenSSH peut très facilement se connecter à une machine à travers un proxy SOCKS et permet ainsi de se connecter de façon totalement transparente en SSH à des machines uniquement joignables à travers le tunnel dynamique.

On utilisera pour ça la directive ProxyCommand dans le fichier de configuration ~/.ssh/config (ou l'option -o "ProxyCommand ..." en ligne de commande car aucune option spécifique n'est fournie). Combinée à l'option -W du client OpenSSH, il est même possible d'ouvrir automatiquement le tunnel dynamique lorsqu'on initie une connexion. Cela permet une ouverture (et une fermeture) du tunnel totalement transparente lorsqu'on utilise une connexion SSH qui en a besoin.

Pour illustrer ces possibilités et continuer dans l'exemple déjà utilisé plus haut, on va définir deux entrées dans le fichier de configuration du client OpenSSH ~/.ssh/config :

  • la première représente le tunnel dynamique connecté à la passerelle SSH (ici passerelle.example.org) ;
  • et la seconde représente une machine derrière le pare-feu, accessible via le tunnel/proxy (host1.example.org) :
Host passerelle.example.org
    User hkrustofski
    DynamicForward 1080

Host host1.example.org
    User hkrustofski
    ProxyCommand ssh -W %h:%p passerelle.example.org

Dans les arguments de ProxyCommand, les valeurs %h et %p sont remplacées par OpenSSH respectivement par le nom de machine et le port du proxy SOCKS que le client OpenSSH devra utiliser. Pour plus de détails, voir les informations sur ProxyCommand dans la page du manuel ssh_config(5) (ici traduite en français).

Avec cette configuration, une connexion OpenSSH à la machine host1.example.org peut alors être ouverte de manière tout à fait transparente avec la commande suivante :

ssh host1.example.org

Cette configuration est bien sûr valable aussi pour d'autres commandes client OpenSSH comme scp :

scp fichier.dat host1.example.org:vers/le/repertoire/

Notez que dans tous les cas, la résolution DNS du nom de machine se fera de l'autre côté du tunnel, c'est à dire, dans notre exemple, sur la passerelle SSH.

Si l'on dispose de plus d'une machine dans le réseau protégé auxquelles on veut se connecter, il n'est pas obligatoire de toutes les définir dans le fichier comme on l'a fait pour host1.example.org. On peut simplement définir une entrée valable pour toutes les machines dont le nom correspond par exemple au motif *.example.org qui imposera l'ouverture et l'utilisation du tunnel/proxy. Cette configuration est décrite dans la section suivante.

Note: pour les anciennes versions d'OpenSSH dont le client ne disposait pas de l'option -W, il était nécessaire d'utiliser l'outil netcat (dans sa version openbsd, fournie sous Debian par le paquet netcat-openbsd) pour transférer les connexions vers le tunnel/proxy, et le tunnel devait être ouvert manuellement avant de lancer les connexions l'utilisant.

La configuration ressemblait donc à ceci :

Host passerelle.example.org
	User hkrustofski
	DynamicForward 1080

Host host1.example.org
	User hkrustofski
	ProxyCommand /bin/nc.openbsd -X 5 -x 127.0.0.1:1080 %h %p

Son utilisation nécessitait ensuite les commandes suivantes (la première ouvre le tunnel avant que la seconde ne crée la connexion qui l'utilise) :

ssh -N passerelle.example.org &
ssh host1.example.org

Utilisation du tunnel par un ensemble de connexions SSH

Quand on dispose d'un ensemble de machines pour lesquelles la connexion par SSH nécessite l'utilisation d'un tunnel (comme dans l'exemple donné plus haut où l'on utilise une passerelle SSH pour se connecter aux machines d'un réseau protégé), on peut automatiser l'utilisation du tunnel pour l'ensemble de ces machines grâce à une seule entrée dans le fichier de configuration. Il n'est ainsi plus nécessaire de créer une entrée pour chaque machine dont l'accès nécessite l'utilisation du tunnel.

On s'appuie pour cela sur la possibilité qu'offre le fichier de configuration d'OpenSSH d'appliquer une configuration commune à un ensemble de machines correspondant à un critère défini. Dans le cas le plus simple qu'on développe ici, on définira une entrée s'appliquant à toutes les machines ayant une partie commune spécifique dans leur nom DNS, comme par exemple un nom correspondant au motif *.example.org.

Voici un exemple de fichier ~/.ssh/config définissant l'ouverture transparente d'un tunnel dynamique jouant le rôle de proxy SOCKS et imposant son utilisation à toute machine dont le nom se termine en .example.org :

Host passerelle
	Hostname passerelle.example.org
	User hkrustofski
	IdentityFile ~/.ssh/id_hkrustofki
	DynamicForward 1080

Host *.example.org !passerelle.example.org
	User hkrustofski
	IdentityFile ~/.ssh/id_hkrustofki
	ProxyCommand ssh -W %h:%p passerelle

Dans l'exemple ci-dessus :

  • la première entrée définit l'accès au serveur SSH établissant le tunnel dynamique permettant l'accès aux machines du réseau interne distant (le serveur qu'on a appelé plus haut la passerelle SSH) ;
  • la seconde sera appliquée à toutes les machines dont le nom se termine en example.org, sauf passerelle.example.org qui représente la passerelle SSH où débouche le tunnel, et qui est par définition accessible directement (voir la note plus bas) ;
  • cette seconde entrée ouvrira et utilisera automatiquement le tunnel grâce à la directive ProxyCommand qu'on a décrite plus haut ;
  • la directive IdentityFile est facultative et désigne un fichier contenant une clef SSH spécifique pour se connecter aux machines concernées (elle est utile lorsqu'on utilise une clef différente de la clef par défaut).

Une fois cette configuration mise en place, toute connexion à une machine telle que host.example.org entraînera l'ouverture et l'utilisation du tunnel/proxy SOCKS qui relaiera les connexions via le serveur passerelle.example.org.

Note concernant l'exclusion de passerelle.example.org dans la seconde entrée : dans l'exemple ci-dessus, il est nécessaire d'exclure la machine passerelle.example.org de la seconde entrée de configuration (à l'aide d'un motif commençant par !), car depuis la version d'OpenSSH version 6.6 (et peut-être quelques versions avant celle-ci), OpenSSH va appliquer à nouveau les configurations du fichier ~/.ssh/config pour le nom de machine correspondant à un alias, comme ci-dessus pour passerelle.example.org. Sans cette exclusion, toute connexion à un serveur en .example.org entraînerait une boucle infinie puisque le client chercherait à ouvrir le tunnel via la passerelle pour se connecter à la passerelle elle-même.

À propos des critères sélectionnant les machines concernées: si vous ne pouvez pas définir un critère sur le nom DNS pour désigner les machines devant utiliser le tunnel (c'est à dire pour les cas où les noms des machines ne se terminent pas toutes par un suffixe commun comme .example.org ou ceux où seule une partie des machines remplissant ce critère nécessite l'utilisation du tunnel), OpenSSH permet de définir d'autres critères plus complexes pour appliquer une configuration à un ensemble de machines (allant jusqu'à l'utilisation d'une commande externe personnalisée chargée de déterminer si la connexion à une machine est concernée par la configuration définie). Pour plus de détails, reportez-vous à la documentation sur les mots-clefs Host et Match de la configuration du client OpenSSH dans la page de manuel ssh_config(5).

Astuce pour une utilisation courante

Bien qu'un peu hors contexte, cette petite astuce complémentaire est fournie par souci d'exhaustivité dans la description des outils et configurations utilisés dans le cas concret qui sert d'exemple tout le long de ce document (celui d'une connexion à un réseau protégé distant accessible via une passerelle SSH).

Les utilisateurs à l'aise avec la ligne de commande sous Unix pourront passer cette section.

On l'a vu ci-dessus, dans notre exemple on utilise le suffixe du nom DNS pour déterminer si l'accès par SSH à une machine nécessite l'utilisation du tunnel (elle est appliquée aux machines dont le nom correspond au motif *.example.org). Lors de l'utilisation du client OpenSSH, il faut donc utiliser le nom complet de la machine pour déclencher l'ouverture et l'utilisation du tunnel.

Dans une utilisation quotidienne dans un interpréteur de commandes, taper le nom complet peut être contraignant. Pour éviter de le taper à chaque connexion, on peut avoir recours à une petite fonction bash, comme par exemple la fonction suivante qui complète son dernier argument par le nom de domaine des machines utilisant par le tunnel :

sshi() { ssh ${@:1:$(($#-1))} ${!#}.example.org; }

Le corps de la fonction bash sshi ci-dessus peut sembler abscons, mais il est en réalité assez simple dans le principe. Il appelle la commande ssh en réutilisant les arguments passés à la fonction :

  • il réutilise d'abord tels quels les arguments passés à la fonction en omettant le dernier (le terme ${@:1:$(($#-1))} sélectionne dans le tableau $@, qui contient les arguments de la fonction, les éléments 1 jusqu'à $# - 1, c'est à dire jusqu'à l'avant-dernier argument, inclus) ;
  • il remplace ensuite le dernier argument (qui est l'argument numéro $#, dont on peut obtenir la valeur, en bash, avec l'expression ${!#}, similaire à la commande eval echo '$#') en le suffixant du nom de domaine, ${!#}.example.org.

Si ce genre de syntaxe, ou, à défaut, ce genre de fonctionnalités, vous met en joie et vous fait saliver, vous pourrez en découvrir d'autres sur ma page « Manipulation des chaînes de caractères et des tableaux en bash » ou sur d'autres pages étiquetées « bash ».

Une fois cette définition de fonction placée dans votre fichier .bash_aliases ou .bashrc, il vous suffira de lancer la commande suivante pour vous connecter à la machine scruffy.example.org :

sshi scruffy

Pour la commande client scp, la fonction suivante, bien qu'imparfaite, devrait fonctionner dans la plupart des cas (attention, elle remplace dans tous ses arguments le caractère « : » par « .example.org: », ce qui pourrait poser problème) :

scpi() { scp "${@/:/.example.org:}"; }

Elle fonctionnera ensuite comme la commande scp, mais en complétant le nom avant le caractère « : » par « .example.org ». En voici quelques exemples d'utilisation :

scpi fichier.txt scruffy:
scpi -r scruffy:/le/repertoire/distant/ /un/repertoire/local/