Je cherche une solution à ce problème par intermittence depuis 16 mois. Mais chaque fois que je regarde, il semble impossible de faire cela avec le protocole SSH tel que spécifié dans les RFC pertinents et mis en œuvre par des implémentations majeures.
Toutefois, si vous êtes prêt à utiliser un client SSH légèrement modifié et à utiliser des protocoles d’une manière qui n’était pas exactement prévue au moment de leur conception, il est alors possible d’y parvenir. Plus à ce sujet ci-dessous.
Pourquoi ce n'est pas possible
Le client n'envoie pas le nom d'hôte dans le cadre du protocole SSH.
Il peut envoyer le nom d’hôte dans le cadre d’une recherche DNS, mais cela peut être mis en cache et le chemin du client via les résolveurs aux serveurs faisant autorité ne croise pas le proxy. Même s’il le faisait, il n’existait aucun moyen robuste d’associer des recherches DNS spécifiques à clients SSH spécifiques.
Il n'y a rien d'extraordinaire à faire avec le protocole SSH lui-même. Vous devez choisir un serveur sans même avoir vu la bannière de la version SSH du client. Vous devez envoyer une bannière au client avant qu’il n’envoie quoi que ce soit au proxy. Les bannières des serveurs peuvent être différentes, et vous n’avez aucune chance de deviner lequel est le bon.
Même si cette bannière est envoyée non chiffrée, vous ne pouvez pas la modifier. Chaque bit de cette bannière sera vérifié lors de l’établissement de la connexion, ce qui entraînera un échec de connexion un peu plus tard.
Pour moi, la conclusion est claire: il faut changer quelque chose du côté du client pour que cette connectivité fonctionne.
La plupart des solutions de contournement encapsulent le trafic SSH dans un protocole différent. On pourrait aussi imaginer un ajout au protocole SSH lui-même, dans lequel la bannière de version envoyée par le client comprend le nom d’hôte. Cela peut rester compatible avec les versions existantes, puisqu’une partie de la bannière est actuellement spécifiée en tant que champ d’identification de formulaire libre. Bien que les clients attendent généralement la bannière de version du serveur avant d’envoyer le leur, le protocole permet au client d’envoyer leur bannière. première. Certaines versions récentes du client ssh (par exemple celle d’Ubuntu 14.04) envoient la bannière sans attendre la bannière du serveur.
Je ne connais aucun client qui aurait pris des mesures pour inclure le nom d'hôte du serveur dans cette bannière. J'ai envoyé un correctif à la liste de diffusion OpenSSH pour ajouter une telle fonctionnalité. Mais il a été rejeté pour ne pas révéler le nom d'hôte à quiconque surveillant le trafic SSH. Étant donné qu'un nom d'hôte secret est fondamentalement incompatible avec le fonctionnement d'un proxy basé sur un nom, ne vous attendez pas à voir une extension SNI officielle pour le protocole SSH de si tôt.
Une vraie solution
La solution qui fonctionnait le mieux pour moi était en réalité d'utiliser IPv6.
Avec IPv6, je peux attribuer une adresse IP distincte à chaque serveur. Ainsi, la passerelle peut utiliser l'adresse IP de destination pour déterminer le serveur auquel envoyer le paquet. Les clients SSH peuvent parfois fonctionner sur des réseaux où le seul moyen d'obtenir une adresse IPv6 serait d'utiliser Teredo. On sait que Teredo n'est pas fiable, mais uniquement lorsque l'extrémité IPv6 native de la connexion utilise un relais Teredo public. On peut simplement mettre un relais Teredo sur la passerelle, où vous exécuteriez le proxy. Miredo peut être installé et configuré en tant que relais en moins de cinq minutes.
Une solution de contournement
Vous pouvez utiliser un hôte de saut / hôte bastion. Cette approche est destinée aux cas dans lesquels vous ne souhaitez pas exposer directement le port SSH des serveurs individuels à Internet. Il présente en outre l'avantage de réduire le nombre d'adresses IP externes dont vous avez besoin pour SSH, raison pour laquelle il est utilisable dans ce scénario. Le fait qu'il s'agisse d'une solution destinée à ajouter une autre couche de protection pour des raisons de sécurité ne vous empêche pas de l'utiliser si vous n'avez pas besoin de la sécurité supplémentaire.
ssh -o ProxyCommand='ssh -W %h:%p user1@bastion' user2@target
Un sale coup à faire pour que ça marche si la vraie solution (IPv6) est hors de portée
Le hack que je suis sur le point de décrire ne devrait être utilisé qu'en dernier recours. Avant même de penser à utiliser ce hack, je vous recommande fortement de fournir une adresse IPv6 pour chacun des serveurs que vous souhaitez pouvoir atteindre de manière externe via SSH. Utilisez IPv6 comme méthode principale pour accéder à vos serveurs SSH et utilisez ce hack uniquement lorsque vous devez exécuter un client SSH à partir d'un réseau exclusivement IPv4 sur lequel vous n'avez aucune influence sur le déploiement d'IPv6.
L'idée est que le trafic entre le client et le serveur doit être un trafic SSH parfaitement valide. Mais le proxy doit seulement comprendre suffisamment le flux de paquets pour identifier le nom d'hôte. Comme SSH ne définit pas de méthode pour envoyer un nom d’hôte, vous pouvez envisager d’autres protocoles offrant une telle possibilité.
HTTP et HTTPS permettent tous deux au client d'envoyer un nom d'hôte avant que le serveur n'envoie des données. La question est maintenant de savoir s'il est possible de construire un flux d'octets qui est simultanément valide en tant que trafic SSH et en tant que HTTP et HTTPS. HTTP est quasiment un non-démarreur, mais HTTP est possible (pour des définitions de HTTP suffisamment libérales).
SSH-2.0-OpenSSH_6.6.1 / HTTP/1.1
$:
Host: example.com
Est-ce que cela ressemble à SSH ou HTTP pour vous? Il est compatible avec SSH et RFC (sauf que certains des caractères binaires ont été endommagés par le rendu de SF).
La chaîne de version SSH comprend un champ de commentaire qui, dans ce qui précède, a la valeur / HTTP/1.1
. Après la nouvelle ligne, SSH a quelques données de paquets binaires. Le premier paquet est un MSG_SSH_IGNORE
message envoyé par le client et ignoré par le serveur. La charge à ignorer est:
:
Host: example.com
Si un proxy HTTP est suffisamment libéral dans ce qu'il accepte, alors la même séquence d'octets serait interprétée comme une méthode HTTP appelée SSH-2.0-OpenSSH_6.6.1
et les données binaires au début du message ignorer seraient interprétées comme un nom d'en-tête HTTP.
Le proxy ne comprendrait ni la méthode HTTP ni le premier en-tête. Mais il pourrait comprendre l'en- Host
tête, qui est tout ce dont il a besoin pour trouver le backend.
Pour que cela fonctionne, le proxy doit être conçu sur le principe qu'il suffit de comprendre suffisamment de HTTP pour trouver le backend, et une fois que le backend a été trouvé, le proxy passera simplement le flux d'octets brut et laissera la véritable terminaison. de la connexion HTTP à effectuer par le serveur.
Cela peut sembler un peu exagéré de faire autant d'hypothèses sur le proxy HTTP. Mais si vous êtes prêt à installer un nouveau logiciel développé dans l’intention de prendre en charge SSH, les conditions requises pour le proxy HTTP ne semblent pas trop mauvaises.
Dans mon cas, je trouvais que cette méthode fonctionnait sur un proxy déjà installé sans modification du code, de la configuration ou autre. Et c'était pour un proxy écrit avec uniquement HTTP et pas SSH à l'esprit.
Preuve de concept client et proxy . (Avertissement: le proxy est un service que je gère. N'hésitez pas à remplacer le lien une fois que tout autre proxy aura été confirmé pour prendre en charge cet usage.)
Mises en garde de ce hack
- Ne l'utilisez pas. Il vaut mieux utiliser la vraie solution, IPv6.
- Si le proxy tente de comprendre le trafic HTTP, il va sûrement casser.
- Compter sur un client SSH modifié n’est pas une bonne chose.