¿Hay un proxy virtual SSH de host virtual basado en nombre?

36

Me he aficionado bastante a los proxy inversos HTTP en nuestro entorno de desarrollo y he encontrado que el proxy inverso de host virtual basado en DNS es bastante útil. Tener solo un puerto (y el estándar) abierto en el firewall hace que la administración sea mucho más fácil.

Me gustaría encontrar algo similar para hacer conexiones SSH pero no he tenido mucha suerte. Prefiero no usar simplemente túneles SSH, ya que eso requiere abrir rangos de puertos distintos al estándar. ¿Hay algo por ahí que pueda hacer esto?

¿Podría HAProxy hacer esto?

ahanson
fuente
¿Es la transferencia de archivos de la aplicación prevista o el acceso SSH real a los hosts?
Kyle Hodgson el
El objetivo es el acceso SSH directo al host. Esta necesidad proviene del deseo de ejecutar un servidor Mercurial internamente, pero nuestro servidor está detrás del firewall. En este momento estoy trabajando simplemente en configurar una versión HTTP, pero quería que los commits usaran SSH en lugar de HTTP. El acceso directo SSH a otros servidores sería una ventaja si esto fuera posible.
ahanson el
Este es un problema tan molesto.
sesgo
Tengo entendido que HAProxy ahora admite esto a través de SNI. Aunque todavía no he podido poner esto en marcha.
Carel

Respuestas:

24

No creo que SSH basado en nombres sea algo que sea posible dado cómo funciona el protocolo.

Aquí hay algunas alternativas.

  • Lo que podría hacer es configurar el host que responde para que el puerto 22 actúe como puerta de enlace. Luego puede configurar el servidor ssh para reenviar solicitudes al interior según la clave. Ejemplo de SSH Gateway con claves

  • Puede ajustar su cliente para usar ese host como proxy. Es decir, enviaría ssh al host de la puerta de enlace y luego usaría ese host para establecer una conexión con el host interno. Proxy SSH con configuración del cliente .

  • También puede configurar un proxy HTTP simple en el borde. Luego use eso para permitir conexiones entrantes. SSH a través del proxy HTTP .

Obviamente, con todo lo anterior, es muy importante asegurarse de configurar y bloquear correctamente la puerta de enlace.

Zoredache
fuente
18

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_IGNOREmensaje 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.1y 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 Hostencabezado, 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.
kasperd
fuente
¿Puedes explicar a qué te refieres con the gateway can use the destination IP address to find out which server to send the packet tola solución IPv6?
Jacob Ford
@JacobFord La clave para entender esa oración es que uso la palabra gateway no proxy . Con IPv6 obtienes suficientes direcciones IP para asignar una a cada host y aún tienes direcciones de sobra. Todas las máquinas que pondría detrás del proxy tendrían su propia dirección IP, que es lo que haría que los nombres resolvieran. Por lo tanto, ya no necesita un proxy, los clientes envían su tráfico a la dirección IP del host deseado y puede enrutar el tráfico directamente allí.
kasperd
para la parte de solución alternativa, hay un cambio de comando relativamente nuevo para ssh -J, por lo que puede usar: ssh -J user1 @ front user2 @ target
PMN
3

No creo que esto sea algo posible, al menos de la forma en que lo describiste, aunque me encantaría que me demuestren lo contrario. No parece que el cliente envíe el nombre de host al que desea conectarse (al menos en claro). El primer paso de la conexión SSH parece ser configurar el cifrado.

Además, tendría problemas con la verificación de la clave del host. Los clientes SSH verificarán las claves en función de una dirección IP y un nombre de host. Tendría varios nombres de host con diferentes claves, pero la misma IP a la que se está conectando.

Una posible solución sería tener un host 'bastión', donde los clientes pueden ingresar a esa máquina, obtener un shell normal (o restringido si lo desea), y luego pueden ingresar en hosts internos desde allí.

marca
fuente
El concepto de bastión es lo que tenemos configurado actualmente, pero es problemático para el control de mi versión (sin esfuerzo adicional).
ahanson el
Es súper molesto que ssh no envíe el fqdn y maneje el DNS en el lado del cliente. Supongo que la gente no se preocupó por la hinchazón de IP pública y NAT cuando se crearon la mayoría de los protocolos de nivel de aplicación TCP / IP. Porque, en serio, deberías poder hacer NAT basado en FQDN con iptables (es decir, filtros de kernel).
sesgo
La clave de host podría ser la clave del proxy inverso. Este es un beneficio si confía en la seguridad del backend, ya que tiene el control de la red interna y los hosts.
rox0r
@bias No puedes hacer NAT basado en el nombre de host, incluso si el protocolo de nivel superior envía el nombre de host. La razón por la que no se puede hacer es que la elección del backend debe hacerse cuando se recibe el paquete SYN, pero el nombre de host solo se envía después de que se haya procesado SYN y se haya devuelto un SYN-ACK. Sin embargo, puede usar un proxy de capa de aplicación muy delgado para protocolos que envían nombres de host, como http y https.
kasperd
3

Hay un nuevo chico en la cuadra. SSH piper enrutará su conexión en función de nombres de usuario predefinidos, que deben asignarse a los hosts internos. Esta es la mejor solución en el contexto de proxy inverso.

SSh piper en github

Mis primeras pruebas confirmadas, SSH y SFTP funcionan.

Király István
fuente
Eso es efectivamente un ataque MITM. Espero que se rompa en cada configuración que esté protegida contra ataques MITM.
Kasperd
1
En realidad, la parte anfitriona es ambos, el anfitrión y el hombre en el medio, por lo que solo esos dos están involucrados.
Király István
@ KirályIstván este es un proyecto increíble y demuestra que otras respuestas no son 100% correctas. Creé una herramienta similar basada en github.com/gliderlabs/sshfront y algunos bash / python (no puedo abrir el código fuente, pero no es tan interesante: bash ejecuta el cliente ssh y python se usa como controlador de autenticación). Pero tengo un problema con la solución actual. No admite sftp (scp funciona). Se cuelga tratando de ejecutar el subsistema debug1: Sending subsystem: sftp. Resulta que el frente de la camisa no admite subsistemas. ¿Soportas dobladillo en SSh piper?
Maciek Sawicki
1
@MaciekSawicki No soy el autor. Solo lo uso en mi proyecto. .)
Király István
2

Me pregunto si Honeytrap (el honeypot de baja interacción) que tiene un modo proxy no podría modificarse para lograrlo.

Este honeypot puede reenviar cualquier protocolo a otra computadora. Agregar un sistema vhost basado en nombres (como lo implementa Apache) podría convertirlo en un proxy inverso perfecto para cualquier protocolo, ¿no?

No tengo las habilidades para lograrlo, pero tal vez podría ser un gran proyecto.

Kafeine
fuente
¡excelente idea!
sesgo
1
La función vhost en Apache se basa en que el cliente envíe el nombre de host antes de que se envíe ningún dato desde el servidor. El protocolo SSH no involucra dicho nombre de host, por lo que no veo cómo incluso comenzaría a implementar dicha función. Pero si puede elaborar sus ideas, me complacería implementar una prueba de concepto o decirle por qué no funcionaría.
kasperd
1

A medida que aumenta la cantidad de puertos / hosts a los que desea acceder detrás de un firewall, aumenta la conveniencia de las VPN.

Sin embargo, no me gustan las VPN.

alex
fuente
1

debido a cómo funciona ssh, creo que no es posible. Similar a https, tendría que tener diferentes direcciones IP (externas) para diferentes hosts, porque la puerta de enlace no sabe a dónde desea conectarse porque todo en ssh está encriptado

Jure1873
fuente