# Seguridad de agentes: un solo proceso frente a un daemon

Un diagrama de procesos puede hacer que un producto de seguridad parezca tranquilizadoramente sencillo o impresionantemente serio. Ninguna de esas impresiones indica si un agente de programación autónomo puede usar mal una credencial, comunicarse con un servicio local no previsto o dejar una acción sin registrar después de un bloqueo.

La comparación útil es concreta: cuenta los límites de confianza, inspecciona las interfaces que los atraviesan y sigue un secreto desde su almacenamiento hasta la solicitud saliente. Una aplicación de un solo proceso puede reducir las piezas locales. Un daemon puede aislar privilegios o mantener un servicio activo para varios clientes. Ambos diseños fallan cuando sus autores confunden la separación de procesos con la autorización.

## El número de procesos no define el límite de seguridad

Un diseño de un solo proceso coloca la interfaz de usuario, el almacén de credenciales, el estado de aprobación, la validación de solicitudes, la acción de red y el registro en el mismo espacio de direcciones. No hay un socket de cliente local entre sus componentes, ni un registro de servicio independiente que mantener, ni un protocolo que un proceso local use para ordenar a otro. Eso puede reducir de forma significativa la superficie de ataque.

También crea un único dominio de confianza. Si un atacante consigue ejecutar código dentro de ese proceso, puede alcanzar todos los componentes a los que el proceso tenga acceso. La corrupción de memoria, la carga de plugins maliciosos, las actualizaciones inseguras o una interfaz de scripting demasiado amplia son especialmente importantes porque el almacén y el ejecutor están junto al código normal de la aplicación.

Un diseño basado en un daemon divide esas funciones. Una aplicación de escritorio o un cliente de línea de comandos envía una solicitud a un servicio en segundo plano. El servicio puede guardar los secretos y realizar la llamada de red, mientras el cliente muestra las aprobaciones y recibe los resultados. Esto puede limitar lo que un cliente comprometido puede hacer, pero solo si el daemon rechaza las solicitudes que el cliente no está autorizado a realizar.

Esa condición suele omitirse en las conversaciones sobre arquitectura. Un socket Unix restringido a una cuenta de usuario es acceso al transporte, no una prueba de que el solicitante sea el agente o la aplicación aprobada. Todos los procesos ejecutados por ese usuario pueden tener la posibilidad de abrirlo. Si el daemon acepta de cualquier interlocutor local una solicitud como «usar la credencial X contra la URL Y», una extensión de editor maliciosa, un script de shell o un agente comprometido puede pedir la misma acción.

Separa estas tres ideas:

- Un límite de proceso separa la memoria.
- Un límite de privilegios restringe lo que el código puede hacer después de un compromiso.
- Un límite de autorización decide qué solicitante puede pedir cada acción.

Los equipos suelen tratar la primera como una prueba de las otras dos. No lo es. Dos procesos ejecutados por el mismo usuario y conectados mediante un canal IPC sin autenticar tienen montones separados, pero pueden seguir perteneciendo al mismo dominio de autorización.

Un proceso único evita un problema de autorización IPC porque sus llamadas internas no atraviesan el límite de un servicio local. Eso no elimina la necesidad de identificar una sesión de agente externa antes de aprobar una acción. Un daemon añade ese problema de identificación dos veces: en su endpoint de cliente y, a menudo, de nuevo en cualquier endpoint administrativo que exponga.

## Un socket local es una API que puede llamar código hostil

Los defensores de los daemons suelen decir, correctamente, que el servicio solo escucha en localhost o en un socket Unix. Lo que suele faltar es que el código local es precisamente donde se ejecutan la mayoría de las integraciones de agentes. El agente de programación, el terminal, el editor, los scripts de compilación, los hooks de paquetes y las extensiones del navegador comparten la máquina.

Trata un endpoint IPC local como tratarías una pequeña API de red. Define formatos de mensaje estrictos. Rechaza los campos desconocidos cuando el protocolo lo permita. Vincula cada solicitud a una identidad de solicitante y a una sesión de corta duración. Limita el tamaño de las solicitudes, la concurrencia y los destinos posibles. Registra las solicitudes rechazadas además de las correctas, porque las denegaciones repetidas pueden revelar que una integración intenta saltarse la ruta prevista.

En macOS, inspecciona una herramienta antes de confiar en su afirmación de que solo es local:

```sh
ps -axo pid,ppid,user,command | grep -i '[a]gent\\|[d]aemon'
lsof -nP -iTCP -sTCP:LISTEN
launchctl print gui/$(id -u) 2>/dev/null | grep -i -C 2 'agent\\|vault\\|security'
```

El segundo comando normalmente muestra columnas como estas cuando un proceso escucha en TCP:

```text
COMMAND   PID USER   FD   TYPE             DEVICE SIZE/OFF NODE NAME
service  4128 sam     9u  IPv4 0x...            0t0  TCP 127.0.0.1:48120 (LISTEN)
```

Que `lsof` no muestre resultados no demuestra que la herramienta no use IPC. Los sockets Unix no aparecen en esa consulta TCP. Comprueba los directorios de soporte de la aplicación, los directorios temporales y la documentación del servicio para encontrar las rutas de los sockets. Después revisa los permisos con `ls -l` y pregunta si otro proceso de tu cuenta puede conectarse.

El manual de `launchd.plist` de Apple describe `KeepAlive` como un conjunto de condiciones bajo las que launchd reinicia un trabajo. Es útil desde el punto de vista operativo, pero crea una obligación de seguridad. Un servicio que se reinicia debe recuperar correctamente su estado bloqueado, el estado de la sesión y el estado de auditoría. «Vuelve automáticamente» no responde a qué acepta durante el primer segundo después del reinicio.

Un buen daemon convierte la identidad del solicitante en parte del protocolo, en lugar de asumirla a partir de lo que dice el cliente. El mecanismo exacto depende del sistema operativo. En las plataformas que pueden exponer las credenciales del interlocutor para un socket local, utilízalas. Cuando exista información de firma de código, verifícala antes de conceder una sesión. No aceptes como identidad un nombre de proceso, un PID incluido en JSON ni una ruta escrita por el solicitante. Los tres pueden falsificarse o quedar obsoletos antes de que los inspecciones.

## El comportamiento de inicio decide si la protección existe cuando hace falta

Una aplicación de un solo proceso suele iniciarse cuando el usuario abre la aplicación. Su estado sensible tiene un ciclo de vida sencillo: la aplicación se inicia, el usuario la desbloquea, se ejecutan las sesiones aprobadas y cerrar o bloquear la aplicación termina el acceso. Es fácil de explicar y de probar.

El coste es la disponibilidad. Un agente de línea de comandos no puede usar la pasarela mientras la aplicación esté cerrada, siga iniciándose, esté bloqueada o espere un permiso del sistema. Eso puede ser exactamente lo correcto para una pasarela de credenciales controlada por una persona. Es una mala opción si el trabajo previsto debe ejecutarse sin supervisión después de un reinicio y el producto no tiene una forma segura de recuperar su autoridad.

Un daemon suele iniciarse al iniciar sesión, bajo demanda o durante el arranque. Cada opción cambia el modelo de amenazas. Un servicio iniciado al iniciar sesión puede esperar al almacén de claves y a la sesión de escritorio del usuario. Un servicio de arranque puede ejecutarse antes de que el usuario pueda aprobar nada. Un servicio bajo demanda reduce la exposición mientras está inactivo, pero su primera solicitud no debe competir con la inicialización.

He visto la versión desagradable de este fallo: la interfaz tarda en iniciarse, el servicio en segundo plano ya acepta solicitudes y el servicio trata «no se ha cargado el estado de aprobación» como equivalente a «no se requiere aprobación». Los desarrolladores escriben esa rama para evitar un error de inicio. A los atacantes y a la automatización inestable no les importa por qué existe.

Escribe la máquina de estados. Debe responder sin evasivas a estas preguntas:

1. ¿Puede el ejecutor aceptar una solicitud antes de que el almacén indique si está bloqueado o desbloqueado?
2. ¿Qué ocurre con una aprobación vinculada a un proceso cliente cuando ese proceso termina?
3. ¿Qué hace el servicio después de reiniciarse mientras hay una solicitud en curso?
4. ¿Puede una solicitud pendiente del usuario sobrevivir a un reinicio y vincularse a otro solicitante?
5. ¿Cerrar sesión revoca el estado utilizable de las credenciales del servicio?

Para las acciones de alto riesgo, falla de forma segura durante cada transición ambigua. Una solicitud que llegue mientras el servicio se inicializa debe recibir una denegación explícita o una respuesta reintentable de no disponibilidad. No debe heredar una aprobación antigua, una credencial descifrada en caché ni una decisión de permitir por defecto.

El inicio también afecta al comportamiento de los usuarios. Si un flujo de agente se rompe porque el usuario debe recuperar manualmente un servicio oculto, acabará desactivando la barrera o guardando un token en una variable de entorno. Los controles de seguridad que no sobreviven al reposo, al cierre de sesión y al reinicio normales enseñan a los usuarios a evitarlos.

## Las credenciales deben llegar al ejecutor sin pasar por el agente

La pregunta central sobre las credenciales no es si una herramienta cifra un almacén. Es si el agente recibe alguna vez el secreto en una forma que pueda copiar, imprimir, colocar en un archivo o transmitir a otro endpoint.

La disposición más segura mantiene la credencial en un almacén controlado por la pasarela. El agente envía una solicitud de acción. La pasarela comprueba el estado de autorización, inserta la credencial en la solicitud HTTP saliente o en la operación SSH, ejecuta la acción y devuelve el resultado. El agente recibe los datos producidos por el sistema remoto, no el token bearer ni la clave privada.

Esta distinción importa porque los agentes trabajan con texto. Si un token aparece en la salida de una herramienta, un modelo puede repetirlo en un comando de shell, un archivo fuente, la descripción de una incidencia, un registro de compilación o una conversación. Ocultar después un campo de la interfaz no repara la exposición. El secreto ya cruzó el límite.

La guía Secrets Management Cheat Sheet de OWASP aconseja evitar los secretos codificados directamente y rotarlos cuando se sospeche una exposición. El consejo es correcto, pero incompleto para los flujos de agentes. Un secreto puede no estar en el control de código fuente y aun así filtrarse a través de la respuesta de una herramienta. La pasarela debe impedir la divulgación en el momento en que ejecuta la acción.

Un daemon puede proteger bien este límite cuando controla el descifrado y la conexión de salida. El cliente envía una descripción de la operación, nunca una solicitud de material secreto sin procesar. El daemon también debe evitar devolver diagnósticos demasiado amplios. Una respuesta de autenticación fallida puede informar al solicitante de que la solicitud no tuvo éxito. No necesita devolver el encabezado de autorización insertado, una configuración SSH serializada ni un volcado de memoria del proveedor de credenciales.

Un proceso único puede aplicar la misma regla. Su ejecutor interno lee el almacén y realiza la llamada, mientras el agente se comunica mediante un punto de integración limitado. La ventaja es que ningún segundo proceso necesita acceder al material descifrado. El riesgo es que las funciones normales de la aplicación compartan el mismo dominio de memoria que el secreto.

Evita estas opciones tentadoras:

- Guardar tokens API en variables de entorno del proceso del agente.
- Escribir un archivo de credenciales efímero que lea un subproceso.
- Devolver un marcador de posición que el cliente intercambie después por el token real.
- Permitir que un daemon local exponga un método «obtener secreto» por comodidad.
- Pasar bytes de una clave privada SSH mediante la entrada estándar.

El patrón del marcador de posición merece una sospecha especial. Solo es seguro si nunca concede acceso a las credenciales fuera de la pasarela y otro proceso no puede reutilizarlo. En la práctica, los equipos suelen convertirlo en un token bearer no documentado. Entonces han creado una segunda credencial con controles de ciclo de vida más débiles. 

En SSH hace falta la misma disciplina. Un asistente puede necesitar una clave para establecer una conexión, pero el agente debe pedirle que ejecute un comando remoto concreto o una operación de conexión estrictamente limitada. Darle una clave privada a un agente porque «solo la necesita un momento» sigue siendo darle una clave privada.

## Más procesos generan más mantenimiento, no más madurez

Un daemon tiene trabajo operativo que una aplicación de escritorio de un solo proceso evita. Alguien debe instalarlo, iniciarlo en el contexto de usuario o de sistema correcto, actualizarlo, verificar su binario, gestionar sus bloqueos, eliminar registros obsoletos y asegurarse de que el socket y los archivos de registro tienen el propietario correcto después de las actualizaciones.

Esto no es un argumento contra los daemons. Es un argumento contra fingir que esas tareas desaparecen dentro del sistema operativo. `launchd` puede reiniciar un trabajo. No puede decidir si un binario nuevo conserva las comprobaciones esperadas del solicitante, si un socket antiguo sobrevivió a una actualización o si un servicio se inicia ahora con un conjunto diferente de permisos.

Un daemon también complica la correspondencia de versiones. Un adaptador de línea de comandos puede hablar el protocolo A mientras el servicio instalado espera el protocolo B. Si los autores gestionan mal esa incompatibilidad, el cliente recurre a una ruta de compatibilidad sin autenticar o desactiva las comprobaciones para mantener una experiencia fluida. El código de compatibilidad ha causado más problemas de los que muchos equipos esperan porque se ejecuta precisamente cuando el sistema se entiende peor.

Rechaza explícitamente las versiones incompatibles. El error debe indicar al usuario que actualice uno de los lados, no negociar silenciosamente un modo más débil. Mantén el protocolo lo bastante pequeño para poder probar clientes antiguos, reinicios del servicio, mensajes malformados y solicitudes simultáneas sin construir un laboratorio entero a su alrededor.

Las aplicaciones de un solo proceso también necesitan disciplina de actualización. Una aplicación firmada puede cambiar su implementación y el esquema del almacén. La diferencia es más limitada: hay un único ciclo de vida ejecutable que inspeccionar, y el proceso que muestra la aprobación es el mismo que realiza la acción protegida. Una actualización aún puede introducir un fallo, pero no introduce automáticamente un protocolo RPC local y el estado de un gestor de servicios.

La diferencia se acentúa para los equipos. Un daemon central puede ofrecer un endpoint local estable a varias herramientas, lo que puede reducir las integraciones duplicadas. También puede convertirse en un punto único de fallo. Un servicio bloqueado puede detener todos los flujos de desarrollo de la máquina. Un servicio con permisos demasiado amplios puede dar a todos los clientes locales acceso a todas las credenciales configuradas.

No consideres que un icono de la barra de menú demuestra que un daemon está sano. Prueba los casos difíciles: fuerza el cierre de la interfaz, termina el servicio, reinicia durante una sesión activa, actualiza primero el cliente, actualiza primero el servicio y elimina una credencial mientras una solicitud espera aprobación. Si la respuesta a alguno de esos casos es «probablemente se recupera», todavía no conoces el comportamiento operativo.

## La aprobación pertenece a una ejecución identificada, no a una máquina recordada

Los sistemas de aprobación fallan cuando autorizan un concepto vago como «este ordenador» o «el usuario actual». Los agentes autónomos suelen crear procesos secundarios, reiniciarse después de cambios y llamar a herramientas mediante shells. Un permiso que acompaña a toda esa actividad es demasiado amplio para tener un significado útil.

Vincula la aprobación a una ejecución de proceso concreta o a otra identidad que el código local no relacionado no pueda copiar. La pantalla de aprobación debe mostrar suficiente procedencia para que una persona detecte una sorpresa: la autoridad de firma del ejecutable, la ruta del comando y la clase de solicitud son mejores evidencias que un nombre amistoso del cliente. Cualquiera puede elegir un nombre de proceso.

Haz explícita la duración. Una aprobación para una sesión corta del agente puede ser razonable para llamadas repetidas de bajo riesgo. Las credenciales sensibles pueden requerir consentimiento cada vez. La revocación debe detener inmediatamente la ejecución identificada, no limitarse a ocultarla en un panel mientras su conexión local sigue abierta.

Aquí un daemon tiene una obligación adicional. Debe relacionar una conexión IPC con la identidad que aprobó la persona y descartar esa relación cuando el cliente termina o se vuelve a conectar. No permitas que un cliente reanude una sesión antigua solo con presentar un identificador almacenado en caché. Si el identificador puede viajar en un archivo o en un argumento de comando, es una credencial bearer con un nombre sofisticado.

Un diseño de un solo proceso puede mantener el estado de aprobación junto a la integración activa, lo que reduce el trabajo de asociación. Aun así, debe distinguir un lanzamiento del agente de otro. Si la aplicación no puede saber si una solicitud procede de la ejecución aprobada o de un proceso de sustitución posterior, debe solicitar aprobación de nuevo.

La política útil suele ser más sencilla de lo que los equipos esperan: denegar mientras el almacén está bloqueado, preguntar una vez para una ejecución recién identificada y exigir una confirmación individual para las credenciales que justifican esa fricción. Los lenguajes de políticas locales elaborados suelen crear reglas que nadie puede auditar bajo presión. Un camino de decisión corto, que los usuarios puedan prever, es más fácil de probar y más difícil de saltarse por accidente.

## Un fallo al reiniciar revela el punto débil

Imagina a un desarrollador que ha aprobado que un agente de programación haga llamadas HTTP normales durante una ejecución activa desde un terminal. La pasarela guarda la aprobación en memoria. El agente envía una solicitud y la pasarela empieza a preparar la llamada saliente. En ese momento, la interfaz se bloquea o el ejecutor en segundo plano se reinicia después de una actualización.

Una implementación descuidada del daemon puede empeorar la situación de varias formas. El ejecutor vuelve antes de cargar el estado de bloqueo del almacén. Su socket de cliente reaparece en la misma ruta. El agente se vuelve a conectar. El daemon ve un identificador de sesión almacenado en caché en la solicitud y supone que la aprobación antigua sigue siendo válida. Mientras tanto, el escritor de auditoría aún no ha vuelto a abrir el registro, por lo que el reintento correcto no tiene un registro duradero.

Cada decisión individual parece cómoda: conservar una sesión, reducir las solicitudes de aprobación, reiniciar rápido y almacenar los registros en búfer. Juntas crean una acción no aprobada durante el momento en que el sistema tiene el estado menos fiable.

El comportamiento seguro es más simple. Al reiniciar, descarta las aprobaciones volátiles. Mantén el almacén no disponible hasta conocer su estado de protección. Rechaza acciones nuevas hasta que el escritor de auditoría pueda confirmar un registro. Haz que el agente reciba una denegación o una respuesta temporal de no disponibilidad, y exige una aprobación nueva si la sesión ya no existe.

Puedes probarlo sin hardware especial:

1. Abre una sesión nueva del agente y aprueba una solicitud permitida de bajo impacto.
2. Inicia otra solicitud que espere en un punto de aprobación o en un endpoint de prueba.
3. Termina el proceso o servicio de seguridad mientras la solicitud espera.
4. Reinícialo y vuelve a intentar la solicitud desde el proceso original del agente.
5. Comprueba que necesita una autorización nueva y que el resultado final aparece una sola vez en el registro de auditoría.

Repite la prueba con una reconexión del cliente, el bloqueo del dispositivo, una credencial eliminada y una actualización. Busca autoridad obsoleta, ejecución duplicada o registros ausentes. Una herramienta que no puede hacer visibles estos resultados es difícil de confiar durante un incidente.

## Los registros de auditoría necesitan un escritor que no pueda evitarse

Los registros de seguridad suelen guardar lo que vio la interfaz de usuario, no lo que hizo el ejecutor de credenciales. Esa diferencia se vuelve grave cuando un daemon acepta solicitudes locales directamente. Un cliente comprometido puede saltarse la interfaz, o la interfaz puede bloquearse después de que el ejecutor actúe.

Coloca la escritura de auditoría en la ruta de acción. El componente que envía la solicitud HTTP o invoca el asistente SSH debe registrar la identidad del solicitante, la referencia de la credencial, el destino, la clase de solicitud, la decisión y el resultado. No registres secretos sin procesar ni cuerpos por defecto. Los registros que contienen tokens bearer se convierten en otro almacén, con controles de acceso peores.

Un proceso único puede hacer explícito el orden: validar, registrar la decisión, ejecutar y registrar el resultado. Un daemon debe asegurarse de que su propio ejecutor no pueda realizar una solicitud por un canal alternativo que evite el componente de registro. Separa el registro de la ejecución mediante IPC solo cuando puedas aceptar el modo de fallo y demostrar el orden.

La evidencia de manipulación importa después de un compromiso, no solo durante la resolución rutinaria de problemas. Si un atacante puede editar un archivo SQLite local o borrar líneas concretas de un registro de texto, el registro puede ayudar a depurar, pero no establece un historial fiable. Un registro orientado a anexos y con encadenamiento criptográfico permite detectar alteraciones, siempre que se conserven los archivos necesarios y se verifiquen.

Sallyport mantiene las vistas de sesiones y llamadas proyectadas desde un único registro de auditoría cifrado y encadenado mediante hashes, y su comando `sp audit verify` comprueba la cadena sin conexión y sin necesitar una clave del almacén. Ese diseño aborda un problema que suelen crear los registros separados de la interfaz y el daemon: dos registros que no coinciden sobre si una acción ocurrió.

No exageres lo que puede hacer una auditoría. Una cadena no puede recuperar un evento cuya escritura un atacante impidió. Puede detectar cambios dentro del conjunto de registros que recibe. Eso sigue siendo útil, especialmente cuando un bloqueo, una actualización o un cliente local inesperado te obliga a reconstruir una secuencia después de los hechos.

## Elige el diseño más pequeño que cubra el ciclo de vida que necesitas

Elige un solo proceso cuando el trabajo protegido requiera la presencia de una persona, la aplicación pueda permanecer abierta mientras se ejecutan los agentes y reducir los endpoints locales sea más importante que sobrevivir de forma independiente a la interfaz. Este diseño encaja con una pasarela de credenciales cuya promesa principal es que una persona ve y controla cada ejecución del agente.

Elige un daemon cuando el trabajo necesite realmente continuidad del servicio, separación frente a un cliente menos fiable o un privilegio distinto del sistema operativo. Exige una respuesta para cada interfaz que añada: quién se conecta, cómo identifica el servicio a los solicitantes, qué solicitud puede hacer cada uno, cuándo caduca el estado y qué ocurre cuando cualquiera de los dos lados se reinicia.

Una herramienta de escritorio como Sallyport sigue la ruta de una sola aplicación y utiliza un adaptador MCP normal para las solicitudes de los agentes. Su almacén realiza directamente la acción HTTP o SSH, por lo que el agente nunca recibe la credencial API o SSH configurada.

Antes de adoptar cualquiera de los dos modelos, haz una comprobación sin concesiones. Termina el componente que tiene la autoridad mientras un agente trabaja, reinícialo e inspecciona si la siguiente solicitud se deniega, requiere una nueva aprobación y queda registrada. Esa prueba revela más sobre el diseño que cualquier diagrama de arquitectura pulido.
