¿Cómo mantener confiablemente abierto un túnel SSH?

235

Utilizo un túnel SSH del trabajo para sortear varios firewalls idóticos (está bien con mi jefe :)). El problema es que, después de un tiempo, la conexión ssh generalmente se cuelga y el túnel se rompe.

Si al menos pudiera monitorear el túnel automáticamente, podría reiniciar el túnel cuando se cuelga, pero ni siquiera he encontrado una forma de hacerlo.

¡Puntos de bonificación para quien pueda decirme cómo evitar que se cuelgue mi conexión ssh, por supuesto!

Peltier
fuente
¿Tu túnel está muerto por inactividad? Tenía este problema cuando un túnel puertos de mi teléfono, así que finalmente terminó generando órdenes ficticias en la conexión para que sea "vivo" utilizando el watchcomando como: watch -n1 60 echo "wiiiii". El túnel no morirá a menos que la red esté rota o no la use.
erm3nda
1
Relacionado: unix.stackexchange.com/q/200239
sampablokuper

Respuestas:

281

Parece que necesitas autossh . Esto monitoreará un túnel ssh y lo reiniciará según sea necesario. Lo hemos usado durante un par de años y parece funcionar bien.

autossh -M 20000 -f -N your_public_server -R 1234:localhost:22 -C

Más detalles sobre el parámetro -M aquí

KeithB
fuente
2
+1 para autossh, hace lo que dice en la lata. Creo que parte de su funcionalidad también es enviar paquetes de estilo keep-alive para evitar cualquier tipo de tiempo de espera.
akent
30
¿Podría poner el túnel de ejemplo usando autosshen la respuesta?
Ehtesh Choudhury
55
autossh -f -nNT -i ~/keypair.pem -R 2000:localhost:22 [email protected] Puede notar que configuré esto usando -nNT que no crea un terminal remoto para que pueda poner autossh en segundo plano, y la opción -i para que SSH use un archivo .pem. Si va a mantener una conexión abierta todo el tiempo, definitivamente recomiendo pasar por la configuración adicional.
juckele
2
Por lo que vale, parece que generalmente es mejor omitir el -Mparámetro: bugs.debian.org/cgi-bin/bugreport.cgi?bug=351162
rinogo
2
Hice esto para volver a intentar el cambio de red, funciona bien para mí: autossh -M 0 -o "ServerAliveInterval 10" -o "ServerAliveCountMax 2" -L 9999: localhost: 19999 [email protected]
Luke Stanley
39

Todos los firewalls con estado se olvidan de una conexión después de no ver un paquete para esa conexión durante algún tiempo (para evitar que las tablas de estado se llenen de conexiones donde ambos extremos murieron sin cerrar la conexión). La mayoría de las implementaciones de TCP enviarán un paquete keepalive después de mucho tiempo sin escuchar del otro lado (2 horas es un valor común). Sin embargo, si hay un firewall con estado que se olvida de la conexión antes de que se puedan enviar los paquetes keepalive, una conexión inactiva de larga duración morirá.

Si ese es el caso, la solución es evitar que la conexión quede inactiva. OpenSSH tiene una opción llamada ServerAliveInterval que se puede usar para evitar que la conexión permanezca inactiva durante demasiado tiempo (como beneficio adicional, detectará cuándo el par murió antes incluso si la conexión está inactiva).

CesarB
fuente
El intervalo especificado es en segundos, por lo que puede proporcionar algunos ajustes. Si su firewall con estado tiene un tiempo de inactividad de 5 minutos, 60 o 120 segundos son suficientes para mantener la conexión abierta. Es una de las formas en que mantengo abiertas mis sesiones ssh a través del enrutador de mi casa.
Darren Hall
Gracias, esto ayudó. Pero tenga en cuenta (de una respuesta de menor rango aquí, superuser.com/a/146641/115515 ) que si especifica ServerAliveInterval y no ServerAliveCountMax, puede encontrar que ssh se desconecta intencionalmente antes de lo que deseaba.
metamatt
44
@metamatt, esa respuesta de menor rango a la que hace referencia tiene un menor rango por una buena razón: ES INCORRECTO.
Lambart
24

En su propia máquina Mac o Linux, configure su ssh y mantenga vivo el servidor ssh cada 3 minutos. Abra una terminal y vaya a su .ssh invisible en su hogar:

cd ~/.ssh/ 

luego cree un archivo de configuración de 1 línea con:

echo "ServerAliveInterval 180" >> config

también deberías agregar:

ServerAliveCountMax xxxx (high number)

el valor predeterminado es 3, por lo que ServerAliveInterval 180 dejará de enviarse después de 9 minutos (3 del intervalo de 3 minutos especificado por ServerAliveInterval).

David Shaw
fuente
2
Tenga en cuenta que su comando no se recomienda si ya tiene un archivo de configuración. ¡Usar >> para la redirección sería mucho mejor!
Peltier
¿Por qué ServerAliveInterval 180nos da 6 minutos? intuición me hace intentar esto: 180/60 == 3. Entonces, ¿ ServerAliveIntervalfunciona en múltiplos de 30 segundos?
nemesisfixx
@mcnemesis: ServerAliveInterval 180 significa 3 minutos. ServerAliveCountMax predeterminado de 3 significa 3 de esos intervalos, es decir, 9 minutos.
metamatt
2
Voy a votar esta respuesta porque gracias por mencionar ServerAliveCountMax, y qué sucede si especifica ServerAliveInterval sin ServerAliveCountMax. Pero al igual que los comentarios anteriores, noto que el cálculo de "dejará de enviar después" es incorrecto, y creo que esta respuesta sería mejor si solo proporcionara la información sobre estas opciones, sin decirnos cómo aplicarlas con los comandos cd y echo .
metamatt
20
Votación negativa porque no tiene sentido establecer ServerAliveCountMax en un "número alto". ServerAliveCountMax especifica cuántas veces intentará enviar el mensaje "keepalive" antes de darse por vencido. El valor predeterminado es 3, por lo que con ServerAliveInterval 180, dejará de enviarse SOLAMENTE si el servidor NO HA RESPONDIDO después de 9 minutos, en cuyo caso su conexión probablemente esté realmente desconectada.
Lambart
22

He usado el siguiente script Bash para seguir generando nuevos túneles ssh cuando el anterior muere. Usar un script es útil cuando no quiere o no puede instalar paquetes adicionales o usar el compilador.

while true
do
  ssh <ssh_options> [user@]hostname
  sleep 15
done

Tenga en cuenta que esto requiere un archivo de claves para establecer la conexión automáticamente, pero ese también es el caso con autossh.

Jawa
fuente
2
Debería agregar cualquier razón por la que usaría este script sobre autossh, o es solo que es más fácil de esta manera?
kyrias
44
Esto no ayudaría si ssh se congela, ¿verdad?
nafg
1
Ayuda si no puede instalar cosas en el servidor. autossh no viene preinstalado y la bureucracia a veces es muy obtusa.
quarkex
Sí, preferible no tener que instalar cosas. Lo he estado haciendo de esta manera durante un año como mi única forma de mantener accesible una máquina remota (incluso configurar crontab para que se ejecute al reiniciar). Nunca ha fallado y, lo que es más importante, sé por qué nunca fallará.
sudo
16

Systemd es ideal para esto.

Cree un archivo de servicio que /etc/systemd/system/sshtunnel.servicecontenga:

[Unit]
Description=SSH Tunnel
After=network.target

[Service]
Restart=always
RestartSec=20
User=sshtunnel
ExecStart=/bin/ssh -NT -o ServerAliveInterval=60 -L 5900:localhost:5900 user@otherserver

[Install]
WantedBy=multi-user.target

(Modifique el comando ssh para adaptarlo)

  • esto se ejecutará como usuario, sshtunnelasí que asegúrese de que el usuario exista primero
  • problema systemctl enable sshtunnelpara configurarlo para que se inicie en el momento del arranque
  • problema systemctl start sshtunnelpara comenzar de inmediato

Actualización de enero de 2018 : algunas distribuciones (por ejemplo, Fedora 27) pueden usar la política de SELinux para evitar el uso de SSH desde systemd init, en cuyo caso será necesario crear una política personalizada para proporcionar las exenciones necesarias.

IanB
fuente
2
Esto se parece mucho a mi esencia: gist.github.com/guettli/… ¡Los comentarios son bienvenidos!
guettli
Excelente para un systemdsistema. Si se usa Restart=on-failure, matar manualmente al cliente SSH no dará como resultado un reinicio por systemd como el cliente SSH con salida con éxito.
David Tonhofer
Si desea iniciar ssh desde un script (bash) dado como argumento para, ExecStartpor ejemplo, construir la sshlista de argumentos, realice comprobaciones básicas, etc. y luego llámelo desde el script de esta manera exec /bin/ssh -N .... Aquí está mi comando: exec /bin/ssh -N -oExitOnForwardFailure=Yes -oTCPKeepAlive=no -oServerAliveInterval=5 -oServerAliveCountMax=6 -i "${LOCAL_PRIVATE_KEY}" -L "${TUNNEL_INLET}:${TUNNEL_OUTLET}" "${REMOTE_USER}@${REMOTE_MACHINE}"dónde TUNNEL_INLET="127.0.0.1:3307"yTUNNEL_OUTLET="127.0.0.1:3306"
David Tonhofer
10

Me parece seguro que todos están malinterpretando ServerAliveCountMax. Según tengo entendido los documentos, es la cantidad de mensajes activos del servidor que pueden quedar sin respuesta sin que se finalice la conexión. Entonces, en casos como los que estamos discutiendo aquí, establecerlo en un valor alto solo garantizará que no se detecte y finalice una conexión bloqueada.

Simplemente configurar ServerAliveInterval debería ser suficiente para resolver el problema con un firewall que se olvida de la conexión, y dejar ServerAliveCountMax bajo permitirá que el extremo de origen note la falla y termine si la conexión falla de todos modos.

Lo que desea es 1) que la conexión permanezca abierta permanentemente en circunstancias normales, 2) que se detecte la falla de conexión y que el lado de origen salga en caso de falla, y 3) que se vuelva a emitir el comando ssh cada vez que salidas (la forma en que lo hace depende mucho de la plataforma, el script "verdadero" sugerido por Jawa es unidireccional, en OS XI realmente configuró un elemento lanzado).

usuario2793784
fuente
9

Utilice siempre la ServerAliveIntervalopción SSH en caso de que los problemas del túnel sean generados por sesiones NAT expiradas.

Siempre use un método de reaparición en caso de que la conectividad se caiga por completo, tiene al menos tres opciones aquí:

  • programa autossh
  • El script bash ( while true do ssh ...; sleep 5; done) no elimina el comando de suspensión, sshpuede fallar rápidamente y reaparecerá demasiados procesos
  • /etc/inittab, para tener acceso a una caja enviada e instalada en otro país, detrás de NAT, sin reenvío de puertos a la caja, puede configurarla para crear un túnel ssh para usted:

    tun1:2345:respawn:/usr/bin/ssh -i /path/to/rsaKey -f -N -o "ServerAliveInterval 180" -R 55002:localhost:22 user@publicip 'sleep 365d'
    
  • script de inicio en Ubuntu, donde /etc/inittabno está disponible:

    start on net-device-up IFACE=eth0
    stop on runlevel [01S6]
    respawn
    respawn limit 180 900
    exec ssh -i /path/to/rsaKey -N -o "ServerAliveInterval 180" -R 55002:localhost:22 user@publicip
    post-stop script
        sleep 5
    end script
    

o siempre use ambos métodos.

claudiuf
fuente
1
+1 para la opción en línea en caso de que no la desee para todas sus conexiones SSH
usuario1146334
Escribe "en caso de que la conectividad se caiga por completo". Ahora no entiendo, ¿qué problemas soluciona autossh y qué no? Pensé, por supuesto, que se encargaría de cualquier conexión rota, como desconectar el cable durante unas horas, ¿pero tal vez no?
Mads Skjern
6

Resolví este problema con esto:

Editar

~/.ssh/config

Y añadir

ServerAliveInterval 15
ServerAliveCountMax 4

Según la página de manual de ssh_config:

ServerAliveCountMax
         Sets the number of server alive messages (see below) which may be
         sent without ssh(1) receiving any messages back from the server.
         If this threshold is reached while server alive messages are
         being sent, ssh will disconnect from the server, terminating the
         session.  It is important to note that the use of server alive
         messages is very different from TCPKeepAlive (below).  The server
         alive messages are sent through the encrypted channel and there‐
         fore will not be spoofable.  The TCP keepalive option enabled by
         TCPKeepAlive is spoofable.  The server alive mechanism is valu‐
         able when the client or server depend on knowing when a connec‐
         tion has become inactive.

         The default value is 3.  If, for example, ServerAliveInterval
         (see below) is set to 15 and ServerAliveCountMax is left at the
         default, if the server becomes unresponsive, ssh will disconnect
         after approximately 45 seconds.  This option applies to protocol
         version 2 only.

 ServerAliveInterval
         Sets a timeout interval in seconds after which if no data has
         been received from the server, ssh(1) will send a message through
         the encrypted channel to request a response from the server.  The
         default is 0, indicating that these messages will not be sent to
         the server.  This option applies to protocol version 2 only.
nachopro
fuente
Cada 15 segundos parece bastante frecuente hacer ping al servidor.
Lambart
@Lambart, pero si la conexión es muy escasa y cae con frecuencia, al menos detecta una conexión inactiva y le da la oportunidad de volver a intentarlo antes.
binki
4

ExitOnForwardFailure yesEs un buen complemento a las otras sugerencias. Si se conecta pero no puede establecer el reenvío de puertos, es tan inútil como si no se hubiera conectado en absoluto.

jcomeau_ictx
fuente
Esta es una muy buena idea. Incluso autossh es inútil si la conexión anterior se percibe como expirada antes en el lado remoto que en el host local, ya que en este caso el host local intentará conectarse nuevamente, pero el reenvío no se puede establecer porque el puerto aún está abierto.
Raúl Salinas-Monteagudo
1

He tenido la necesidad de mantener un túnel SSH a largo plazo. Mi solución se ejecutaba desde un servidor Linux, y es solo un pequeño programa en C que reaparece ssh usando autenticación basada en claves.

No estoy seguro acerca de la ejecución, pero he tenido túneles muertos debido a los tiempos de espera.

Me encantaría proporcionar el código para el respawner, pero parece que no puedo encontrarlo en este momento.

Baumgart
fuente
1

mientras que hay herramientas como autossh que ayudan a reiniciar la sesión ssh ... lo que encuentro realmente útil es ejecutar el comando 'screen'. Le permite REANUDAR sus sesiones ssh incluso después de desconectarse. Especialmente útil si su conexión no es tan confiable como debería ser.

... no olvide marcar esta es la respuesta 'correcta' si le ayuda k! ;-)

koss
fuente
77
... pero la pregunta era sobre cómo mantener abiertos los túneles SSH, no solo una sesión de terminal. ¡Aunque la pantalla es genial!
akent
Ya uso la pantalla, pero no resuelve mi problema: - / Gracias por tu respuesta, sin embargo.
Peltier
1

Un poco hack, pero me gusta usar la pantalla para mantener esto. Actualmente tengo un avance remoto que ha estado funcionando durante semanas.

Ejemplo, comenzando localmente:

screen
ssh -R ......

Cuando se aplica el reenvío remoto, y tiene un shell en la computadora remota:

screen
Ctrl + a + d

Ahora tiene un avance remoto ininterrumpido. El truco es ejecutar la pantalla en ambos extremos

landypro
fuente
1

Recientemente tuve este problema, porque estas soluciones requieren que vuelva a ingresar la contraseña cada vez que use una contraseña de inicio de sesión. Usé sshpass en un bucle junto con un mensaje de texto para evitar tener la contraseña en el archivo por lotes.

Pensé que compartiría mi solución en este tema en caso de que alguien más tenga el mismo problema:

#!/bin/bash
read -s -p "Password: " pass
while true
do
    sshpass -p "$pass" ssh user@address -p port
    sleep 1
done
Brainfloat
fuente
0

He tenido problemas similares con mi ISP anterior. Para mí fue lo mismo con cualquier conexión tcp, visitar sitios web o enviar correos.

La solución fue configurar una conexión VPN a través de UDP (estaba usando OpenVPN). Esta conexión fue más tolerante con lo que causó las desconexiones. Entonces puede ejecutar cualquier servicio a través de esta conexión.

Todavía puede haber problemas con la conexión, pero dado que el túnel será más tolerante, cualquier sesión ssh sentirá una breve interrupción en lugar de desconectarse.

Para hacer esto, necesitará un servicio VPN en línea que puede configurar en su propio servidor.

hultqvist
fuente
0

Como autosshno satisface nuestras necesidades (existe con error si no puede conectarse al servidor en el primer intento), hemos escrito una aplicación de bash puro: https://github.com/aktos-io/link- con servidor

Crea un túnel inverso para el puerto sshd del NODE (22) en el servidor de forma predeterminada. Si necesita realizar otras acciones (como reenviar puertos adicionales, enviar correos electrónicos en conexión, etc.) puede colocar sus scripts on-connecty on-disconnectcarpetas.

ceremcem
fuente