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.
Figure 1. Exemple d'une connexion directe impossible. [voir la description]
Figure 2. Principe d'un tunnel SSH de réexpédition locale. [voir la description]
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.
Figure 3. Principe d'un tunnel SSH de réexpédition distante. [voir la description]
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).
Figure 4. Principe d'un tunnel SSH dynamique. [voir la description]
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 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
- 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 commekill %1dans 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
-Lest 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'adresse127.0.0.1en IPv4,::1en IPv6). (On pourrait aussi utiliser*:3033pour é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:3033de votre machine locale) seront transmises à la machinehost1.example.orgsur le port80en passant par la passerelle sur laquelle on se connecte en SSH. Le nom DNShost1.example.orgsera 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 nomlocalhostcomme machine de destination pour vous y connecter ; ce nom désignera dans ce cas l'interface locale de la machine distante ;
- ici, l'entrée s'effectuera sur le port TCP
hkrustofski@passerelle.example.orgrepré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é) :
&
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(ici3033) 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:portest 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 :
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 :
5
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: - en modifiant par vous-même la variable d'environnement
LD_PRELOADdans l'environnement de l'application appelée : - en lançant la commande «
. tsocks -on» (en pas oublier le point du début, synonyme de la fonctionsourcedu shell) dans votre shell avant d'y lancer votre application. Elle permet de modifier la variableLD_PRELOADdans l'environnement du shell courant (environnement dont héritera les applications appelées depuis celui-ci). «. tsocks -off» permet ensuite de supprimer ces changements :
(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 sectionParamètres Réseauet cliquer sur le boutonParamè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 caseHôte SOCKSet le port dans la casePortde la ligne (dans l'exemple donné ci-dessus, l'hôte estlocalhostet le port1080).
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 :
Cette configuration est bien sûr valable aussi pour d'autres commandes client
OpenSSH comme scp :
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, saufpasserelle.example.orgqui 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
ProxyCommandqu'on a décrite plus haut ; - la directive
IdentityFileest 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 :
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éments1jusqu'à$# - 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 commandeeval 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 :
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) :
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 :