Voici un petit retour d'expérience sur la création de tunnels avec SSH.
Nous déployons des serveurs hébergés par OVH. Chaque serveur est protégé par un pare-feu ASA en mode transparent. Les serveurs ne sont accessibles depuis Internet que par des machines dont l'IP est identifiée (liste blanche). On veut établir un tunnel SSH pour accéder à ces machines par HTTPS à partir d'une IP non-autorisée, par exemple celle assignée par un fournisseur d'accès Internet. Il faut alors la bonne clé privée, donc ça reste sécurisé.
Question subsidaire : ça doit fonctionner depuis une machine virtuelle Virtual Box avec un réseau "Host only".
== Précisions techniques
Le pare-feu ASA est un boîtier vendu par Cisco et installé par OVH. Il ajoute une latence non-mesurable, et offre énormément de possibilités de configuration. La principale fonction exploitée ici est le filtrage des adresses d'origine, ainsi que les protections standard contre diverses attaques au niveau TCP.
Un pare-feu en mode transparent se comporte comme un câble réseau intelligent. Le contraire du mode tranparent, c'est le mode routeur, qui nécessite d'indiquer une passerelle pour sortir du sous-réseau. Pourquoi le mode transparent ? Parce qu'OVH n'en supporte pas d'autre pour l'instant.
Les serveurs choisis sont du type "Infrastructure" c'est à dire qu'ils disposent de deux cartes réseau. L'une (eth0) est accessible à partir d'Internet. L'autre (eth1) accède à un réseau virtuel (Vrack) reliant les machines d'un même propriétaire. Le pare-feu ne protège que eth0.
Les serveurs physiques peuvent héberger plusieurs démons (serveurs applicatifs). Pour que les ports ouverts soient toujours les mêmes, un démon n'écoute que sur une seule adresse, qui correspond à la résolution de son nom de domaine. Si un serveur reçoit une requête HTTPS qui ne contient pas le nom de domaine attendu, il redirige vers la page d'accueil (avec le bon nom de domaine).
== Pourquoi du filtrage par adresse IP
Le filtrage par IP limite la vulnérabilité à toutes sortes d'attaques genre déni de service. Il est rarement mis en place, car c'est peu fréquent de connaître l'intégralité des IP des machines qui peuvent se connecter. Une telle chose peut arriver pour une société avec un petit nombre de clients, qui maîtrisent bien leur infrastructure réseau.
Bien sûr une adresse IP peut se falsifier, donc ce type de filtrage ne se substitue pas à toutes les autres mesures de sécurité.
Il faut également que les machines ainsi protégées n'aient pas besoin de recevoir des requêtes initiées par une machine dont l'adresse change fréquemment.
== Contournement
La solution consiste à passer par un serveur de rebond, dont le pare-feu ne filtre pas les adresses d'origine. Le tunnel SSH passe alors par le Vrack, et débouche ensuite sur l'adresse IP du serveur auquel on veut se connecter. Techniquement parlant, le serveur SSH de la machine où s'effectue le rebond devient le client de la connexion vers la machine cible.
On a donc deux tunnels, mais avec la bonne configuration il n'y a qu'une seule commande à lancer. SSH sait mettre des tunnels bout à bout. Une solution inélégante mais équivalente consiste à ouvrir une session SSH sur la machine de rebond, à partir de laquelle on ouvre manuellement une deuxième session vers la machine cible.
Le prérequis pour ce que nous allons voir est le déploiement de sa clés publique SSH sur la machine de rebond et la machine cible.
== La base des tunnels SSH
On parle de tunnel mais le terme utilisé dans la documentation est le transfert de port ("port-forwarding"). L'idée de base consiste à établir une connexion quelque part sur un port donné, mais magiquement le tuyau apparaît sur un autre port, généralement sur une autre machine. Il existe trois sortes de transfert de port.
=== Transfert de port local ("local")
Une fois établi le transfert de port, en se connectant à un port local on "ressort" sur une machine distante, sur un port donné. Bien sûr le tuyau entre les 2 machines s'appuie sur les mécanismes cryptographiques de SSH.
=== Transfert de port distant ("remote")
Une fois établi le transfert de port, un port est ouvert sur la machine distante. En s'y connectant, on ressort sur la machine locale, sur un port donné. On parle aussi de tunnel inverse, puisque c'est la machine locale qui initie la connexion, mais la machine distante aura l'impression d'initier une connexion vers la machine locale.
Bien sûr c'est ce qu'on utilise pour franchir un pare-feu qui ne permet d'initier les connexions que dans une seule direction, et qu'on est du mauvais côté.
=== Transfert de port dynamique ("dynamic")
Une fois le transfert de port établi, on a un proxy SOCKS sur la machine locale est les connexions ressortent sur la machine distante. Nous ne parlerons pas de ce type de transfert de port.
== Rebond avec SSH
Le fichier `~/.ssh/config` permet de préciser toutes sortes de choses. Notamment :
- propager la clé publique en cas de rebond ;
- des alias pour des éléments de configuration qui correspondent à des serveurs.
Disons qu'on a les machines suivantes :
- `bounce` (machine utilisée pour le rebond)
- IP sur eth1 : 172.16.0.1 (côté Vrack)
- `demo` (machine protégée par un filtrage par IP)
- IP sur eth1 : 172.16.0.2 (côté Vrack)
Bon je ne recopie pas la doc de SSH, on se contentera du résultat. Le fichier de configuration SSH :
<<<
Host *
ForwardAgent yes
Host bounce-vrack
User <some-user>
ProxyCommand ssh <some-user>@bounce -W %h:%p
Compression no
>>>
`ForwardAgent` propage la clé publique pour éviter d'avoir à saisir son mot de passe à la création du second tunnel. `Compression no` évite de recompresser un flux déjà compressé.
Pour créer un tunnel du port 8443 vers le port 8443 de la machine cible :
<<<
ssh -vN -o HostName=172.16.0.2 -L 8443:demo:8443 bounce-vrack
>>>
Si on veut ouvrir une session SSH interactive toute simple on ajoute cette entrée dans le fichier de configuration :
<<<
Host demo
User <some-user>
ProxyCommand ssh <some-user>@bounce -W %h:%p
HostName 172.16.0.2
>>>
Et il n'y a plus qu'à faire :
<<<
ssh demo
>>>
== Sortie d'une VirtualBox Windows
Considérons le cas d'une VirtualBox avec Windows (installé par exemple grâce à l'admirable IEVMS). Une configuration raisonnable consiste à créer 2 interfaces virtuelles, une de type "NAT" pour aller sur Internet, et l'autre de type "Host only", qui définit un sous-réseau commun entre la machine qui héberge et les machines virtuelles hébergées.
Dans le cas de Mac OS X au moins, la machine virtuelle hébergée est considérée comme extérieure par le pare-feu, ce qui est tout à fait correct. Mais voilà, on voudrait que la machine hébergée puisse bénéficier du tunnel créé précédemment.
Comment sortir ? Grâce à un tunnel inverse bien entendu. Cela exige au préalable d'installer un serveur SSH tournant sur Windows.
Je n'ai pas réussi à automatiser l'installation de sshd avec Cygwin, alors j'ai choisi la facilité avec l'excellent "Bitvise SSH server"
qui est un produit commercial, mais dont l'utilisation est gratuite pour des particuliers.
Une fois installé Bitvise SSH server, on génère une paire de clés du côté de la machine qui héberge la machine virtuelle. On récupère la clé publique côté machine virtuelle hébergée (avec un partage de fichier VirtualBox par exemple), et on l'assigne à l'utilisateur de son choix (`IEUser` si on se base sur IEVMS). Après il faut redémarrer Windows pour activer les options d'authentification de Bitvise.
Si on a configuré sa machine virtuelle avec l'adresse `192.168.100.101` sur le sous-réseau créé avec l'option "Host only", on teste la connexion ainsi :
<<<
>>>
Pour faire un tunnel inverse tout seul :
<<<
ssh -i <private-key> -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -nNT -R <port_in>:localhost:<port_out>
IEU...@192.168.100.101>>>
Pour utiliser le tunnel créé précédemment (celui vers `demo`) on ajoute une entrée dans le fichier de configuration SSH :
<<<
Host vboxguest
HostName 192.168.100.101
User IEUser
UserKnownHostsFile /dev/null
StrictHostKeyChecking no
IdentityFile <private-key>
Compression no
>>>
Et après on lance :
<<<
ssh -vnNT -R 443:localhost:8443 vboxguest
>>>
On démarre les deux tunnels (celui de VirtualBox en dernier) et on peut alors se conncter avec un navigateur à partir de Windows.
== Bonus : utiliser le vrai nom de hôte
La commande là-dessus permet de se connecter à `localhost:443`, mais que se passe-t-il si on a besoin de tester un logiciel qui veut impérativement se connecter à `demo:443` ?
Dans Windows il suffit d'ajouter la ligne suivante dans le fichier `C:\Windows\system32\drivers\etc\hosts` :
<<<
127.0.0.1 demo
>>>
== Conclusion
Encore une fois on est impressionné par la puissance de SSH. J'espère que ces explications donneront des idées pour faire des trucs sympas. Le rebond avec SSH n'est pas expliqué très souvent, et l'installation d'un serveur SSH sur Windows demeure une possibilité méconnue.
== Références