8 min de lectura

Reenvío de puertos SSH para agentes autónomos: túneles más seguros

El reenvío de puertos SSH para agentes autónomos necesita límites estrictos de ruta, accesos que expiren y registros de aprobación capaces de resistir una revisión de incidentes.

Reenvío de puertos SSH para agentes autónomos: túneles más seguros

El reenvío de puertos SSH para agentes autónomos necesita un diseño más estricto que el acceso habitual de los desarrolladores. Un túnel puede convertir una base de datos, un panel de administración o un servicio de staging que solo estaba disponible en localhost en algo que un agente puede alcanzar con un único comando SSH. Si el agente tiene credenciales SSH amplias, el túnel es solo un síntoma. El problema real es que nadie limitó el destino, la duración ni la autoridad asociada a ese comando.

He visto equipos llamar «temporal» a un túnel porque alguien escribió ssh -L en una terminal. Después la terminal siguió activa durante días, su puerto se convirtió en una dependencia para otro proceso y la persona que lo abrió no estaba disponible cuando seguridad preguntó por qué un servicio cercano a producción tenía un listener sin explicación. El trabajo autónomo empeora este patrón porque el agente puede reintentar, reconectarse y usar un túnel de forma mucho más constante que una persona distraída.

El enfoque seguro es sencillo: proporciona al agente una ruta de conexión con permisos limitados, aprueba una exposición temporal concreta, registra suficiente contexto para reconstruirla más tarde y convierte el cierre en un evento obligatorio, no en una intención.

Un túnel cambia el alcance de red, no solo el comportamiento de SSH

Un reenvío de puertos SSH crea un listener en un lado de la conexión SSH y transporta su tráfico hasta un destino accesible desde el otro lado. Ese listener es una vía de acceso. Revisarlo como «acceso SSH» oculta la parte que crea el riesgo: quién puede conectarse, dónde termina el tráfico y qué puede circular por el túnel.

Imagina un agente que se ejecuta en un host de compilación y necesita consultar db-admin.internal.example en el puerto 5432. Un reenvío local puede hacer que ese servicio aparezca como 127.0.0.1:15432 en el host de compilación. La base de datos sigue siendo privada frente a Internet, pero ahora todo proceso de ese host que pueda alcanzar el listener puede intentar conectarse. El servidor SSH también se convierte en una ruta autorizada hacia la base de datos.

Esto puede ser aceptable. No equivale a permitir que el agente «use SSH para tareas de mantenimiento». La aprobación debe describir esta ruta exacta:

  • carga de trabajo y host que inician la conexión
  • dirección y puerto de escucha
  • host y puerto de destino
  • motivo de la ruta
  • expiración y persona que la aprueba

Un reenvío remoto invierte la ubicación del listener. Si un agente se conecta a un bastión y pide al bastión que escuche en el puerto 18080, un proceso que alcance ese listener puede recibir tráfico de vuelta hacia el host del agente. El reenvío remoto suele sorprender a los equipos porque puede exponer un servicio situado detrás de un firewall sin abrir una regla de entrada en él.

OpenSSH documenta estos modos por separado en ssh(1): -L crea reenvíos locales, -R crea reenvíos remotos y -D crea un proxy SOCKS. Esta separación es útil en la operación diaria. No apruebes el «reenvío de puertos» como un permiso único e indiferenciado. Cada modo expone un listener distinto y necesita restricciones diferentes.

El reenvío local, remoto y dinámico requiere decisiones distintas

Un reenvío local suele encajar en un trabajo de agente cuando este necesita un servicio conocido a través de un host de salto controlado. El listener existe en el lado del agente y el servidor SSH se conecta al destino interno. La forma del comando resulta familiar:

ssh -N \
  -L 127.0.0.1:15432:db-admin.internal.example:5432 \
  [email protected]

-N indica a SSH que no ejecute un comando remoto. No hace que la conexión sea inofensiva. La dirección de enlace 127.0.0.1 limita el listener al host local, mientras que db-admin.internal.example:5432 especifica el destino solicitado. Son propiedades distintas y ambas deben aparecer en el registro de aprobación.

Un reenvío remoto solo es apropiado cuando alguien ha elegido explícitamente crear un listener en el lado remoto. El comando siguiente pide al bastión que escuche en su propia interfaz de loopback y transporte el tráfico de vuelta al puerto 8080 de la máquina del agente:

ssh -N \
  -R 127.0.0.1:18080:127.0.0.1:8080 \
  [email protected]

No consideres automáticamente seguro un listener remoto asociado a loopback. Un proceso local del bastión puede tener mucho más acceso que la máquina de origen del agente, y otro usuario de un bastión compartido podría alcanzarlo. Revisa la población de usuarios y los procesos locales del host remoto antes de permitirlo.

El reenvío dinámico debería denegarse por defecto a los agentes autónomos:

ssh -N -D 127.0.0.1:1080 [email protected]

Esto inicia un listener SOCKS. El cliente elige los destinos más tarde mediante solicitudes SOCKS, en lugar de indicar un destino concreto en el comando SSH. A los equipos les gusta porque permite hacer funcionar rápidamente una interfaz web interna en un navegador o un entorno de pruebas. Esa comodidad elimina el límite de destino que necesitas para el trabajo desatendido. Un revisor no puede aprobar una sola ruta y las restricciones normales de destino de SSH no pueden expresar una lista de permitidos útil para tráfico SOCKS arbitrario.

No intentes resolverlo con un formulario de aprobación más largo. Deniega el reenvío dinámico a la cuenta del agente. Si la tarea necesita varios destinos, define cada reenvío por separado o coloca delante de los servicios un proxy diseñado para ese propósito, con autenticación y registros propios.

La dirección de enlace decide quién puede usar el listener

La dirección de enlace forma parte de la decisión de seguridad porque determina qué máquinas pueden conectarse al listener del túnel. Un reenvío asociado a loopback y otro asociado a todas las interfaces pueden usar el mismo destino y crear exposiciones completamente distintas.

Para un reenvío local, usa una dirección de loopback explícita:

-L 127.0.0.1:15432:db-admin.internal.example:5432

Evita depender del valor predeterminado. OpenSSH normalmente asocia los reenvíos locales a loopback cuando omites la dirección, pero indicar una dirección explícita hace que la revisión, los registros y la investigación de incidentes sean menos ambiguos. También evita que un cambio posterior en la configuración del cliente modifique silenciosamente el alcance del listener.

Esta es la versión peligrosa:

-L 0.0.0.0:15432:db-admin.internal.example:5432

Expone el puerto 15432 en todas las interfaces IPv4 del host cliente. Cualquier host que pueda alcanzar al cliente puede intentar usar el túnel. Un ejecutor de compilación en una subred compartida puede convertirse en un puente hacia una base de datos interna, aunque la base de datos solo acepte conexiones desde el bastión.

IPv6 requiere la misma atención. ::1 es loopback, mientras que :: escucha en todas las interfaces IPv6. Comprueba ambas familias de direcciones. He visto equipos confirmar que 127.0.0.1 era seguro y descubrir después que la automatización también había abierto un listener IPv6 mediante otra ruta de configuración.

En los reenvíos remotos, el servidor SSH controla qué direcciones de enlace acepta. En sshd_config, GatewayPorts afecta al comportamiento de enlace de los reenvíos remotos. OpenSSH documenta que, de otro modo, estos reenvíos se asocian a loopback de forma predeterminada, según la dirección solicitada y la política del servidor. Mantén GatewayPorts no salvo que exista un motivo revisado para permitir listeners remotos más amplios. GatewayPorts clientspecified da demasiado control a los clientes de una cuenta de agente.

Después de abrir un reenvío, inspecciona el listener en la máquina que lo posee. En macOS o Linux, esta comprobación local resulta útil:

lsof -nP -iTCP:15432 -sTCP:LISTEN

La salida debería mostrar un proceso ssh con una dirección como 127.0.0.1:15432 o ::1:15432. Si muestra *:15432, detén el trabajo e inspecciona el comando y la configuración del cliente. Este comando solo demuestra la dirección del listener local. No demuestra que el extremo remoto esté limitado al destino previsto.

Una cuenta SSH dedicada debe expresar la ruta permitida

Una cuenta SSH dedicada con restricciones en el servidor es el límite mínimo razonable para los túneles de agentes. Un archivo de configuración del cliente no puede imponer restricciones frente a un agente que puede cambiar sus propios argumentos. Coloca los límites donde el servidor SSH acepta la conexión.

Para una cuenta de reenvío exclusivamente local, empieza con un bloque como este en sshd_config:

Match User agent-db-tunnel
    PasswordAuthentication no
    KbdInteractiveAuthentication no
    PubkeyAuthentication yes
    PermitTTY no
    X11Forwarding no
    AllowAgentForwarding no
    AllowStreamLocalForwarding no
    AllowTcpForwarding local
    PermitOpen db-admin.internal.example:5432
    GatewayPorts no
    PermitUserEnvironment no

AllowTcpForwarding local permite reenvíos locales y deniega los remotos. PermitOpen indica el destino que esta cuenta puede solicitar. La restricción importa porque, de otro modo, un comando de reenvío local podría apuntar a cualquier host y puerto accesible desde el bastión. Sin PermitOpen, un agente que necesita legítimamente la base de datos también podría solicitar un reenvío hacia una API de administración, una caché, un servicio de metadatos u otro servidor SSH.

Usa los nombres de host con cuidado. El servidor SSH resuelve el host de destino, así que el nombre de PermitOpen debe resolverse allí. Haz que ese nombre sea estable y esté bajo el control de tu infraestructura. Si un nombre DNS puede cambiar a direcciones arbitrarias, la configuración parece limitada mientras el destino real cambia por debajo. Una dirección IP puede ser más clara para un dispositivo fijo, pero un nombre de host funciona cuando el control del DNS interno y la propiedad del servicio son rigurosos.

El manual sshd_config(5) de OpenSSH describe PermitOpen como una restricción de destino con el formato host:port. No convierte la cuenta en un motor de políticas. Cumple una función precisa: rechaza una solicitud de reenvío cuyo destino no esté incluido. Mantén el diseño de la cuenta igual de preciso.

No des un shell a esta cuenta porque «el agente no lo usará». Elimina la capacidad. Una cuenta dedicada debe existir para una sola función de conexión. Configura su clave pública autorizada con un comando forzado que termine si necesitas otra protección contra el uso interactivo, pero prueba esa opción con el comportamiento de reenvío de tu entorno. Algunos patrones de comandos forzados y wrappers interfieren con las sesiones SSH esperadas, y una restricción rota suele llevar a alguien a eliminar todo el bloque bajo presión.

Evita también el reenvío del agente. AllowAgentForwarding no impide que el servidor conectado pida al agente SSH del lado cliente que firme solicitudes de autenticación. Una cuenta de túnel SSH debe transportar tráfico, no convertirse en un trampolín hacia otros hosts.

Para un destino con dos servicios legítimos, incluye dos entradas explícitas de PermitOpen. No uses un comodín ni sustituyas la cuenta por una cuenta de bastión general solo para evitar crear una cuenta adicional. Una cuenta más cuesta menos que explicar por qué un agente de código podía alcanzar toda una subred interna.

Temporal significa expiración obligatoria y cierre limpio

Ejecuta SSH sin entregar las claves
Sallyport realiza acciones SSH mediante sp-ssh sin exponer las claves privadas al agente MCP.

Un túnel solo es temporal cuando algo aparte de las buenas intenciones lo termina. SSH mantiene la conexión hasta que el cliente la cierra, falla la red o un tiempo de espera la interrumpe. Un agente autónomo puede dejar un proceso activo después de que una prueba termine, falle o agote el tiempo.

Asigna a cada trabajo una duración máxima fija de sesión fuera del control del agente. Un ejecutor puede imponerla con timeout cuando esté disponible:

timeout 20m ssh -N \
  -o ExitOnForwardFailure=yes \
  -o ServerAliveInterval=30 \
  -o ServerAliveCountMax=3 \
  -L 127.0.0.1:15432:db-admin.internal.example:5432 \
  [email protected]

ExitOnForwardFailure=yes impide que el trabajo continúe cuando SSH no puede crear el listener solicitado. Sin esta opción, el agente puede continuar y comunicar un error confuso de la aplicación en lugar de un fallo de configuración del túnel. Las opciones de actividad del servidor hacen que SSH termine después de varias respuestas perdidas, lo que ayuda cuando un host cliente pierde conectividad de red sin avisar. No sustituyen el límite de 20 minutos.

macOS no incluye de forma predeterminada el comando GNU timeout. Usa la función de tiempo de espera de tu ejecutor, un proceso supervisado con una fecha límite o un pequeño wrapper que envíe TERM y confirme después que el proceso terminó. No sustituyas la fecha límite por una tarea programada que mate «SSH antiguo». Eso puede detener trabajos no relacionados y te impedirá demostrar qué conexión se detuvo.

Exige también que el trabajo cierre su túnel en su propia ruta de limpieza. Para un proceso que haya iniciado, conserva su PID, envía TERM cuando termine la tarea, espera brevemente y verifica que el listener haya desaparecido con lsof o ss. La fecha límite cubre los fallos; la limpieza se ocupa de la finalización normal.

El multiplexado de conexiones exige especial cuidado. Con ControlMaster y un ControlPath compartido, varias invocaciones de SSH pueden reutilizar una conexión maestra. Esto ahorra tiempo en una sesión de terminal humana. En el trabajo de agentes confunde la propiedad y la expiración: un trabajo puede heredar una conexión aprobada para otro y cerrar un cliente puede no cerrar el maestro. Desactiva el multiplexado para la cuenta del túnel o genera una ruta de control única para cada trabajo aprobado.

Un flujo de mantenimiento que necesite acceso recurrente debe solicitar sesiones recurrentes con límites independientes. No dejes un túnel abierto porque volver a crearlo parezca incómodo. Los pocos segundos que ahorras al configurarlo se convierten en ambigüedad cada vez que alguien investiga una conexión inesperada.

Una aprobación debe describir una conexión que el revisor pueda reconstruir

Un registro de aprobación debe permitir responder quién autorizó una ruta, qué la abrió y si se mantuvo dentro de su límite declarado. Un comentario genérico en un ticket, como «acceso de agente aprobado», no es una prueba. No identifica ningún listener, destino ni duración.

Registra la aprobación antes de iniciar el túnel. Captura la identidad de la persona que la aprobó, la marca de tiempo, el identificador del trabajo o ejecución, la identidad del proceso del agente, la máquina de origen, la cuenta SSH dedicada y la huella de la clave pública. Después registra el reenvío solicitado completo: dirección, dirección de enlace, puerto de escucha, host de destino, puerto de destino, propósito y expiración.

El registro del resultado necesita sus propios campos. Incluye si SSH creó el reenvío, el identificador del proceso cliente, la hora de inicio, el estado final, la hora de cierre y el motivo del cierre. Un tiempo de espera, una limpieza normal, una revocación del operador y un fallo de red tienen significados distintos durante una revisión.

Usa registros estructurados en lugar de prosa que obligue a alguien a deducir los campos. Esta estructura basta para muchos sistemas internos:

{
  "run_id": "run-7c31",
  "approved_by": "oncall-engineer",
  "approved_at": "2025-04-12T14:03:00Z",
  "ssh_account": "agent-db-tunnel",
  "direction": "local",
  "listen": "127.0.0.1:15432",
  "destination": "db-admin.internal.example:5432",
  "expires_at": "2025-04-12T14:23:00Z",
  "purpose": "run migration compatibility check"
}

Los identificadores del ejemplo son marcadores de posición, no un esquema que debas copiar exactamente. Lo importante es la disciplina, no los nombres de los campos: un registro debe vincular a una persona, un proceso, una ruta y una ventana de tiempo.

No permitas que el mismo agente genere la aprobación y la trates como autorización humana. El agente puede explicar por qué quiere una conexión, pero una persona o un flujo gobernado por separado debe decidir si se permite. Si la acción es suficientemente rutinaria para aprobarse automáticamente, expresa esa decisión en una definición de trabajo limitada, con expiración, y conserva la versión de la regla que la autorizó.

La fatiga de aprobaciones es un fallo de diseño. Pedir a alguien que apruebe cada paquete produce clics sin significado. Una aprobación por cada ruta temporal y concreta ofrece algo específico que evaluar. Mantén breve la solicitud, pero incluye la dirección de enlace y el destino exacto. Esas dos cadenas detectan muchas solicitudes incorrectas.

Mantén las credenciales fuera del agente y la autoridad fuera de su prompt

Bloquea la bóveda y detén las acciones
Una bóveda bloqueada rechaza todas las acciones, de modo que un agente desatendido no puede usar las credenciales SSH almacenadas.

Un agente autónomo debe solicitar una conexión, no recibir una clave privada con la que pueda crear conexiones arbitrarias más tarde. Cuando un agente tiene una clave SSH amplia en su entorno de proceso o espacio de trabajo, puede copiarla, llamar a otro cliente SSH, usarla después de que termine el trabajo aprobado o introducirla en una herramienta que no pensabas confiar. Un prompt que diga «úsala solo para la base de datos» no limita ninguna de esas acciones.

Un diseño mejor mantiene las credenciales SSH en un límite de ejecución separado. El agente envía una acción solicitada con los campos de la ruta. Ese límite comprueba que la bóveda esté disponible, que la sesión tenga autorización y que la credencial solicitada cuente con la aprobación necesaria. Ejecuta la acción SSH y devuelve el resultado sin entregar la clave privada al agente.

Sallyport usa este modelo en macOS: su helper sp-ssh integrado realiza acciones SSH mientras las claves SSH permanecen en la bóveda cifrada de la aplicación y no llegan al agente MCP.

Esta distinción importa al revisar un fallo. La custodia de credenciales responde a «¿Podía el agente reutilizar el secreto en otro lugar?». La restricción de rutas responde a «¿Podía esta cuenta alcanzar un destino no aprobado?». La aprobación responde a «¿Quién permitió este uso concreto?». Los equipos suelen agrupar estas preguntas en un solo control y descubren después que su auditoría dice que se usó una clave, pero no qué ruta habilitó.

Haz que la interfaz de solicitud sea más limitada que los argumentos SSH sin procesar. Acepta campos como destination_host, destination_port, listen_address, listen_port y max_duration. Rechaza las opciones proporcionadas por el cliente que cambien el proxy, la dirección del reenvío, la ejecución de comandos remotos, los archivos de identidad o los sockets de control. Si aceptas una cadena de comando ssh arbitraria, has delegado la interpretación de la política al análisis de texto. Este enfoque falla en cuanto alguien añade -R, -D, ProxyCommand o un segundo reenvío.

No expongas un shell general como vía de escape para un flujo de túnel. El agente no necesita ejecutar ssh de forma interactiva cuando la acción consiste en crear un canal limitado. Las acciones concretas son más fáciles de aprobar y de revocar.

Las pruebas de auditoría deben resistir una revisión hostil

Los registros de túneles solo son útiles si un agente comprometido o un proceso local no puede reescribirlos después. Los registros de texto junto al espacio de trabajo del agente resultan cómodos, pero demuestran muy poco cuando la misma identidad puede editar tanto el comando del túnel como su historial.

Registra el evento de aprobación, el de solicitud, el resultado de ejecución y el resultado de cierre como eventos separados con un identificador de ejecución compartido. Conserva los campos originales de la solicitud en lugar de registrar únicamente una línea de comando renderizada. Una línea de comando puede ocultar valores en un archivo de configuración u omitir valores predeterminados que afectaron al listener.

Recopila registros que se corroboren desde más de un lugar. Los registros SSH del bastión pueden mostrar autenticaciones aceptadas y fallos de reenvío. El ejecutor del trabajo puede mostrar la creación y finalización del proceso. El servicio de destino puede mostrar una conexión desde el bastión. Ninguna fuente explica por sí sola todo el evento, pero juntas revelan contradicciones.

Por ejemplo, una aprobación puede autorizar 127.0.0.1:15432 hacia una base de datos durante 20 minutos. Si el registro del cliente dice que se cerró a las 14:23, pero el destino ve tráfico a las 15:10 desde el bastión, investiga de inmediato. El agente pudo abrir otra conexión, reutilizar una sesión maestra compartida o alguien puede estar usando otra ruta con la misma cuenta.

Sallyport proyecta los diarios de sesiones y de llamadas individuales desde un registro de auditoría cifrado y encadenado mediante hashes, y sp audit verify comprueba esa cadena sin conexión y sin la clave de la bóveda. Es una propiedad más sólida que un archivo de registro que un proceso del agente puede modificar, aunque no sustituye unas restricciones débiles de cuenta.

Realiza la verificación durante las revisiones, no solo después de un incidente. Una cadena resistente a manipulaciones te indica si el historial registrado mantiene su continuidad. No puede decirte que registraste suficientes campos desde el principio. Decide qué necesitarás saber durante un mal día y asegúrate de que esos campos entren en el evento antes de iniciar la conexión.

Revoca la ruta antes de investigar la historia

Mantén las claves de los túneles fuera de los agentes
Sallyport mantiene las claves SSH en su bóveda cifrada y el agente solo recibe los resultados de las acciones.

Cuando sospeches que un túnel es incorrecto o se ha utilizado indebidamente, detén primero la ruta de conexión. La investigación puede esperar unos minutos; una ruta expuesta puede mover datos o permitir acceso lateral mientras se debate qué registro es fiable.

Termina el proceso cliente y confirma que su listener haya desaparecido. Desactiva la cuenta SSH dedicada o elimina su clave pública del servidor que acepta la conexión. Si el servicio de destino usa una credencial que pasó por el túnel y pudo quedar expuesta a un proceso no confiable, rótala según el procedimiento del propio servicio. No rotes credenciales no relacionadas solo porque existiera un túnel.

Después conserva las pruebas. Guarda el registro de aprobación, la ruta solicitada, los registros de procesos, los registros del servidor SSH, los registros de conexión del servicio de destino y el resultado del cierre. Registra las horas en una sola zona horaria, preferiblemente UTC. Las investigaciones pierden horas cuando el ejecutor usa hora local, el bastión usa UTC y una aplicación escribe marcas de tiempo sin zona.

Comprueba si existe un segundo listener antes de declarar contenido el evento. En el host del agente, inspecciona el puerto solicitado y los procesos de reenvío cercanos. En el bastión, revisa las sesiones SSH activas y los registros de autenticación de la cuenta dedicada. En el destino, busca conexiones iniciadas después de la expiración prevista. El objetivo es encontrar rutas de acceso que sigan activas, no construir rápidamente una historia ordenada.

Si una aprobación humana permitió la ruta equivocada, corrige primero la superficie de solicitud. Una pantalla de revisión mejor que siga aceptando flags SSH arbitrarios reproducirá el mismo error. Si falló una restricción del servidor, prueba el comando exacto que debía rechazarse después de repararla y conserva la salida del fallo. Los controles generan confianza cuando confirmas que deniegan la ruta que querías denegar.

Prueba las denegaciones antes de que un agente dependa del túnel

Un diseño de túnel solo está listo después de probar tanto la conexión aprobada como sus variaciones prohibidas. Las pruebas del camino correcto detectan errores de escritura. Las pruebas de denegación indican si el límite existe.

Usa una cuenta que no sea de producción e intenta un reenvío que debería funcionar:

ssh -N -o ExitOnForwardFailure=yes \
  -L 127.0.0.1:15432:db-admin.internal.example:5432 \
  [email protected]

Después intenta acceder a un destino que PermitOpen no permite:

ssh -N -o ExitOnForwardFailure=yes \
  -L 127.0.0.1:15433:admin-api.internal.example:443 \
  [email protected]

El segundo comando debería fallar durante la configuración del reenvío. Conserva la salida del cliente y la entrada correspondiente del registro del servidor. Después intenta -R y -D; ambos deberían fallar en una cuenta configurada con AllowTcpForwarding local. Por último, solicita un listener que no sea de loopback con 0.0.0.0 y confirma que el límite de tu solicitud lo rechaza antes de ejecutar SSH, o que el entorno local impide la exposición que quieres evitar.

Prueba la expiración con una duración corta. Inicia el túnel aprobado, deja que se active la fecha límite del supervisor y verifica tres cosas: el proceso SSH termina, lsof ya no encuentra el listener y el registro de cierre identifica la fecha límite en lugar de fingir que la tarea terminó normalmente. Este pequeño ejercicio detecta el problema de los procesos huérfanos antes de que el trabajo de producción dependa de un túnel que nunca se cierra.

Mantén una regla sencilla cuando aumente la presión: un agente autónomo puede recibir la ruta mínima necesaria para su trabajo actual, durante un tiempo limitado y con una aprobación identificada. Si tu configuración no puede expresar esa ruta en una sola línea ni demostrar después que se cerró, el agente tiene más autoridad de red de la que justifica la tarea.

FAQ

¿Cuál es la diferencia entre el reenvío local y remoto de puertos SSH?

Un reenvío local escucha en la máquina que inicia SSH y envía las conexiones a través del servidor SSH hacia un destino accesible desde ese servidor. Un reenvío remoto hace lo contrario: el servidor SSH escucha y envía el tráfico de vuelta a través del cliente. Trata ambos casos como cambios en la exposición de red, porque cualquiera de las dos direcciones puede atravesar un límite que el firewall no debía cruzar.

¿Es seguro un túnel SSH si lo asocio a 127.0.0.1?

No. Asociar el reenvío a loopback limita quién puede conectarse al puerto de escucha, pero no limita lo que el servidor SSH puede alcanzar al otro lado. Restringe tanto la dirección de escucha como el destino permitido en la configuración de la cuenta SSH.

¿Debería permitirse que un agente autónomo use ssh -D?

El reenvío dinámico crea un proxy SOCKS, por lo que el cliente puede solicitar muchos destinos en lugar de un servicio concreto. Esto dificulta la revisión, el registro útil y las restricciones mediante las reglas habituales de reenvío SSH. No lo permitas a un agente autónomo salvo que exista una puerta de enlace independiente y estrictamente controlada para ese fin.

¿Cómo restrinjo una cuenta SSH a un solo servicio interno?

Usa una cuenta dedicada, desactiva el acceso al shell, desactiva la asignación de TTY, desactiva el reenvío del agente y permite solo pares concretos de host y puerto mediante PermitOpen. Define también una duración de sesión fuera de SSH, porque la configuración de SSH por sí sola no decide cuándo debe terminar un túnel legítimo.

¿Qué debe incluir el registro de aprobación de un túnel SSH?

Registra la identidad de la persona que aprobó, el proceso o carga de trabajo del agente que inició la conexión, el host de origen, la cuenta SSH, la dirección del túnel, la dirección de escucha, el host y puerto de destino, la hora de apertura, la expiración prevista y el resultado del cierre. Un registro que solo diga «SSH aprobado» no sirve cuando alguien deba entender la exposición durante una investigación.

¿Cuánto tiempo debe permanecer abierto un túnel SSH temporal?

Usa un túnel por trabajo cuando el trabajo tenga un propósito definido y una duración breve. Los túneles de larga duración son más difíciles de atribuir, facilitan que otros trabajos los reutilicen y suelen sobrevivir al cambio que los justificó. Si un servicio necesita conectividad permanente, crea una vía de acceso administrada en lugar de llamar temporal a una sesión SSH permanente.

¿AllowTcpForwarding local impide el acceso a todos los hosts internos?

AllowTcpForwarding local permite el reenvío local, pero bloquea el remoto. Por sí solo no restringe los destinos, así que debes combinarlo con entradas PermitOpen host:port. Aplica esas restricciones a la cuenta que acepta la conexión, no solo a una configuración de cliente que el agente pueda editar.

¿Puedo dar a un agente de IA una contraseña SSH en lugar de usar una puerta de enlace?

Una contraseña no proporciona al agente una ruta de acción restringida y auditable. Normalmente le permite abrir un shell, copiar datos, crear reenvíos arbitrarios y actuar sin un registro de aprobación útil. Usa una cuenta cuyas reglas del servidor expresen la única conexión que quieres permitir.

¿Cómo puedo verificar que un reenvío de puerto SSH se cerró realmente?

ss o lsof indican si un proceso escucha localmente, y la salida de depuración de SSH puede confirmar el reenvío solicitado. Ninguno demuestra que el servidor aceptara únicamente un destino aprobado ni que el túnel se cerrara después. Revisa también la configuración del servidor, el registro de la sesión y los registros del servicio de destino.

¿Qué debo hacer si un agente abre el túnel SSH equivocado?

Termina el proceso cliente SSH, revoca o desactiva su cuenta dedicada y elimina el acceso al servicio de destino si sospechas un uso indebido. Después conserva los registros de aprobación y conexión antes de cambiar más configuraciones. No rotes automáticamente todas las credenciales, salvo que el túnel transportara una credencial que el otro lado pudiera haber capturado.

Sallyport

Sallyport ejecuta llamadas de API y comandos SSH por tu agente de IA. Las claves se quedan en una bóveda local de tu Mac; tú apruebas cada ejecución y cada acción queda en un registro sellado.

© 2026 Sallyport · Código abierto bajo Apache-2.0 · Oleg Sotnikov