8 min de lectura

Cómo una lista de preparación para SSH cambia la seguridad de los agentes

Usa esta lista de preparación para SSH para probar el análisis de resultados, la identidad del host, la idempotencia, las aprobaciones y los estados remotos desconocidos antes de dar acceso de shell a los agentes.

Cómo una lista de preparación para SSH cambia la seguridad de los agentes

HTTP de solo lectura es un primer canal indulgente para un agente. La solicitud tiene un método, un destino acotado, un código de estado, encabezados y, por lo general, un cuerpo con una forma documentada. Un GET fallido normalmente no cambia el servicio. Aun así, puedes gestionar mal las credenciales o exponer datos sensibles, pero el modelo de ejecución da a los revisores algo compacto sobre lo que razonar.

SSH cambia la unidad de riesgo. El agente no llama a una operación definida. Envía texto a un shell remoto cuyo comportamiento depende de la cuenta de inicio de sesión, el shell, el directorio de trabajo, el entorno, el sistema operativo, las herramientas instaladas, las reglas de comillas y el estado actual de la máquina. Un comando puede tener éxito en parte, desconectarse antes de informar su resultado y dejar que el siguiente reintento haga el trabajo dos veces.

Eso no hace que SSH sea inadecuado para agentes. Significa que «el canal HTTP funcionó» es una prueba débil para añadir SSH. La prueba de admisión debe cubrir cinco propiedades distintas: análisis fiable de resultados, identidad de host verificada, comandos seguros de repetir, aprobaciones que describan consecuencias y manejo explícito de estados remotos desconocidos. Si falta una, mantén apagado el segundo canal.

Trata SSH como un modelo de ejecución distinto

La preparación para SSH empieza por reconocer que un shell remoto no es un transporte HTTP con otra URL. Las API HTTP exponen operaciones elegidas por el servidor. SSH expone un intérprete elegido por la cuenta remota y después pide a tu cliente que lleve hasta él una cadena de comandos sin estructura.

Con una llamada HTTP de solo lectura, el revisor a menudo puede inferir el efecto a partir de GET, el host y la ruta. Una respuesta como el estado 404 también tiene un lugar convencional en el protocolo, aunque la aplicación le dé un significado propio del dominio. Con SSH, test -f /srv/app/release && cat /srv/app/release puede devolver 1 porque el archivo no existe, mientras que cat /srv/app/release puede devolver 1 porque se deniega el permiso. Si tu analizador reduce ambos a «el comando falló», el agente no puede decidir con seguridad qué hacer.

El entorno remoto también cambia el significado. sed -i se comporta de forma distinta entre sistemas operativos habituales. Un shell de inicio de sesión puede cargar archivos de arranque que imprimen avisos en stdout. PATH puede encontrar un envoltorio en lugar del binario esperado. La configuración regional puede cambiar los diagnósticos legibles para personas. Un pseudoterminal puede mezclar un comportamiento pensado para una persona con otro pensado para un analizador. Ninguna de estas variables aparece en un esquema típico de solicitud HTTP.

Redacta un contrato de canal antes de habilitar comandos. Debe indicar el usuario remoto, los hosts aceptados, el shell, el directorio inicial, la política de entorno, si se prohíbe un pseudoterminal, el tiempo máximo de ejecución, los límites de salida y la envoltura exacta del resultado. Fija las rutas de ejecutables para acciones sensibles. Establece una configuración regional conocida cuando debas procesar texto. Prefiere salida legible por máquinas del programa remoto, pero no supongas que la opción JSON de un programa vuelve estructurado al shell que lo rodea.

Usa una comprobación no interactiva como primera puerta:

/usr/bin/ssh \
  -o BatchMode=yes \
  -o StrictHostKeyChecking=yes \
  -o ConnectTimeout=10 \
  -T [email protected] \
  'umask 077; printf "%s\n" "{\"probe\":\"ssh-ready\",\"version\":1}"'

OpenSSH documenta BatchMode=yes como una opción que desactiva las solicitudes, incluidas las de contraseña y confirmación de clave de host. -T desactiva la asignación de pseudoterminal. StrictHostKeyChecking=yes rechaza claves desconocidas o modificadas. La stdout esperada es un único objeto JSON, stderr está vacía y el estado de salida remoto es cero. Prueba cada desviación por separado. Un sistema que no distingue un host desconocido de JSON malformado no ha superado esta puerta.

Haz que la envoltura del resultado sea inequívoca

El análisis de resultados está listo solo cuando los errores de transporte, la terminación remota, stdout y stderr siguen separados. Colapsarlos en un único campo de texto genera una falsa confianza peligrosa.

RFC 4254 define stdout como datos de canal y stderr como datos extendidos de canal. También define mensajes exit-status y exit-signal, pero la redacción importa: enviar un estado de salida es recomendable, no obligatorio, y el cliente puede ignorarlo. OpenSSH añade una convención local útil sobre ese protocolo. Su comando ssh sale con el estado del comando remoto, o con 255 si ocurre un error.

No conviertas esa convención en una verdad universal. Un programa remoto puede salir con 255, lo que choca con el valor de error del cliente OpenSSH en el límite del proceso. Un servidor o biblioteca puede omitir el mensaje exit-status. Una señal no equivale a una salida normal distinta de cero. El canal puede cerrarse después de producir salida, pero antes de que el cliente reciba un estado final.

Tu resultado interno debería parecerse a un registro tipado, no a una transcripción:

{
  "phase": "completed",
  "transport": "ok",
  "host": "host.example",
  "host_key_fingerprint": "SHA256:verified-value",
  "exit_status": 0,
  "exit_signal": null,
  "stdout": "{\"state\":\"present\",\"release\":\"2026.07\"}\n",
  "stderr": "",
  "truncated": false,
  "started_at": "request timestamp",
  "finished_at": "result timestamp"
}

Mantén exit_status anulable. Haz que phase distinga al menos entre rechazado, no iniciado, iniciado, completado y desconocido. Registra el truncamiento como dato, nunca recortes la salida en silencio y entregues el resto al agente como completo. Adjunta la huella de host verificada usada en esa conexión, no solo el nombre de host solicitado por el agente.

Después prueba una matriz de resultados. Ejecuta un comando que tenga éxito con salida vacía, otro que salga con 7 después de escribir en ambos flujos, uno terminado por una señal, otro que supere el plazo, uno que emita UTF-8 no válido si tu pila permite bytes y otro que supere cada límite de salida. Corta la conexión del cliente mientras duerme un comando remoto y luego inspecciona el resultado. Tu adaptador no debe inventar exit_status: 0, inferir éxito a partir de stdout ni llamar «fallido» a un tiempo de espera cuando no puede probar si el comando se ejecutó.

La composición de shell merece sus propias pruebas. POSIX indica que una canalización normalmente informa el estado de su último comando, salvo que pipefail esté habilitado. Eso significa que generate | upload puede informar éxito porque upload aceptó una entrada vacía después de que generate fallara. No dependas de los valores predeterminados de un shell interactivo. Pon las operaciones de varios comandos en scripts revisados con manejo explícito de errores y una versión, e invoca un único punto de entrada del script.

Verifica los hosts antes de que un agente pueda alcanzarlos

La verificación de hosts es un problema de inventario, no una pregunta que un agente deba responder. Una credencial de usuario válida prueba quién es el cliente ante el servidor. La clave de host del servidor prueba qué servidor respondió al cliente. Necesitas ambas.

Nunca uses StrictHostKeyChecking=no como solución para la automatización. La documentación actual de OpenSSH dice que esa configuración puede añadir claves nuevas automáticamente y permitir que una conexión continúe cuando cambia una clave de host, sujeta a restricciones. accept-new es mejor porque rechaza claves modificadas, pero sigue confiando en la primera conexión. Para un canal de agentes, aprovisiona la confianza antes de la ejecución y usa StrictHostKeyChecking=yes.

ssh-keyscan ayuda a recopilar claves públicas de host, pero no las autentica. Su propio manual advierte que un atacante de red puede sustituir una clave y aconseja verificar la salida por una vía externa o usarla solo en una red de confianza. Copiar su salida en directo directamente a known_hosts convierte una comprobación de verificación en un registro de quien respondiera primero.

Obtén huellas mediante un plano de control independiente: una consola de instancia en la nube, un registro de creación de imagen, un repositorio de configuración revisado por el propietario del host o una entrega directa del administrador. Guarda el nombre de host, puerto, algoritmos de clave de host permitidos, huellas, propietario, entorno y procedimiento de rotación. Revisa también los alias y los hosts de salto. El destino final puede estar perfectamente fijado mientras un host de salto sin fijar rompe la cadena de confianza.

Una comprobación de admisión útil compara las claves observadas y aprobadas sin cambiar la confianza:

ssh-keyscan -T 5 -t ed25519 host.example > observed.keys
ssh-keygen -lf observed.keys

La salida de huella tiene campos para longitud en bits, huella, etiqueta de host y tipo de clave. Una persona o servicio de inventario fiable compara la huella con el valor suministrado de forma independiente. Solo después de esa coincidencia la automatización debe instalar la entrada de hosts conocidos. El escaneo es evidencia para comparar, no evidencia en la que confiar.

Planifica la rotación antes de imponer la fijación. UpdateHostKeys de OpenSSH puede aprender claves adicionales después de que el servidor se haya autenticado con una clave ya fiable, lo que permite una rotación gradual. Tanto si usas esa extensión como si distribuyes un conjunto nuevo de hosts conocidos, define un período de solapamiento y una vía de emergencia. Una clave modificada debe detener la ejecución y producir un error de identidad distinto. Nunca debe activar un reintento genérico, el borrado automático de la entrada anterior ni una tarjeta de aprobación que pida a un revisor apresurado aceptar una huella sin explicación.

Exige idempotencia en el límite del efecto

Un comando es seguro de reintentar solo cuando repetirlo después de cualquier ejecución parcial produce el mismo estado previsto sin duplicar el efecto. La sintaxis de solo lectura no concede esa propiedad y un estado de salida cero no la prueba.

Algunos comandos son repetibles de forma natural: leer un archivo fijo, comprobar el estado de un servicio o crear un directorio con mkdir -p bajo permisos controlados. Otros necesitan protecciones. Añadir una línea con echo ... >> file, enviar una notificación, crear un usuario con un identificador generado, cobrar una cuenta mediante una herramienta local y reiniciar un servicio no son seguros solo porque el comando de shell sea corto.

El consejo habitual de «reintentar errores transitorios de SSH» es incorrecto en esta capa. Es popular porque reconectar resuelve muchos fallos de red y porque las bibliotecas cliente HTTP normalizan los reintentos. SSH puede perder la conexión después de que el proceso remoto confirme su cambio, pero antes de que el cliente reciba el estado de salida. Un reintento automático repite entonces una acción completada.

Lleva la seguridad de repetición a la operación remota. Da a cada solicitud mutante un identificador de operación estable generado antes de la aprobación. Guarda ese identificador junto al efecto en la misma transacción cuando sea posible. Si la operación vuelve a ejecutarse, devuelve el resultado registrado en lugar de aplicar la mutación de nuevo. Cuando no haya una transacción que abarque el marcador y el efecto, añade una consulta de reconciliación que pueda determinar cuál de los dos se completó.

Un pequeño script de despliegue puede hacer visible el contrato:

#!/bin/sh
set -eu

op_id=$1
release=$2
state_dir=/var/lib/agent-ops
record="$state_dir/$op_id"

test -d "$state_dir" || exit 70
if test -f "$record"; then
  cat "$record"
  exit 0
fi

current=$(/usr/bin/readlink /srv/app/current || true)
if test "$current" = "/srv/app/releases/$release"; then
  /usr/bin/printf '{"operation":"%s","state":"already-current"}\n' "$op_id"
  exit 0
fi

test -d "/srv/app/releases/$release" || exit 66
/usr/bin/ln -sfn "/srv/app/releases/$release" /srv/app/current.new
/usr/bin/mv -f /srv/app/current.new /srv/app/current
/usr/bin/printf '{"operation":"%s","state":"changed","release":"%s"}\n' \
  "$op_id" "$release" > "$record.tmp"
/usr/bin/mv -f "$record.tmp" "$record"
cat "$record"

Este ejemplo no es atómico en todos los casos. El cambio de enlace simbólico y el registro de operación son dos cambios en el sistema de archivos, así que un fallo entre ambos deja un vacío. La comprobación explícita de current reconcilia ese vacío concreto. Tu operación necesita una protección vinculada a su propio efecto, no un marcador genérico copiado de este script.

Clasifica cada comando permitido como de solo lectura, convergente, deduplicado o no repetible. Convergente significa que la ejecución repetida se mueve hacia un estado declarado, como establecer un valor de configuración. Deduplicado significa que el lado remoto reconoce el identificador de operación. Las acciones no repetibles requieren una consulta de estado separada y una decisión humana después de la incertidumbre. Si el propietario no puede clasificar un comando, no lo admitas.

Muestra al aprobador el efecto, no el texto del shell

Revoca una ejecución activa de un agente
El diario de sesiones muestra las ejecuciones activas de agentes y permite revocarlas al instante.

Una aprobación es útil solo cuando un revisor puede identificar el destino, la autoridad, el efecto previsto y la peor consecuencia plausible antes de actuar. El texto de shell sin procesar es evidencia necesaria, pero un resumen deficiente.

Compara systemctl restart api con una aprobación que diga: host de producción api-03, usuario remoto deploy, reiniciar servicio api, las conexiones activas pueden interrumpirse, identificador de operación rel-2026-07-24-04, versión de comando restart-service/v2. La segunda descripción da al revisor hechos que puede contrastar con un cambio. También deja al descubierto el contexto que falta. Si el agente no puede indicar qué host o servicio afectará, no debería obtener aprobación.

La carga de aprobación debe vincularse a la solicitud de ejecución exacta. Incluye el host y puerto canónicos, la huella verificada, la cuenta remota, el resumen del comando o script revisado, los argumentos normalizados, el directorio de trabajo, las adiciones al entorno, el tiempo de espera, el cambio de privilegios solicitado, el identificador de operación y si la acción es repetible. Calcula un hash de esa carga y ejecuta solo el hash aprobado. De otro modo, un agente puede obtener aprobación para un comando y alterar un argumento antes del envío.

Muestra las comillas del shell exactamente, pero no obligues al revisor a ejecutarlas mentalmente. Analiza solo formas de comando que controles. Si sigue estando permitido texto de shell arbitrario, etiquétalo como arbitrario y presenta toda la cadena sin omisiones. Señala redirecciones, sustitución de comandos, tuberías, ejecución en segundo plano, sudo, borrado de archivos, cambios de permisos, operaciones de paquetes, control de servicios y descargas de red. Una señal no es un veredicto. Indica al revisor dónde pueden esconderse las consecuencias.

El alcance de la aprobación debe estrecharse a medida que crecen los efectos. Una aprobación de sesión puede ser razonable para comprobaciones fijas de solo lectura contra un inventario aprobado. Una aprobación por llamada encaja con cambios de estado, uso de una cuenta remota con privilegios o comandos cuyos argumentos eligen el destino. No dejes que una aprobación inocua de uname autorice silenciosamente un despliegue posterior porque ambas comparten una clave SSH.

Los controles fijos de Sallyport encajan bien con esta división: su autorización de sesión identifica un nuevo proceso de agente, mientras que una clave por llamada puede exigir aprobación en cada uso. El trabajo de diseño importante sigue estando en la solicitud: la tarjeta debe revelar el efecto remoto, porque poseer un canal aprobado no explica qué hará un comando.

Prueba la integridad de la aprobación, no solo su apariencia. Cambia un byte de un argumento aprobado y confirma que la ejecución se detiene. Haz competir dos solicitudes que reutilicen un identificador de operación. Revoca la sesión entre la aprobación y el envío. Bloquea el almacén de credenciales después de que aparezca la tarjeta. Cada prueba debe terminar en una denegación registrada o en una solicitud que deba aprobarse de nuevo, nunca en una continuación de mejor esfuerzo.

Modela el estado remoto desconocido como un resultado de primera clase

Desconocido es un resultado válido siempre que el cliente no pueda probar si el efecto remoto se completó. Llamarlo fallo invita a reintentos. Llamarlo éxito oculta trabajo sin terminar.

Recorre una secuencia común. Un agente se conecta, inicia un script y el script reemplaza un archivo de configuración. Empieza la recarga del servicio. En ese momento se cae la ruta de red. El cliente no recibe ni el estado de salida ni stdout final. Un tiempo de espera local se activa y marca la llamada como fallida. El agente reintenta. La segunda ejecución ve el archivo nuevo, envía otra recarga y quizá sobrescribe el registro de diagnóstico de la primera. La llamada original hizo trabajo real aunque el cliente nunca observó su finalización.

RFC 4254 hace esperable esta ambigüedad. El protocolo transporta la salida de comando, el estado de salida, la señal de salida, EOF y el cierre de canal como mensajes separados. Recomienda devolver un estado de salida, pero no lo garantiza. Incluso un cierre limpio de canal solo informa al cliente sobre el canal, no sobre si un sistema externo alcanzó el estado de negocio solicitado.

Define la máquina de estados antes de publicar:

  1. not_started: conexión, identidad, autenticación o aprobación fallaron antes del envío.
  2. started: el lado remoto aceptó el comando, pero aún no existe resultado final.
  3. completed: llegaron un estado final y toda la salida acotada.
  4. unknown: el envío pudo ocurrir, pero el cliente perdió la prueba de finalización.
  5. reconciled: una consulta independiente estableció después el estado resultante.

Solo not_started suele ser seguro para un reintento automático, e incluso esa etiqueta debe proceder de un límite fiable. Si los bytes que contienen el comando pudieron llegar al servidor, usa unknown. Un plazo no cancela un proceso remoto salvo que tengas un protocolo de cancelación confirmado. Cerrar el socket del cliente no es ese protocolo.

Todo comando mutante necesita un plan de reconciliación definido antes de la aprobación. El plan puede consultar el registro de operación, comparar un identificador de versión desplegada, leer el estado del gestor de servicios o pedir al sistema posterior el identificador de operación estable. Ejecuta la reconciliación con una credencial de solo lectura cuando sea posible. Conserva la solicitud original, su salida parcial, marcas de tiempo, huella de host e identificador de operación para que el seguimiento responda a la pregunta correcta.

Establece un presupuesto de estado desconocido. Decide cuánto espera el sistema, quién recibe la alerta, qué acciones se bloquean detrás de la operación sin resolver y cuándo interviene una persona. Nunca permitas que dos operaciones inciertas contra el mismo recurso compitan. Serializa por recurso o usa un bloqueo remoto con propietario y política de caducidad que sobreviva a la desconexión del cliente.

Restringe la cuenta remota antes de ampliar los comandos

Aprueba cada uso sensible de SSH
Marca una clave para aprobación por llamada, de modo que cada acción remota espere tu decisión.

La preparación para SSH depende más de la autoridad remota que de la intención del lado cliente. Una pantalla de aprobación perfecta no puede compensar una cuenta de inicio de sesión capaz de reescribir el host.

Crea una cuenta dedicada para el canal de agentes. Dale el menor acceso posible al sistema de archivos y los permisos de servicio necesarios para las operaciones admitidas. Evita una cuenta general de administrador. Si hace falta elevación, permite comandos con nombre, rutas fijas y argumentos controlados. Trata sudo sin restricciones, escapes de shell dentro de programas permitidos, directorios de scripts editables y ejecutables editables como rutas equivalentes a un acceso más amplio.

Las restricciones de authorized_keys de OpenSSH pueden reducir la exposición de una credencial. Según tu diseño, un comando forzado puede dirigir cada conexión a través de un despachador, mientras que las opciones pueden desactivar pseudoterminales, reenvío de agente, reenvío X11 y reenvío de puertos. La configuración del servidor también puede restringir el reenvío. Usa el manual real del servidor y prueba la configuración efectiva, porque una inclusión o bloque match permisivo puede invalidar tu suposición.

Un despachador debe aceptar un nombre de operación pequeño y datos, validar ambos y llamar a un ejecutable por ruta absoluta sin reconstruir texto de shell arbitrario. Por ejemplo, read-release puede no aceptar argumentos, mientras que activate-release acepta un identificador de versión que coincide con un formato estricto. El canal SSH sigue siendo el transportista, pero la superficie remota empieza a parecerse a una API definida.

No reenvíes el agente de autenticación del desarrollador a una sesión autónoma. El reenvío de agente permite que el lado remoto solicite firmas mediante el socket reenviado mientras dure la conexión. Un host remoto comprometido quizá no pueda extraer la clave privada, pero puede usar la capacidad de firma. Da al canal una credencial dedicada cuya autorización del lado servidor ya sea limitada.

Comprueba la propiedad del sistema de archivos hasta cada ejecutable y archivo de configuración. Si la cuenta restringida puede modificar un directorio padre, reemplazar el despachador, influir en un archivo de inicio cargado o anteponer un binario mediante PATH, la lista de permitidos es decorativa. Comprueba también los intérpretes. Permitir ejecutar un intérprete amplio suele significar permitir hacer cualquier cosa que la cuenta pueda hacer.

Mantén aburrido el primer conjunto de comandos: lecturas fijas de inventario, consultas de estado con salida acotada y una mutación convergente con una ruta de reconciliación probada. El reenvío de puertos, shells interactivos, cargas arbitrarias, gestión de paquetes y comandos root de forma libre deben quedar para revisiones posteriores, si es que llegan a pertenecer allí.

Demuestra observabilidad bajo truncamientos y desconexiones

Audita HTTP y SSH juntos
Ambos canales alimentan un único registro ciego para escritura, así que el segundo canal no divide tu evidencia.

La evidencia de auditoría está lista cuando puede reconstruir autorización, envío, identidad remota y resultado observado sin depender del resumen del propio agente. Los registros deben conservar la incertidumbre en lugar de eliminarla.

Registra un identificador de solicitud y de operación estables, la identidad del proceso o sesión del agente, la decisión de aprobación, el método del aprobador, el hash de la carga aprobada, el destino canónico, la huella de clave de host, la cuenta remota, la hora de inicio, la hora de envío, la hora final, el estado o señal de salida, los recuentos de bytes de cada flujo, las marcas de truncamiento y la clasificación final de estado. Mantén separados stdout y stderr. Si la política prohíbe conservar toda la salida, guarda la parte permitida más un resumen y metadatos claros de retención.

Los límites de salida necesitan dos comportamientos: detener la recopilación local y decidir qué ocurre en remoto. Cerrar simplemente el canal después de un megabyte puede dejar el proceso en marcha. Un envoltorio remoto puede limitar la salida, enviarla a un archivo controlado e informar un resumen, pero ese envoltorio también necesita cuotas de disco y limpieza. Prueba un comando que nunca cierre stdout, un hijo que sobreviva a su padre y un proceso que escriba en stderr sin fin.

Los registros también deben mostrar lo que no ocurrió. Una discrepancia de clave de host, bóveda bloqueada, aprobación rechazada, sesión caducada, solicitud malformada o comando no permitido debe crear un registro de denegación antes de devolver el control. De otro modo, los operadores ven un hueco y no pueden distinguir un sistema silencioso de una omisión.

Sallyport registra ejecuciones de agentes y llamadas individuales en un único registro de auditoría cifrado y encadenado por hash, y sp audit verify comprueba la cadena sin conexión sobre el texto cifrado sin una clave. Eso da al canal evidencia local a prueba de manipulación, pero los identificadores de operación remota y los resultados de reconciliación también deben aparecer en la solicitud y el resultado para que un operador pueda conectar la llamada con el estado de la máquina.

Inyecta fallos mientras recopilas la evidencia que recibiría un revisor de incidentes. Mata el cliente antes del envío, justo después del envío, a mitad de stdout y después de que termine el proceso remoto pero antes de la finalización local. Rota la clave de host sin actualizar el inventario. Llena el sistema de archivos remoto antes de escribir un marcador. Devuelve una salida correcta con salida estructurada malformada. Para cada caso, plantea una pregunta: ¿puede un revisor saber qué autoridad se usó, qué pudo cambiar y qué debe ocurrir después?

Admite SSH solo después de superar las puertas

El segundo canal está listo cuando el equipo puede demostrar su comportamiento ante fallos, no cuando un comando de ruta feliz llega a un host de prueba. Usa una puerta escrita con responsables y evidencia conservada.

El registro de admisión debe contener:

  1. Un contrato de canal que nombre el shell, cuenta, directorio, entorno, tiempos de espera, límites de salida y esquema de resultados.
  2. Un inventario de hosts verificado con una fuente independiente de huellas, cobertura de hosts de salto y un proceso de rotación probado.
  3. Un catálogo de comandos que clasifique el comportamiento de repetición y nombre la consulta de reconciliación para cada mutación.
  4. Una especificación de aprobación vinculada al host, cuenta, versión de comando, argumentos, privilegio, plazo e identificador de operación exactos.
  5. Resultados de inyección de fallos que demuestren estados desconocidos, denegaciones, truncamiento, revocación y reconstrucción de auditoría.

Supera primero las comprobaciones de solo lectura. Después admite una escritura convergente en un entorno desechable. Desconéctala en cada límite y reconcilia el resultado. Repite contra un host parecido a producción con un recurso inocuo. Revisa la evidencia con la persona propietaria de ese host, no solo con el equipo que construyó la puerta de enlace del agente.

Mantén separada la reversión del reintento. Una reversión es una mutación nueva y explícita con su propia aprobación, identificador de operación, condiciones previas y posible estado desconocido. Ejecutar automáticamente un comando inverso después de un tiempo de espera puede dañar un cambio que sí se completó correctamente. El sistema debe establecer el estado actual antes de cambiarlo otra vez.

Establece criterios de retirada junto con los de admisión. Deshabilita una operación cuando el resumen de su script cambie sin revisión, su host salga del inventario, deje de funcionar la reconciliación, la salida deje de estar acotada o los operadores no puedan explicar un resultado desconocido. El acceso al canal no es una insignia permanente de graduación.

SSH se gana la admisión una operación cada vez. Si no puedes fijar el servidor, describir el efecto, repetirlo con seguridad, distinguir todos los estados de resultado y reconstruir la llamada después, el resultado correcto de la lista es «no listo». Mantén el canal HTTP de solo lectura y corrige el límite que falta antes de que un shell remoto convierta un reintento ambiguo en un segundo cambio de producción.

FAQ

¿Cuándo está listo un agente de IA para acceder por SSH?

Un agente está listo cuando la identidad del host está fijada, los comandos producen resultados tipados, las mutaciones se pueden repetir sin riesgo, las aprobaciones se vinculan al efecto exacto y las desconexiones generan un estado desconocido explícito. Un inicio de sesión de prueba correcto solo demuestra conectividad.

¿Es suficientemente seguro habilitar primero SSH de solo lectura?

Es la primera etapa adecuada, pero la cuenta remota aún necesita permisos limitados y salida acotada. Un comando que parece de solo lectura puede ejecutar archivos de inicio, invocar un binario inesperado mediante PATH o exponer secretos en la salida.

¿La automatización SSH debe usar StrictHostKeyChecking no?

No. Aprovisiona claves de host verificadas antes de la ejecución y usa StrictHostKeyChecking=yes. Desactivar la comprobación cambia una pregunta operativa por un fallo de identidad que la automatización quizá no detecte.

¿Puede ssh-keyscan crear known_hosts de forma segura?

ssh-keyscan puede recopilar una clave, pero no autenticar la clave que recibe. Compara su huella con un valor obtenido por un canal independiente de confianza antes de instalarla.

¿El código de salida SSH 0 prueba que el cambio tuvo éxito?

Solo prueba que el estado informado del comando remoto fue cero. El comando puede definir mal el éxito, una canalización puede ocultar un fallo anterior o el estado externo deseado puede seguir siendo incorrecto. Comprueba una salida estructurada o reconcilia el estado.

¿Qué significa el código de salida SSH 255?

El cliente OpenSSH usa 255 cuando encuentra un error y, en caso contrario, devuelve el estado del comando remoto. Como un programa remoto también puede elegir 255, mantén separado el estado de transporte del estado de salida remoto dentro de tu adaptador.

¿Cuándo es idempotente un comando SSH?

Es idempotente cuando repetirlo después de cualquier ejecución parcial produce el mismo estado deseado sin duplicar el efecto. Prueba el límite del efecto, no cómo está escrito el comando, y usa identificadores de operación estables o protecciones basadas en el estado.

¿Debe un agente reintentar automáticamente un tiempo de espera de SSH?

Solo cuando el sistema puede probar que el comando nunca empezó. Si el envío pudo haberse producido, marca el resultado como desconocido y ejecuta una consulta de reconciliación de solo lectura antes de considerar otra mutación.

¿Qué debe mostrar una tarjeta de aprobación SSH?

Muestra el host canónico, la huella verificada, la cuenta remota, el efecto previsto, el comando exacto o la versión del script, los argumentos normalizados, el cambio de privilegios, el tiempo de espera y el identificador de operación. Vincula la aprobación a esa carga para que nada pueda cambiar después del clic.

¿Cómo deben probar los equipos el acceso SSH de los agentes?

Inyecta fallos antes, durante y después de la ejecución remota, incluidos cambios de clave de host, desbordamientos de salida, señales, tiempos de espera y desconexiones. La evidencia debe indicar a un operador qué pudo haber cambiado y qué acción de reconciliación es segura.

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