Multiplexar o Reutilizar SSH: acelerando el login

Publicado por Diego Córdoba en

En este post veremos cómo podemos multiplexar o reutilizar una conexión de SSH contra un servidor para acelerar la autenticación o login de las siguientes conexiones.

Este post complementa el contenido del Curso online de administración SSH.

Motivación de multiplexar o reutilizar SSH

Supongamos que necesitamos realizar transferencia de muchos archivos entre dos sistemas en Internet. Para hacerlo de forma segura una excelente opción es usar scp (Secure Copy over SSH), o herramientas como rsync.

En algunos casos, por ejemplo, cuando tenemos que copiar archivos desde un servidor a otro, y los mismos se encuentran en diferentes orígenes y se copian a distintos destinos, trabajar con rsync puede ser complicado.

Si esas copias se realizan en un script de la shell, una excelente opción es usar scp para copiar archivos o directorios individuales.

El problema, si hilamos un poco más fino, es que cada vez que se ejecute un comando scp se establecerá un túnel SSH para la transferencia del contenido, luego se cerrará, y con la siguiente ejecución de scp se creará un nuevo túnel.

La creación del túnel, el proceso de autenticación/login, y intercambio de claves subyacente llevan su tiempo, y si la cantidad de túneles a crear es grande, este tiempo puede ser bastante importante.

Y surge la pregunta: ¿Puedo crear un túnel SSH con el primer comando, y luego reutilizarlo para todos los demás, ahorrando el tiempo de establecimiento de conexión?

Sí, se puede, y en este post te explico cómo hacerlo 🙂

Multiplexar o Reutilizar SSH: caso práctico

Podemos reutilizar o multiplexar varias conexiones SSH hacia un solo servidor mediante el uso de sockets de tipo Unix (un archivo en el filesystem).

Cabe aclarar que se trata de todas las sesiones SSH establecidas con igual origen y destino, es decir, si creamos un túnel SSH desde nuestra computadora a un servidor remoto, podremos reutilizar ese túnel para otras sesiones. Para cada servidor remoto, obviamente, deberemos crear un nuevo túnel.

Así, podemos crear un socket Unix por cada conexión hacia un servidor distinto, y la segunda conexión a cada uno de esos servidores reutilizará el túnel creado por la anterior, por lo que la autenticación/login no se producirá.

Prueba de concepto inicial

Manos a la obra, vamos a crear nuestro primer túnel SSH reutilizable usando sockets Unix!

Los sockets Unix son archivos en nuestro filesystem, podemos escribirlos en cualquier parte del disco. Yo, por una cuestión de orden, voy a hacerlo dentro de un directorio llamado sockets/ dentro de mi configuración de usuario SSH local:

mkdir ~/.ssh/sockets/

Luego, vamos a agregar las siguientes líneas en el archivo de configuración local del cliente ssh: ~/.ssh/config:

Host *
    ControlMaster auto
    ControlPath ~/.ssh/sockets/%r@%h:%p
    ControlPersist 600s

Estas líneas crean un bloque que de opciones que se va a cargar para cualquier conexión remota (por eso el asterisco en Host *).

Aquí:

  • ControlMaster auto: permite reutilizar una conexión SSH por múltiples sesiones. Por defecto está seteado a no. El parámetro auto permite al cliente SSH utilizar una conexión existente, y si no existe, crear una nueva (el caso de la primer sesión contra un servidor).
    Esta opción requiere de otra opción: ControlPath, para funcionar.
  • ControlPath <ruta>: esta opción permite especificar la ruta, dentro del sistema de archivos, al socket Unix que se utilizará para compartir la conexión entre varias sesiones.
    En mi caso, el archivo será ~/.ssh/sockets/%r@%h:%p (más adelante explico estos comodines/wildcards).
  • ControlPersist 600s: esta opción debe ser usada junto con ControlMaster, y permite especificar el tiempo que la conexión principal permanecerá activa en segundo plano una vez que todas las sesiones contra un determinado servidor se hayan cerrado. Si la conexión está activa, cualquier nueva sesión la utilizará. Una vez que haya expirado el tiempo, la conexión se cerrará, se eliminará el archivo de socket Unix, y la siguiente sesión creará una nueva conexión.
    Aquí he especificado 600 segundos, pero otras opciones válidas son no (para que se cierre la conexión al cerrar la última sesión) o yes (para mantener permanentemente la conexión abierta).

Los comodines o Tokens de ControlPath

Los comodines o tokens válidos para el nombre de archivo son los siguientes (extraídos directamente desde man ssh_config):

%% Un caracter ‘%’
%CEquivalente a %l%h%p%r
%dDirectorio local del usuario.
%fEl fingerprint de la clave del servidor
%HEl nombre o dirección del host al que se conecta, extraído de known_hosts.
%hEl hostname del servidor remoto
%iEl UID del usuario local
%KLa clave del host codificada usando Base64
%kSi está especificado, el alias de la clave del host, de lo contrario, el nombre de host remoto especificado en la línea de comando de conexión
%LEl hostname del equipo local
%lEl hostname del equipo local, incluyendo el dominio completo
%nEl hostname remoto original pasado por argumento en la línea de comando de conexión
%pEl puerto de la conexión remota
%rEl nombre de usuario remoto
%TLa interfaz local de tipo tun o tap asignada para el túnel SSH en el caso de que se haya solicitado reenvío de túnel. En caso contrario será NONE.
%tEl tipo de clave utilizada para el servidor remoto, por ejemplo, ssh-ed25519.
%uEl nombre de usuario local

En nuestro caso, el archivo especificado es: %r@%h:%p, o sea, usuarioRemoto@hostRemoto:puerto, lo que nos va a permitir identificar perfectamente qué archivo de socket corresponde con cada conexión remota.

Conectando y analizando

Nada más, conectemos contra un servidor y veamos qué pasa.

En mi caso, realicé la siguiente conexón:

ssh d1cor@test.juncotic.com

Y el archivo de socket que generó fue:

srw------- 1 diego diego 0 feb 20 19:49 .ssh/sockets/d1cor@test.juncotic.com:22

Si analizamos los procesos que están corriendo, se puede ver el cliente ssh, y el proceso que gestiona el socket creado contra el servidor para establecer la conexión:

reutilizar conexiones SSH una conexion

Conectemos contra el mismo servidor en otra terminal, y veamos qué pasa con los procesos:

reutilizar conexiones SSH dos conexiones

Aquí se puede ver que se mantuvo el archivo, y se creó otra conexión. Por cierto, la segunda conexión ya se notó mucho mas rápida en el login (cierto, no hubo login xD).

Si conectamos a un host diferente, se verá el proceso de cliente SSH, y un nuevo proceso de gestión de un nuevo socket. En mi caso conecté usando este comando:

ssh d1cor@test2.juncotic.com
reutilizar conexiones SSH procesos

Y por supuesto, tenemos un nuevo archivo de socket Unix:

srw------- 1 diego diego 0 feb 20 20:00 d1cor@test2.juncotic.com:22
srw------- 1 diego diego 0 feb 20 19:49 d1cor@test.juncotic.com:22

Multiplexar o Reutilizar SSH: otras consideraciones

En mi ejemplo utilicé el encabezado de bloque Match Host *, lo que implica que esas líneas de configuración serán válidas para cualquier conexión que establezca contra cualquier servidor SSH remoto (de hecho, podría haber omitido el bloque match y carga las opciones como globales :P).

Si quisiéramos que estas configuraciones fueran válidas para ciertos y determinados hosts remotos, o usuarios, etc, podemos especificar un patrón más restrictivo en el bloque match.

Si quieren aprender a configurar correctamente bloques Match en el cliente SSH, les recomiendo pasar por esta lectura: Bloques Match: Configurando SSH server al detalle.

Por otro lado, recordemos que las configuraciones que el cliente lee desde ~/.ssh/config pueden especificarse por línea de comandos. Por lo que, si no quisiéramos que estas configuraciones fueran persistentes, podríamos haber ejecutado los comandos ssh pasando todas las opciones por argumento… algo así:

ssh -o ControlMaster=auto -o ControlPath=~/.ssh/sockets/%r@%h:%p -o ControlPersist=600s d1cor@test.juncotic.com

Claramente se ve que si vamos a usar estas opciones a menudo, conviene cargarlas en el archivo de configuración.

Gestionando la conexión Master

Veamos ahora algunos comandos de gestión de túneles SSH compartidos.

Desactivar el multiplexado para una sola conexión

Si hemos habilitado multiplexado de canal SSH para todos los hosts, y deseamos conectarnos a uno de ellos sin usar el canal reutilizable, podemos desactivar el ControlMaster únicamente para esa conexión en la llamada a ssh:

ssh -o 'ControlMaster=no' d1cor@test.juncotic.com

Otra forma es desactivar la ruta al socket Unix:

ssh -o 'ControlPath=no' d1cor@test.juncotic.com

Chequeando el estado del túnel multiplexado

Una vez que hemos establecido el canal compartido, podemos verificar su estado utilizando la opción check del cliente ssh:

ssh -O check d1cor@test.juncotic.com

Si el túnel SSH ya está establecido, obtendremos una salida similar a esta:

Master running (pid=400478)

Si no, el comando nos informará que el socket Unix no está creado:

Control socket connect(/home/diego/.ssh/sockets/d1cor@test.juncotic.com:22): No such file or directory

Deteniendo conexiones multiplexadas

Si tenemos un canal SSH multiplexado y deseamos eliminarlo en un momento determinado, en tiempo real, y además queremos cerrar todas las sesiones SSH establecidas usando dicho canal multiplexado, podemos usar la opción de control exit:

ssh -O exit d1cor@test.juncotic.com

En la consola obtendremos una salida como esta:

Exit request sent.

Y cada cliente recibirá un mensaje similar a este, y se cerrará su conexión SSH:

Shared connection to test.juncotic.com closed.

Si deseamos mantener las conexiones activas, pero queremos dejar de recibir nuevas conexiones en el canal compartido, podemos eliminar el socket (como se ve

¿Realmente esto acelera las conexiones?

Analicemos la siguiente captura:

multiplexar o reutilizar reutilizar SSH practica

Primero se ve el tiempo necesario para establecer una conexión contra un servidor, ejecutar el comando /bin/true (retorna true nada más) y cerrar la sesión.

Luego, la ejecución de una nueva sesión, igual que la anterior, contra el mismo servidor. Se ve que el tiempo real de ejecución del comando fue mucho menor (bajó de poco menos de 3 segundos a casi medio segundo!).

Luego eliminé el archivo de socket como vimos en la sección anterior, para demostrar que realmente es la fuente magia de este procedimiento, y volví a realizar las dos conexiones, con resultados similares los anteriores.

Sí, efectivamente, una vez que la conexión se estableció la primera vez, se autenticó el usuario y se creó el archivo de socket Unix, las siguientes conexiones van mucho más rápidas en el inicio.

Conclusiones

Hemos aprendido a crear conexiones reutilizables con nuestro cliente SSH!

Los nombres y rutas de los sockets son totalmente personalizables.

Una vez establecido el túnel y el socket, cualquier comando que haga uso de SSH contra el mismo servidor hará us de dicho canal previamente creado, por lo que se verán beneficiados comandos como sftp, scp y rsync.

Nunca está de mas aclarar lo siguiente: este procedimiento acelera la autenticación/login de las conexiones SSH, pero no acelera las transferencias posteriores de datos que se realicen por el túnel.

Para acelerar las transferencias existen opciones de compresión y optimización… pero eso es tema de otro post 🙂

Finalmente, recordemos que SSH es un protocolo gigante, y permite muchísimas posibilidades, por lo que pueden ocurrir conflictos de opciones, y muchas veces algunos mecanismos deben desactivarse en favor de otros.

Por ejemplo, opciones como X11Forwarding, ssh-agent o port forwarding requieren de su propio canal no compartido, por lo que esto que hemos visto será difícil de implementar. Igualmente, no es imposible, pero eso será historia para otro post 🙂

Espero que les haya sido útil! Hasta la próxima!


¿Preguntas? ¿Comentarios?

Si tenés dudas, o querés dejarnos tus comentarios y consultas, sumate al grupo de Telegram de la comunidad JuncoTIC!
¡Te esperamos!


Diego Córdoba

- Ingeniero en Informática - Mg. Teleinformática - Tesis pendiente - Docente universitario - Investigador