Autorización de procesos de agentes: revoca el acceso bifurcado
La autorización de procesos de agentes necesita un límite que sobreviva a las bifurcaciones. Aprende a inspeccionar los procesos secundarios, revocar el acceso, detener grupos de procesos y conservar pruebas.

Los agentes autónomos rara vez se detienen de forma tan limpia como sugieren sus registros de tareas. Un agente puede terminar una solicitud de programación, mostrar un alegre mensaje de finalización y dejar atrás un formateador, un ejecutor de pruebas, un túnel, un shell o un proceso auxiliar que sigue ejecutándose. Si ese proceso residual aún puede llamar a las API de producción o abrir sesiones SSH, la tarea no terminó realmente donde indica la interfaz.
La solución no consiste en obsesionarse con terminar procesos. La limpieza de procesos es necesaria, pero no es un sistema de autorización. Define por separado la duración de la autoridad y la de los procesos Unix, haz que el límite sea observable y revoca el acceso antes de empezar a perseguir procesos descendientes. He visto equipos tratar un PID principal como prueba de contención. No demuestra gran cosa en cuanto un agente puede invocar un shell.
Un árbol de procesos no define un límite de autorización
La relación entre proceso principal y secundario indica quién creó un proceso. No indica si ese proceso debe conservar autoridad después de que termine el principal. Son preguntas distintas, y mezclarlas produce el fallo habitual: alguien cancela una tarea del agente, ve desaparecer el proceso principal y descubre después que un proceso secundario sigue haciendo solicitudes cinco minutos más tarde.
Unix ofrece varias formas de escapar de la estructura prevista. Un proceso secundario puede bifurcarse otra vez. Puede crear una sesión nueva con setsid. Puede pedir a un gestor de servicios que lo supervise. Puede quedar en una canalización del shell donde solo uno de sus miembros recibe la señal. También puede sobrevivir simplemente porque el proceso principal no envió ninguna señal.
Un proceso contiene más que su línea de comandos. Puede heredar variables de entorno, el directorio de trabajo actual, archivos abiertos, tuberías, sockets y descriptores de archivo. Si un proceso principal guarda un token de portador en una variable de entorno, cualquier proceso secundario que herede ese entorno tiene el token. Terminar después ese proceso no devuelve el secreto a la bóveda ni deshace las solicitudes ya realizadas.
Por eso, un límite de autorización debe responder a una pregunta concreta: qué ejecución de proceso activa puede solicitar acciones privilegiadas, hasta cuándo y cómo se le deniega el acceso de inmediato. La respuesta no debería ser «cualquier proceso descendiente de la terminal que inició la tarea». Eso es un accidente de la estructura de procesos, no una decisión de seguridad.
Para un agente que necesita acceso HTTP o SSH, mantén las credenciales fuera del proceso del agente. Deja que un componente que posee las credenciales realice la acción después de reconocer a un solicitante aprobado. Así, la limpieza deja de ser un intento desesperado de borrar autoridad filtrada y se convierte en una rutina operativa normal.
Define la duración antes de iniciar la tarea
Una ejecución de agente necesita una condición de inicio, una condición de finalización y una condición de revocación explícitas. Escríbelas antes de decidir si el control corresponde a un clic en una interfaz, un grupo de procesos o un envoltorio del shell.
Para el trabajo interactivo, el valor predeterminado más sensato suele ser una autorización para un único proceso raíz del agente. La autorización comienza cuando apruebas esa ejecución concreta y termina cuando el proceso raíz sale o cuando la revocas. Una nueva invocación del agente recibe una decisión nueva, aunque ejecute el mismo binario desde el mismo directorio.
Para el trabajo sin supervisión, no prolongues discretamente la aprobación durante todo el día solo porque los avisos repetidos molestan. Asigna al trabajo un responsable identificado, una duración limitada, un conjunto restringido de destinos y una vía de cancelación que alguien pueda usar mientras el trabajo siga activo. Si el trabajo debe continuar después de que termine su tarea principal, trátalo como un trabajo independiente y haz que solicite una autoridad separada.
Tres preguntas revelan enseguida los diseños imprecisos:
- ¿Qué ejecutable y qué instancia de proceso aprobó el usuario?
- ¿Qué evento termina su autoridad si el proceso nunca envía una señal de finalización limpia?
- ¿Puede un descendiente obtener una nueva acción privilegiada después de ese evento?
Si la última respuesta es sí porque el descendiente heredó un token, has delegado autoridad sin registrar la delegación. Si es sí porque la puerta de enlace sigue considerando de confianza a todos los descendientes, has convertido la ascendencia de procesos en tu lenguaje de políticas. Ambas decisiones son difíciles de explicar durante un incidente.
No confundas la comodidad con un límite. Una pestaña de terminal, un directorio de proyecto, una cuenta de agente y una identidad de firma de código aportan contexto útil. Ninguno identifica por sí solo una ejecución. Una identidad de firma de código indica quién firmó un ejecutable. No puede indicar si el ejecutable inició el auxiliar previsto, una copia antigua del auxiliar o un proceso secundario que se desvinculó tras la cancelación.
Inspecciona el árbol activo antes de terminar nada
En macOS, empieza por los datos del proceso que expone el kernel, en lugar de hacer suposiciones a partir de una etiqueta de tarea. El manual de ps documenta pid, ppid, pgid y sid como campos distintos. Muestran la ascendencia, la pertenencia a grupos de procesos y la pertenencia a sesiones. Necesitas los cuatro cuando se ha permitido al agente invocar shells y herramientas.
Ejecuta este comando y guarda el resultado cuando investigues una ejecución:
ps -axo pid,ppid,pgid,sid,stat,etime,command
La estructura útil se parece a esta:
PID PPID PGID SID STAT ELAPSED COMMAND
48102 47790 48102 48102 S 00:18:04 agent-cli run build
48131 48102 48102 48102 S 00:17:59 /bin/sh -c make test
48144 48131 48102 48102 S 00:17:56 test-runner --watch
48209 1 48209 48209 S 00:16:02 helper --upload-results
Los tres primeros procesos comparten grupo y sesión. El último tiene PPID 1 y un grupo y una sesión distintos. Puede haberse desvinculado o puede que ahora lo controle un lanzador. En cualquier caso, enviar una señal solo al PID 48102 no lo detendrá.
Para consultar los procesos secundarios directos de un PID raíz conocido, usa:
pgrep -P 48102 -alf
Ese comando encuentra una sola generación. Repítelo para cada proceso secundario si necesitas recorrer el árbol manualmente. Para un registro de incidente, captura la salida de ps antes y después de la revocación y registra el PID raíz exacto, la hora de inicio, el comando, el grupo de procesos y la sesión. Un simple nombre de proceso es una prueba deficiente porque los nombres se repiten y las líneas de comandos cambian.
Comprueba las conexiones de red abiertas cuando el riesgo implique llamadas externas. En macOS, lsof puede mostrar los archivos de red de un proceso:
lsof -nP -p 48209 -i
Un socket a la escucha, una conexión saliente establecida o un transporte SSH de larga duración cambian la urgencia. No demuestran un comportamiento malicioso. Demuestran que un proceso que creías desaparecido todavía tiene un canal que merece atención.
No construyas en producción un control de seguridad que dependa de analizar la salida legible para humanos de ps. Úsala para investigar y probar. Un lanzador real debe registrar los identificadores en el momento del inicio y conservar un mecanismo directo de revocación en la puerta de enlace de autorización.
Los grupos de procesos ayudan, pero los procesos desvinculados los esquivan
Un grupo de procesos dedicado ofrece al lanzador una forma práctica de cancelar un árbol de tareas normal. Crea el grupo antes de iniciar el agente, conserva el proceso raíz como líder del grupo y envía las señales al grupo en lugar de enviarlas solo al proceso raíz. Esto cubre el caso habitual de shells, compiladores, ejecutores de pruebas y canalizaciones que permanecen en el mismo grupo.
En los sistemas compatibles con la sintaxis habitual de señales, un ID de grupo negativo apunta a un grupo de procesos:
kill -TERM -48102
sleep 3
kill -KILL -48102 2>/dev/null || true
La señal TERM da a las herramientas normales la oportunidad de cerrar archivos e informar de la cancelación. La señal KILL posterior gestiona los procesos que se niegan a salir o no consiguen hacerlo. No copies esto en una automatización hasta comprobar que 48102 es el grupo de procesos correcto. Un ID equivocado puede terminar tu propio shell o trabajo no relacionado.
Este método tiene límites. Un proceso secundario puede llamar a setsid, que crea una sesión nueva y normalmente un grupo de procesos nuevo. Una tarea puede enviar trabajo a un servicio local, un sistema de compilación remoto o una cola. Un shell puede iniciar un proceso en segundo plano fuera del grupo. Cuando eso ocurre, terminar el grupo sirve para limpiar, no para contener.
En Linux, un gestor de servicios puede colocar una tarea en un cgroup dedicado y terminar el cgroup como una unidad. Suele ser más sólido que limpiar un grupo de procesos porque el kernel sigue la pertenencia más allá de la ascendencia normal. No des a entender que una aplicación de macOS en la barra de menús tiene este control. macOS y Linux tienen modelos de supervisión de procesos distintos, y un diseño de agente portátil no debe fingir lo contrario.
La recomendación de «terminar simplemente el árbol de procesos» sigue siendo popular porque funciona en las demostraciones. Falla justo en las condiciones que hacen arriesgado el acceso de los agentes: trabajos largos, auxiliares en segundo plano, envoltorios y cancelaciones parciales. Usa grupos de procesos porque reducen los residuos. No los uses como único mecanismo de revocación.
Mantén los secretos completamente fuera del árbol
El proceso secundario más seguro sigue siendo el que no puede leer una credencial. Pasar un token mediante una variable de entorno lo pone a disposición de todos los descendientes que hereden el entorno y también puede exponerlo a diagnósticos, informes de errores o registros descuidados. Pasarlo mediante un archivo temporal es solo un poco menos malo si el proceso secundario puede copiarlo antes de que lo elimines.
Evita estos patrones en los comandos iniciados por agentes:
export DEPLOY_TOKEN='token-value'
agent-cli run deploy
agent-cli run deploy --token "$(cat ~/.config/deploy-token)"
Ambos ponen la autoridad sin procesar en el entorno de ejecución del agente. El segundo también puede dejarla en los argumentos del proceso, el historial del shell o los registros. Rotar el token después de un problema puede ser necesario, pero la rotación es una medida de recuperación, no una vía normal de cancelación.
Usa en su lugar una puerta de enlace de acciones local. El agente debe solicitar una operación, como una solicitud HTTP a un endpoint aprobado o un comando SSH, sin recibir nunca el material de la credencial. La puerta de enlace inyecta la credencial correspondiente, ejecuta la acción y devuelve el resultado. Así tienes un lugar donde denegar la siguiente llamada aunque un proceso secundario perdido siga ejecutándose.
Sallyport aplica este enfoque a las API HTTP y SSH: su bóveda cifrada permanece en la aplicación, mientras el agente se conecta mediante el componente sp mcp y recibe resultados de acciones en lugar de secretos. Esto importa más que cualquier script ingenioso para terminar procesos, porque un proceso secundario no puede heredar un token que nunca tuvo.
No exageres este beneficio. Un proceso con una sesión de puerta de enlace aprobada todavía puede solicitar acciones hasta que la sesión termine o la revoques. Mantener los secretos fuera del árbol de procesos limita el robo de credenciales, pero no vuelve inofensivo a un agente aprobado.
La aprobación debe vincularse a una ejecución, no a un nombre de familia
La autorización basada únicamente en el nombre de un ejecutable es débil. Cualquiera puede copiar un binario a otra ruta, envolverlo en un script de shell o ejecutar otra instancia más adelante. La autorización basada únicamente en un firmante mejora la atribución, pero sigue siendo demasiado amplia si aprueba silenciosamente todas las ejecuciones futuras firmadas por la misma parte.
Una tarjeta de aprobación útil debe identificar el proceso solicitante de una forma que una persona pueda comprobar: su autoridad de firma de código, la ruta del ejecutable, el PID raíz y la hora de inicio. La decisión debe aplicarse a esa única ejecución del proceso. Un proceso secundario no debe obtener un derecho indefinido a actuar solo porque tiene un antecesor aprobado en algún punto de su historial.
Hay dos modelos razonables para los descendientes. El modelo más estricto pide aprobación para cada solicitante distinto. El modelo práctico permite las llamadas que tienen lugar durante una ejecución raíz aprobada y rechaza todas las llamadas nuevas cuando esa ejecución termina. El segundo modelo funciona bien con agentes que legítimamente inician herramientas de corta duración, siempre que la puerta de enlace pueda saber que la ejecución raíz terminó y que la autorización no pueda vincularse de nuevo a un proceso posterior con el mismo nombre.
Sallyport usa por defecto la autorización por sesión: la primera llamada de un proceso de agente nuevo muestra una tarjeta de aprobación que comienza con la autoridad de firma de código del proceso, y la aprobación dura hasta que termina esa ejecución. La puerta de la bóveda deniega toda acción mientras está bloqueada, y una opción por llamada puede exigir aprobación para una credencial concreta cada vez que se use. Son controles deliberadamente pequeños. Un conjunto enorme de reglas de políticas solo ocultaría la pregunta de quién aprobó qué.
La aprobación por llamada encaja con credenciales donde cada acción merece una revisión deliberada, como una cuenta de despliegue de producción o una API administrativa destructiva. No encaja con todas las solicitudes de solo lectura. Cuando cada acción inofensiva exige atención, la gente aprende a aprobarlo todo. Coloca la fricción donde lo justifique la consecuencia y mantén breve y claro el límite de sesión en el resto de los casos.
Revoca la autorización antes de perseguir el proceso
Cuando una tarea principal termina de forma inesperada, revoca primero su capacidad para realizar nuevas llamadas privilegiadas. Después termina el grupo raíz, inspecciona los procesos supervivientes y limpia lo que haya escapado. Invertir ese orden crea un intervalo de riesgo: el proceso secundario que todavía no has encontrado puede seguir comunicándose mientras inspeccionas las tablas de procesos.
Una secuencia de respuesta sólida sería esta:
- Revoca la sesión o bloquea la bóveda en la puerta de enlace de acciones.
- Conserva el PID raíz, la información de los procesos, los registros recientes de acciones y la hora de cancelación.
- Envía
TERMal grupo de procesos conocido e inspecciona los procesos restantes. - Recurre a
KILLsolo para los procesos que todavía pertenecen a la tarea y se niegan a salir. - Comprueba si los procesos locales desvinculados o los trabajos remotos requieren una cancelación independiente.
La primera acción debe funcionar aunque el PID raíz ya haya desaparecido. También debe funcionar si un agente intenta deliberadamente mantener activo un proceso auxiliar. Una puerta de enlace que exige que el solicitante esté presente para revocar la autorización tiene el enfoque invertido.
La revocación inmediata debe denegar las solicitudes futuras, no reescribir el historial. Conserva los registros que muestran la autorización y las llamadas anteriores. Si una puerta de enlace solo registra las acciones correctas, oculta pruebas útiles durante una investigación. Las llamadas denegadas después de la revocación indican que algo siguió intentando actuar.
El bloqueo de la bóveda es el freno de emergencia para todas las sesiones activas, lo que resulta apropiado cuando no puedes identificar rápidamente la ejecución afectada. La revocación de sesión es una respuesta más limitada cuando sí puedes identificarla. Mantén separadas ambas operaciones para que el operador no tenga que elegir entre no hacer nada e interrumpir el trabajo de todos los ingenieros.
Un registro de auditoría debe combinarse con las pruebas de los procesos
Un registro de actividad sin contexto del proceso responde solo a la mitad de la pregunta. Puede decir que ocurrió una solicitud HTTP, pero no qué ejecución aprobada la inició. Un registro de sesión sin llamadas individuales tiene el problema contrario. Necesitas ambas perspectivas y una forma de verificar que nadie las editó discretamente después de un incidente.
Registra al menos el evento de autorización, la identidad del proceso solicitante, el inicio y el final de la sesión, cada acción privilegiada, la revocación y cualquier denegación posterior a la revocación. Incluye marcas de tiempo e identificadores de correlación estables. No registres secretos sin procesar. Tampoco supongas que es seguro almacenar una línea de comandos, porque las líneas de comandos suelen contener valores que nunca deberían haberse pasado de esa forma.
Sallyport genera sus registros Sessions y Activity a partir de un único registro de auditoría cifrado y encadenado mediante hashes. Su comando sp audit verify comprueba la cadena sin conexión sobre el texto cifrado y sin necesitar una clave de la bóveda. Esta verificación resulta útil cuando necesitas entregar un registro exportado a alguien que debe confirmar su integridad, pero no debe leer las credenciales almacenadas.
Una cadena de hashes no convierte un registro incompleto en uno completo. Si tu lanzador nunca registró el PID raíz, el registro de auditoría no puede reconstruirlo después. Si un trabajo remoto recibió una solicitud API y continuó en otra máquina, las pruebas de procesos locales no mostrarán el proceso remoto. Audita la solicitud que inició el trabajo remoto y exige después que el sistema remoto exponga sus propios registros de cancelación y eventos.
Un fallo conocido requiere dos soluciones independientes
Imagina un agente de programación al que se le pide ejecutar pruebas de integración y publicar un informe. Inicia un shell, que inicia el ejecutor de pruebas, que a su vez inicia un auxiliar para subir los resultados. El usuario ve un fallo en las pruebas y cancela el agente. El agente principal termina. El shell desaparece. El auxiliar ya se ha desvinculado, conserva una conexión saliente y envía un informe después de la cancelación.
Si la credencial de carga estaba en una variable de entorno, el auxiliar puede conservarla incluso después de que revoques la aprobación local. Ahora necesitas rotar la credencial, revisar los registros y posiblemente responder a un incidente. La limpieza de procesos llegó demasiado tarde porque la autoridad ya había pasado al proceso secundario.
Si el auxiliar solicitó la carga mediante una puerta de enlace, revocar la sesión principal detiene una nueva solicitud de carga. Si la carga ya había comenzado, la auditoría de la puerta de enlace lo muestra. Aun así terminas el auxiliar, pero ya no dependes de la terminación como único medio de control.
El caso incómodo es un auxiliar que debe sobrevivir al agente por diseño, como un servidor local de vista previa o un trabajo de publicación en cola. No lo llames proceso secundario para heredar autoridad indefinidamente. Asígnale un responsable explícito, un registro de autorización independiente, una caducidad definida y un control de detención visible. Cuando el trabajo sobrevive a la tarea que lo inició, se ha convertido en un objeto operativo propio.
Prueba la cancelación con un proceso secundario persistente
Un control que nunca pruebas fallará de la forma menos conveniente. Crea un agente de prueba inofensivo que inicie un proceso secundario que duerma, abra una conexión local inofensiva e intente realizar una acción en la puerta de enlace después de que termine el proceso principal. Después, cancela el proceso principal en varios momentos: antes de que empiece el proceso secundario, mientras se ejecuta, después de que se desvincule y mientras tenga una acción en curso.
El resultado esperado debe ser concreto. El proceso secundario normal termina con el grupo de procesos. Un proceso secundario desvinculado puede seguir visible, lo que demuestra por qué aún debes inspeccionar. Después de la revocación, toda nueva solicitud privilegiada recibe una denegación. El registro de sesión muestra la aprobación y la revocación, mientras que el registro de actividad muestra las llamadas realizadas antes y después del límite.
No permitas que una prueba aprobada signifique únicamente que la interfaz cambió de estado. Verifica el estado del proceso en el sistema operativo, la decisión de la puerta de enlace y el registro de auditoría. Son tres observaciones distintas. Un botón de cancelación que solo oculta una tarjeta de tarea es puro teatro.
El primer cambio práctico es sencillo: registra la identidad del proceso raíz cada vez que autorices un agente y haz que la autorización caduque de forma independiente cuando termine esa ejecución. Después, ejecuta la prueba con el proceso secundario persistente antes de confiar una función de cancelación a credenciales capaces de modificar producción.
FAQ
¿Qué es un proceso de agente de IA bifurcado?
Un proceso de agente bifurcado es un proceso secundario creado por un agente o por una de sus herramientas. Puede seguir ejecutándose después de que la tarea principal informe de que ha terminado y conservar conexiones de red abiertas, descriptores de archivo heredados, variables de entorno o acceso a un agente de credenciales.
¿Un proceso secundario del agente se detiene cuando termina su proceso principal?
No. La salida del proceso principal cambia la relación de ascendencia, pero no termina de forma fiable los procesos secundarios. Un proceso secundario puede ignorar un cierre ordenado, desvincularse en otra sesión o ser adoptado por el gestor de servicios del sistema operativo.
¿Cómo puedo encontrar los procesos secundarios iniciados por un agente?
Usa ps para inspeccionar el PID, el PPID, el grupo de procesos, la sesión, el tiempo transcurrido y el comando. Después, usa pgrep -P para consultar los procesos secundarios directos. En macOS, ps -axo pid,ppid,pgid,sid,stat,etime,command ofrece pruebas más útiles que una vista gráfica de actividad.
¿Puedo terminar de forma segura un grupo de procesos completo?
Un grupo de procesos resulta útil cuando el lanzador crea uno antes de iniciar el agente. Puedes enviar una señal al ID negativo del grupo de procesos, pero solo funcionará si los procesos secundarios permanecen en ese grupo y no crean deliberadamente una sesión nueva.
¿Debo revocar el acceso del agente antes de terminar sus procesos?
Resuelven problemas distintos. Terminar un proceso detiene el trabajo futuro, pero puede dejar un intervalo breve y no explica lo ocurrido. Revocar la autorización detiene las nuevas acciones privilegiadas en la puerta de enlace, por lo que es el primer paso más seguro cuando el proceso todavía puede seguir activo.
¿Basta la firma de código para autorizar a un agente autónomo?
La firma de código identifica quién firmó un ejecutable, pero no qué hará cada proceso que este inicie. Úsala como información útil en la pantalla de aprobación y vincula después la aprobación a una única ejecución observada del proceso, con una caducidad clara.
¿Puede un proceso secundario heredar credenciales API de su proceso principal?
Las variables del shell, los tokens exportados, los archivos de configuración, los archivos temporales, los sockets abiertos y los descriptores heredados pueden llevar la autoridad más lejos de lo previsto. Evita pasar secretos sin procesar al proceso del agente, porque la limpieza de procesos no puede recuperar de forma fiable los datos que este ya ha leído.
¿Cuánto debe durar la autorización de un agente?
Las aprobaciones deben terminar cuando acaba la ejecución del proceso autorizado, aunque un proceso secundario siga activo. Si un proceso secundario necesita acceso continuo, debe solicitarlo mediante su propio límite explícito en lugar de heredar silenciosamente la aprobación del proceso principal.
¿Un registro de auditoría impide que un agente malicioso actúe?
Un registro de auditoría resistente a manipulaciones puede demostrar que los registros no se modificaron sin dejar señales, pero no impide una llamada no deseada. También necesitas una puerta de la bóveda, un límite de aprobación y una forma de cortar una ejecución activa.
¿Cuál es el primer control que debo añadir a las tareas de agentes de larga duración?
Empieza por lanzar cada tarea del agente en un grupo de procesos dedicado, registrar su PID raíz y la hora de inicio, y probar la cancelación con un proceso secundario deliberadamente persistente. Después, haz que la revocación sea independiente de la limpieza de procesos, porque esta terminará fallando cuando más la necesites.