SSH: Proporcione fds "pipe" adicionales además de stdin, stdout, stderr

12

Cuando se conecta a un host con SSH, por lo general se proporcionan tres "tubos" entre anfitrión y huésped, para stdin, stdout, y stderr.

¿Existe una opción de línea de comandos para crear reenvíos para descriptores de archivo adicionales ( 3y en adelante)?

Por ejemplo, me gustaría hacer

ssh --forwardfd=10:3 remotehost 'echo test >&3'

que imprimiría 'prueba' al descriptor de archivo abierto localmente 10.

ratones
fuente
2
Probablemente no sin ediciones de origen complicadas, dadas las diversas closefrom(STDERR_FILENO + 1)llamadas bajo el código fuente de OpenSSH. ¿Qué estás tratando de hacer que exige esto?
thrig
El protocolo admite el túnel de flujos adicionales además de stdin/ out/ err, pero AFAIK, ningún servidor / cliente proporciona soporte de ninguna manera.
salva
@thrig Not OP, y ha pasado mucho tiempo, pero en caso de que todavía tenga curiosidad por lo que esto podría ser útil, lo que esperaba encontrar aquí era una pista sobre cómo canalizar a través de ssh, un script para bash y el stdin para ese script. Algo parecido a:infinite-output-cmd | ssh user@host bash /proc/self/fd/3 3< local-script-to-execute-remotely.sh
JoL
@thrig Se me ocurre que algo como eso --forwardfdni siquiera debería ser necesario. sshpodría verificar cuáles son los descriptores de archivos abiertos antes de abrir cualquier otra cosa y reenviarlos automáticamente a los mismos descriptores de archivos en el lado remoto. Podría ser totalmente transparente como mi ejemplo. Me pregunto qué difícil sería arreglarlo ssh. Como dijiste, podría ser complicado dependiendo de las razones detrás de eso closefrom(STDERR_FILENO + 1).
JoL

Respuestas:

6

Puede hacerlo utilizando el reenvío de socket, que está disponible desde openssh-6.7. Este es algún tipo de tubería. Esta técnica se describe, por ejemplo, aquí: http://www.25thandclement.com/~william/projects/streamlocal.html

Obtendrá una ruta en dos direcciones para sus datos. Hay un ejemplo con mysql:

Conexiones de cliente MySQL proxy en un servidor remoto a su instancia local:

ssh -R/var/run/mysql.sock:/var/run/mysql.sock \
    -R127.0.0.1:3306:/var/run/mysql.sock somehost 
Jakuje
fuente
1

Estoy seguro de que debería ser posible. Solo puedo sugerir un truco donde use conexiones ssh adicionales para que cada uno lleve otro par de descriptores de archivo. Por ejemplo, el siguiente script de prueba de concepto realiza un primer ssh para ejecutar un comando ficticio (suspensión) para conectar los fds locales 5 y 6 a stdin y stdout remotos, suponiendo que estos fds son los que desea agregar a los habituales 0,1, 2)

Luego se realiza el ssh real, y en el control remoto conecta los fds remotos 5 y 6 al stdin y stdout del otro ssh.

Solo como ejemplo, este script pasa una página man comprimida al control remoto, que descomprime y ejecuta a través de man. El stdin y el stdout del ssh real todavía están disponibles para otras cosas.

#!/bin/bash
exec 5</usr/share/man/man1/ssh.1.gz 6>/tmp/out6 # pretend need 5 and 6

ssh remote 'echo $$ >/tmp/pid; exec sleep 99999' <&5 >&6 &
sleep 1 # hack. need /tmp/pid to be set

ssh remote '
  pid=$(</tmp/pid) 
  exec 5</proc/$pid/fd/0 6>/proc/$pid/fd/1
  echo start
  gzip -d <&5 | man /dev/stdin >&6
  echo stop
  kill -hup $pid
'
wait
less /tmp/out6
meuh
fuente
1

El problema con la respuesta de @jakuje es: solo funciona con sockets , pero no puede utilizar herramientas UNIX estándar que esperan archivos con ellos:

ssh -R/tmp/sock.remote:/tmp/sock.local "$HOST" 'LANG=C cat >/tmp/sock.remote'

bash: /tmp/sock.remote: No existe tal dispositivo o dirección

También existe el problema de que el archivo de socket local no se elimina en el host remoto; la próxima vez que ejecute el mismo comando, recibirá una advertencia y el socket no se volverá a crear correctamente. Puede dar la opción -o StreamLocalBindUnlink=yesde sshdesvincular ese socket antiguo, pero en mis pruebas no fue suficiente; también tiene que editarlo sshd_configpara contener StreamLocalBindUnlink=yesesa opción para que funcione.

Pero puede usar socato netcatcualquier otra herramienta similar que admita sockets locales UNIXNOnetcat-traditional es suficiente!) Para usar el reenvío de sockets locales para la transferencia de archivos:

# start local process listening on local socket, which receives the data when ssh opens the connections and saves it to a local file
nc -l -N -U /tmp/sock.local >/tmp/file.local &
# connect to remote $HOST and pipe the remote file into the remote socket end
ssh \
 -o ExitOnForwardFailure=yes \
 -o StreamLocalBindUnlink=yes \
 -R /tmp/sock.remote:/tmp/sock.local \
 "$HOST" \
 'nc -N -U /tmp/sock.remote </tmp/file.remote'

También puede ejecutar comandos interactivos, en cuyo caso debe usar ssh -tpara asignar TTY.

El problema con esta solución es que debe codificar las rutas de los sockets locales de UNIX: localmente, este no es un problema tan grande como puede incluir $$en la ruta para que sea único por proceso o usuario, un directorio temporal, pero en el es mejor que no use el directorio de escritura mundial /tmp/como lo hago en mi ejemplo. El directorio también debe existir cuando sshse inicia la sesión. Y el inodo del socket permanecerá incluso después de que se cierre la sesión, por lo que usar algo como "$ HOME / .ssh. $$" desordenará su directorio con inodos muertos a lo largo del tiempo.

También puede usar los sockets TCP vinculados localhost, lo que le ahorrará el desorden de sus sistemas de archivos con inodos muertos, pero incluso con ellos aún tiene problemas para elegir un número de puerto (único) no utilizado. Entonces todavía no es ideal. ( sshtiene código para asignar puertos dinámicamente, pero no encontré ninguna forma de recuperar esa información en el host remoto).

Probablemente, la solución más fácil para copiar archivos es usar la funcionalidad de intercambio de conexiones incorporada de ssh y hacer un comando scpo sfrpmientras su sesión interactiva todavía se ejecuta en paralelo. Consulte Copiar un archivo al sistema local con ssh .

pmhahn
fuente