He estado buscando una solución para este problema por intervalos durante los últimos 16 meses. Pero cada vez que miro, parece imposible hacer esto con el protocolo SSH como se especifica en RFC relevantes e implementado por implementaciones importantes.
Sin embargo, si está dispuesto a usar un cliente SSH ligeramente modificado y está dispuesto a utilizar protocolos de una manera que no fue exactamente cuando se diseñaron, entonces es posible lograrlo. Más sobre esto a continuación.
Por qué no es posible
El cliente no envía el nombre de host como parte del protocolo SSH.
Puede enviar el nombre de host como parte de una búsqueda de DNS, pero puede almacenarse en caché, y la ruta del cliente a través de resolvers a servidores autorizados no puede cruzar el proxy, e incluso si lo hiciera, no existe una forma sólida de asociar búsquedas de DNS específicas con clientes específicos de SSH.
Tampoco hay nada lujoso que pueda hacer con el protocolo SSH. Debe elegir un servidor sin siquiera haber visto el banner de la versión SSH del cliente. Debe enviar un banner al cliente, antes de que envíe algo al proxy. Las pancartas de los servidores podrían ser diferentes, y no tienes ninguna posibilidad de adivinar cuál es la correcta para usar.
Aunque este banner se envíe sin cifrar, no puede modificarlo. Cada bit de ese banner se verificará durante la configuración de la conexión, por lo que estarías causando una falla de conexión un poco más adelante.
La conclusión para mí es bastante clara, uno tiene que cambiar algo en el lado del cliente para que esta conectividad funcione.
La mayoría de las soluciones están encapsulando el tráfico SSH dentro de un protocolo diferente. También se podría imaginar una adición al protocolo SSH en sí mismo, en el que el banner de versión enviado por el cliente incluye el nombre de host. Esto puede seguir siendo compatible con servidores existentes, ya que parte del banner se especifica actualmente como un campo de identificación de forma libre, y aunque los clientes generalmente esperan el banner de la versión del servidor antes de enviar el suyo, el protocolo permite que el cliente envíe su banner. primero. Algunas versiones recientes del cliente ssh (por ejemplo, la de Ubuntu 14.04) envía el banner sin esperar el banner del servidor.
No conozco ningún cliente que haya tomado medidas para incluir el nombre de host del servidor en este banner. He enviado un parche a la lista de correo de OpenSSH para agregar dicha función. Pero fue rechazado por el deseo de no revelar el nombre de host a nadie que esté husmeando en el tráfico SSH. Dado que un nombre de host secreto es fundamentalmente incompatible con la operación de un proxy basado en nombres, no espere ver una extensión SNI oficial para el protocolo SSH en el corto plazo.
Una solución real
La solución que mejor funcionó para mí fue usar IPv6.
Con IPv6 puedo tener una dirección IP separada asignada a cada servidor, por lo que la puerta de enlace puede usar la dirección IP de destino para averiguar a qué servidor enviar el paquete. Los clientes SSH a veces se pueden ejecutar en redes donde la única forma de obtener una dirección IPv6 sería mediante el uso de Teredo. Se sabe que Teredo no es confiable, pero solo cuando el extremo nativo de IPv6 de la conexión está utilizando un relé público de Teredo. Uno simplemente puede poner un relé Teredo en la puerta de enlace, donde ejecutaría el proxy. Miredo se puede instalar y configurar como un relé en menos de cinco minutos.
Una solución alternativa
Puede usar un host de salto / host de bastión. Este enfoque está destinado a casos en los que no desea exponer el puerto SSH de los servidores individuales directamente a Internet público. Tiene el beneficio adicional de reducir la cantidad de direcciones IP externas que necesita para SSH, por lo que es utilizable en este escenario. El hecho de que sea una solución destinada a agregar otra capa de protección por razones de seguridad no le impide usarla cuando no necesita la seguridad adicional.
ssh -o ProxyCommand='ssh -W %h:%p user1@bastion' user2@target
Un truco sucio para que funcione si la solución real (IPv6) está fuera de tu alcance
El truco que estoy a punto de describir solo debe usarse como el último recurso absoluto. Antes de siquiera pensar en usar este truco, le recomiendo obtener una dirección IPv6 para cada uno de los servidores a los que desea que se pueda acceder externamente a través de SSH. Use IPv6 como su método principal para acceder a sus servidores SSH y solo use este truco cuando necesite ejecutar un cliente SSH desde una red solo IPv4 donde no tenga ninguna influencia en la implementación de IPv6.
La idea es que el tráfico entre el cliente y el servidor debe ser un tráfico SSH perfectamente válido. Pero el proxy solo necesita comprender lo suficiente sobre el flujo de paquetes para identificar el nombre de host. Dado que SSH no define una forma de enviar un nombre de host, puede considerar otros protocolos que ofrecen esa posibilidad.
HTTP y HTTPS permiten que el cliente envíe un nombre de host antes de que el servidor haya enviado datos. La pregunta ahora es si es posible construir una secuencia de bytes que sea simultáneamente válida como tráfico SSH y como HTTP y HTTPS. HTTP es prácticamente un no iniciador, pero HTTP es posible (para definiciones suficientemente liberales de HTTP).
SSH-2.0-OpenSSH_6.6.1 / HTTP/1.1
$:
Host: example.com
¿Te parece SSH o HTTP? Es SSH y completamente compatible con RFC (excepto que algunos de los caracteres binarios se alteraron un poco por la representación SF).
La cadena de versión SSH incluye un campo de comentario, que en el anterior tiene el valor / HTTP/1.1
. Después de la nueva línea SSH tiene algunos datos de paquetes binarios. El primer paquete es un MSG_SSH_IGNORE
mensaje enviado por el cliente e ignorado por el servidor. La carga útil a ignorar es:
:
Host: example.com
Si un proxy HTTP es lo suficientemente liberal en lo que acepta, la misma secuencia de bytes se interpretaría como un método HTTP llamado SSH-2.0-OpenSSH_6.6.1
y los datos binarios al comienzo del mensaje de ignorar se interpretarían como un nombre de encabezado HTTP.
El proxy no entendería ni el método HTTP ni el primer encabezado. Pero podría entender el Host
encabezado, que es todo lo que necesita para encontrar el backend.
Para que esto funcione, el proxy debe diseñarse según el principio de que solo necesita comprender suficiente HTTP para encontrar el backend, y una vez que se ha encontrado el backend, el proxy simplemente pasará el flujo de bytes sin procesar y dejará la terminación real de la conexión HTTP que debe realizar el backend.
Puede parecer un poco exagerado hacer tantas suposiciones sobre el proxy HTTP. Pero si estaba dispuesto a instalar un nuevo software desarrollado con la intención de admitir SSH, entonces los requisitos para el proxy HTTP no suenan demasiado mal.
En mi propio caso, encontré que este método funciona en un proxy ya instalado sin cambios en el código, la configuración u otros. Y esto fue para un proxy escrito con solo HTTP y sin SSH en mente.
Prueba de concepto cliente y proxy . (Descargo de responsabilidad de que el proxy es un servicio operado por mí. Siéntase libre de reemplazar el enlace una vez que se haya confirmado que cualquier otro proxy admite este uso).
Advertencias de este truco
- No lo uses Es mejor usar la solución real, que es IPv6.
- Si el proxy intenta comprender el tráfico HTTP, seguramente se romperá.
- Confiar en un cliente SSH modificado no es bueno.