Los agentes de IA locales y los trabajos de CI necesitan modelos de acceso distintos
Los agentes de IA locales y los trabajos de CI necesitan modelos de acceso distintos porque la aprobación humana, la identidad de la carga de trabajo y la exposición de credenciales crean riesgos diferentes.

Una estación de trabajo de desarrollador y un runner de compilación pueden ejecutar comandos de shell, llamar a API y enviar código. Tratarlos como el mismo entorno de seguridad es la forma en que los equipos terminan poniendo una credencial de despliegue en un lugar del que ni una persona ni un servicio pueden rendir cuentas correctamente.
Los agentes de IA locales funcionan en un entorno atendido. Una persona puede ver una solicitud, revisar un diff, rechazar una petición inusual y revocar un proceso que haya empezado a comportarse mal. Los trabajos de CI se ejecutan porque ha ocurrido un evento: un push, una solicitud de cambios, una etiqueta, una ejecución programada o un lanzamiento manual. El trabajo necesita una autoridad verificable por una máquina y vinculada a ese evento. No puede esperar a que un desarrollador apruebe cada llamada, y tampoco debería heredar el acceso permanente de un desarrollador solo porque el flujo de trabajo se haya ejecutado.
La diferencia no es académica. Determina si un agente recibe un secreto, si un token existe durante minutos o meses, qué debe decir una entrada de auditoría y si una dependencia comprometida puede convertirse en un incidente de producción.
La presencia humana cambia el significado de una aprobación
Una aprobación en una estación de trabajo puede ser un control de seguridad significativo porque hay una persona presente para juzgar la acción inmediata. La aprobación debe describir al solicitante con suficiente precisión para que ese juicio sea útil. «Un agente quiere hacer una solicitud HTTP» es una prueba deficiente. «Un proceso firmado por esta autoridad, iniciado en esta sesión, quiere usar la credencial de despliegue de producción» da al operador algo concreto que aceptar o rechazar.
Ese modelo tiene un límite claro: la aprobación consiste en que una persona asuma la responsabilidad de un proceso activo. No sustituye a la identidad. Si un proceso malicioso puede hacerse pasar por el solicitante de confianza, ocultar el destino o reutilizar una aprobación después de cambiar de tarea, la solicitud se convierte en una representación vacía.
En una máquina de desarrollo, quiero tres hechos separados:
- el almacén protegido permanece bloqueado hasta que el usuario local lo desbloquea;
- la primera solicitud de un proceso nuevo requiere una decisión durante la sesión;
- las credenciales sensibles pueden requerir una decisión en cada uso.
Estos controles responden a preguntas distintas. El bloqueo indica si se puede realizar alguna acción. La autorización de sesión indica si ese proceso puede actuar durante esta ejecución. La aprobación por llamada indica si una credencial concreta tiene consecuencias demasiado importantes para reutilizarla sin más. Los equipos suelen juntar las tres cosas en un único botón de «permitir acceso al agente» y después descubren que el botón aprobó mucho más de lo que el desarrollador pretendía.
Un agente local también debería recibir resultados, no material de credenciales. Si necesita consultar una API, un componente externo al agente puede añadir la credencial, hacer la solicitud y devolver la respuesta. Así, una inyección de instrucciones en el agente no puede limitarse a imprimir una clave API en un terminal, un parche o una transcripción de chat. Ocultar un secreto después de que haya entrado en el contexto del agente no ofrece la misma protección. El agente puede codificarlo, enviarlo a otro host o usarlo en una solicitud antes de que cualquier sistema de ocultación lo detecte.
Sallyport aplica este modelo atendido en macOS: su bóveda permanece detrás de una puerta absoluta y puede exigir autorización para una sesión de proceso o para cada uso de una credencial seleccionada. Tiene sentido cuando hay un desarrollador presente. Sería el mecanismo equivocado para un runner sin supervisión.
Un trabajo de CI necesita una identidad que resista el análisis
Un trabajo de CI no puede proporcionar intención humana cuando se le solicite. Necesita una identidad de carga de trabajo, es decir, una identidad derivada de hechos verificables sobre el trabajo y no de un secreto copiado en su entorno.
En un flujo de trabajo de despliegue, esos hechos suelen incluir el emisor de CI, el repositorio o proyecto, la confirmación o referencia, la identidad del flujo de trabajo, el entorno y la audiencia prevista. El servicio de destino comprueba la afirmación firmada y la intercambia por una credencial de corta duración. Esta es la parte útil de la federación OIDC: el runner demuestra de dónde procede el trabajo sin llevar una clave de nube reutilizable en su almacén de secretos.
GitHub Actions documenta este patrón mediante su endpoint de tokens OIDC y el permiso id-token: write. El nombre del permiso puede inducir a error. Permite que el flujo de trabajo solicite un token de identidad, pero no concede por sí mismo permisos de despliegue. El rol de nube o el servicio de destino todavía debe rechazar los tokens cuyo emisor, audiencia, sujeto u otras afirmaciones no coincidan con el flujo de trabajo previsto.
Esa segunda parte es donde fallan muchas configuraciones. Un rol que acepta cualquier token de un repositorio ha delegado demasiada autoridad a todos los flujos de trabajo que cumplan los requisitos en ese repositorio. Una vista previa de documentación, un flujo de publicación y un despliegue en producción no deberían volverse equivalentes solo porque compartan el control de versiones.
Usa las afirmaciones para que el rol describa una única clase de trabajo. La sintaxis exacta depende del proveedor de CI y de la nube, pero la política debería responder preguntas sencillas:
- ¿Qué repositorio puede solicitar este rol?
- ¿Qué archivo de flujo de trabajo o entorno protegido puede solicitarlo?
- ¿Qué rama, etiqueta o condición de publicación puede solicitarlo?
- ¿Qué audiencia debe indicar la afirmación?
- ¿Durante cuánto tiempo puede seguir usándose la credencial emitida?
No escribas condiciones de política que no hayas inspeccionado en un token real. Muestra las afirmaciones en un entorno de prueba seguro, compáralas con la política de confianza y prueba los casos de denegación. La gente prueba los despliegues correctos y deja como suposición el camino peligroso, una solicitud de cambios procedente de una rama que no es de confianza.
Los secretos y las identidades resuelven problemas distintos
Un secreto demuestra que alguien lo posee. Una afirmación de identidad declara algo sobre la carga de trabajo que solicitó el acceso. Ambos pueden terminar en un token de portador, pero crean rutas de fallo muy diferentes.
Un secreto de CI almacenado normalmente no recuerda por qué el trabajo lo recibió. Si un flujo de trabajo puede leer DEPLOY_TOKEN, un script modificado, una acción comprometida, una ruta maliciosa de una solicitud de cambios o un comando de registro puede usar ese token allí donde lo permitan sus permisos. La rotación limita cuánto tiempo sigue siendo útil el secreto, pero no restringe el contexto de cada uso.
La federación de corta duración no hace que CI sea segura por arte de magia. Un trabajo comprometido todavía puede usar su token válido durante el tiempo de vida del token. La ventaja es que el radio de impacto es menor: el atacante debe ejecutar un trabajo elegible, cumplir las reglas del emisor y de las afirmaciones, y actuar antes de que caduque la credencial. También puedes revocar o modificar el rol que acepta el token sin tener que encontrar cada secreto copiado.
No confundas las credenciales de corta duración con los privilegios bajos. Un token válido durante diez minutos que puede borrar todas las bases de datos de producción sigue siendo inaceptable. Los límites de tiempo reducen la persistencia; el alcance de la autorización limita el daño. Necesitas ambas cosas.
El acceso de los agentes locales tiene la preocupación inversa. Un desarrollador puede usar la misma herramienta local en muchos repositorios y tareas, de modo que una única clave API amplia puede convertirse en un objetivo atractivo para un agente comprometido. El diseño local más seguro mantiene esa clave fuera del agente y permite solo la llamada API o el comando SSH concreto que el usuario haya aprobado. Si el servicio externo admite tokens con permisos detallados, úsalos también. Una bóveda protegida no puede mejorar un token con demasiados permisos después de que la solicitud abandona la máquina.
La comodidad peligrosa es usar un token para ambos mundos
Usar el token personal de un desarrollador en CI es popular porque permite desbloquear rápidamente un despliegue detenido. También es una de las peores formas de borrar la responsabilidad.
Un token personal suele llegar más lejos de lo que necesita la canalización. Puede pertenecer a una persona que cambia de equipo, abandona la empresa, lo usa desde su portátil y tiene un acceso definido por su pertenencia personal en lugar de por sus funciones de despliegue. Cuando aparece en CI, un registro de auditoría puede mostrar que actuó el token, pero no puede afirmar con honestidad si la acción procedió del desarrollador o de un trabajo de publicación.
También ocurre el error inverso. Los equipos exponen un secreto de despliegue de CI a un agente local para que pueda «probar lo mismo». Así entregan a la generación de código interactiva una capacidad de producción sin supervisión, a menudo con menos controles que el flujo de publicación. Además, la respuesta ante incidentes se vuelve muy difícil: ¿usó la credencial el agente, un script de shell o se filtró un valor copiado a otra herramienta?
Dale a cada entorno su propia frontera de autorización. Un desarrollador local puede tener acceso interactivo y revocable a un endpoint de desarrollo. Un trabajo de publicación puede recibir un rol federado limitado a un entorno de producción protegido. Un trabajo de una solicitud de cambios puede no tener ninguna autoridad de escritura. No son inconvenientes que deban suavizarse. Son las pruebas que usarás después para explicar por qué se permitió una acción.
Una regla útil para nombrar credenciales es que el nombre debe revelar tanto el actor como el propósito. ci-release-prod-deploy dice mucho más a un revisor que deploy-token. Mejor aún, el trabajo de CI no almacena un token con ese nombre. Solicita un rol vinculado a una identidad cuya política de confianza codifique el mismo propósito.
El estado del runner convierte el acceso temporal en residuos
Un trabajo puede usar una credencial temporal y aun así dejar problemas permanentes. Las filtraciones habituales aparecen en registros, trazas del shell, directorios de inicio en caché, capas de Docker, artefactos del espacio de trabajo y archivos escritos por acciones de terceros.
Los runners autoalojados merecen una sospecha especial porque pueden conservar el estado entre trabajos. Un trabajo que descargue código no confiable puede colocar un ejecutable modificado, alterar una caché compartida, inspeccionar un espacio de trabajo que haya quedado atrás o esperar a que un flujo de trabajo privilegiado reutilice el host. Aislar por nombre de repositorio no sirve si los trabajos comparten la misma cuenta del sistema operativo, el mismo socket de contenedor o el mismo sistema de archivos.
Los runners efímeros eliminan una gran clase de residuos porque desaparecen después del trabajo. No eliminan la necesidad de controlar las entradas del flujo de trabajo. Un trabajo privilegiado que ejecuta scripts de una solicitud de cambios no confiable sigue entregando autoridad a código no confiable, incluso en una máquina nueva.
GitHub Actions advierte que pull_request_target se ejecuta en el contexto del repositorio base y puede acceder a secretos o permisos de escritura. El evento tiene un propósito legítimo: los mantenedores pueden necesitar etiquetar o comentar una solicitud de cambios procedente de un fork. El fallo aparece cuando un flujo de trabajo activado por ese evento descarga la confirmación principal de la solicitud de cambios y ejecuta sus scripts. El flujo de trabajo ha combinado credenciales de confianza con código controlado por un atacante.
Mantén el patrón sencillo:
- Ejecuta el código no confiable de las solicitudes de cambios sin autoridad de despliegue.
- Reserva el acceso a entornos protegidos para referencias revisadas y rutas de flujo de trabajo controladas.
- Fija las acciones de terceros a referencias de confirmación inmutables cuando tu proceso lo permita.
- Mantén el material secreto fuera de cachés, artefactos y salidas de diagnóstico.
- Destruye las instancias de runners sensibles después del trabajo.
El primer y el cuarto punto evitan más incidentes reales que las convenciones elaboradas para nombrar tokens. Una credencial con un alcance perfecto se filtra igualmente si un comando de shell la imprime o si un artefacto incluye su archivo de configuración.
Una identidad de despliegue debería poder inspeccionarse en un solo archivo
Un flujo de trabajo debería hacer visible la solicitud de autoridad externa. Este ejemplo de GitHub Actions solicita una identidad OIDC solo en el trabajo de despliegue y declara un entorno de producción. Intencionadamente no contiene ninguna credencial de nube almacenada.
name: deploy
on:
push:
tags:
- "v*"
permissions:
contents: read
id-token: write
jobs:
deploy:
runs-on: ubuntu-latest
environment: production
steps:
- uses: actions/checkout@<full-commit-sha>
- name: Request deployment identity
run: |
token=$(curl -sS \
-H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \
"${ACTIONS_ID_TOKEN_REQUEST_URL}&audience=deploy.example.internal")
test -n "$token"
- name: Deploy
run: ./scripts/deploy.sh
El intercambio real normalmente usa una acción o una CLI de nube en lugar de guardar la respuesta en una variable del shell. Lo importante aquí es la forma de la solicitud: GitHub proporciona un token y una URL de solicitud de un solo uso, el flujo de trabajo pide una audiencia concreta y el destino decide después si esa identidad puede asumir un rol. El runner nunca debería tratar el token de identidad resultante como una credencial API general.
La política de confianza del lado de la nube debe rechazar un token a menos que sus afirmaciones identifiquen este contexto de publicación. No copies un ejemplo del proveedor y lo dejes así. Los ejemplos de proveedores suelen empezar con permisos amplios porque deben funcionar para muchos usuarios. Restringe el repositorio, el entorno, la rama o etiqueta y la audiencia antes de permitir que el rol modifique producción.
Si un destino no puede validar OIDC directamente, coloca delante un pequeño intermediario de credenciales. El intermediario valida la afirmación de CI, asigna las afirmaciones a acciones definidas con un alcance preciso, emite una credencial temporal para el destino y registra la correspondencia. No resuelvas esta limitación colocando un secreto permanente del destino en cada flujo de trabajo que necesite el servicio.
SSH muestra la diferencia con especial claridad
Las claves SSH son una mala pieza de mobiliario compartido para CI. Una clave privada copiada en un secreto de CI puede autenticar desde cualquier trabajo autorizado a leerla, y su parte pública rara vez indica al destino qué revisión del repositorio solicitó la conexión. Los comandos forzados, las restricciones de origen y las cuentas separadas pueden limitar el daño, pero la identidad básica sigue siendo una clave privada reutilizable.
Para el trabajo local, SSH puede necesitar una puerta interactiva porque un desarrollador puede ver que un agente quiere conectarse a un host concreto. La clave privada debe permanecer en un almacenamiento protegido, y el agente debería solicitar un comando específico en lugar de obtener acceso directo a la clave. El host todavía debe imponer sus propios permisos de cuenta y restricciones de comandos. La aprobación local controla el punto de inicio, pero no hace seguro un comando remoto peligroso.
Para CI, prefiere certificados SSH de corta duración cuando tu autoridad de certificación SSH y tu flota de destinos los admitan. El trabajo usa la federación de carga de trabajo para solicitar un certificado con un periodo de validez corto, un principal restringido y quizá un comando forzado. El destino verifica la autoridad certificadora en lugar de aceptar para siempre una clave privada copiada.
Si no hay certificados disponibles, usa una cuenta de despliegue distinta y una clave privada dedicada para cada clase de despliegue. Restringe esa cuenta en authorized_keys y en el servidor. Una restricción mínima tiene este aspecto:
command="/usr/local/bin/receive-release",no-port-forwarding,no-agent-forwarding,no-X11-forwarding ssh-ed25519 AAAA... ci-release
Esta línea impide que la clave abra un shell interactivo arbitrario y obliga al servidor a ejecutar un único programa receptor. Por sí sola no identifica el repositorio de origen. Combínala con una clave intermediada y de corta duración, o con otra señal de carga de trabajo verificada, si la decisión de publicación depende del contexto del repositorio.
No establezcas StrictHostKeyChecking=no en un script de CI para que SSH funcione. Eso desactiva la comprobación de identidad del servidor precisamente en un lugar donde los runners son fáciles de redirigir. Proporciona las claves de hosts conocidos mediante un mecanismo controlado, rótalas de forma deliberada y haz que el trabajo falle cuando la verificación del host cambie de forma inesperada.
Los registros de auditoría deben responder preguntas distintas
Un registro de acciones que solo diga «el despliegue terminó correctamente» es información operativa, pero no evidencia de seguridad suficiente. Necesitas reconstruir quién o qué recibió autoridad, qué solicitud hizo y si el propio registro cambió después.
Para un agente local atendido, registra la identidad del proceso local, el inicio de la sesión, la decisión de aprobación, la etiqueta de la credencial o clase de acción, el destino, la hora de la solicitud y el resultado. No registres valores secretos ni cargas completas sensibles. Una firma de proceso o una autoridad de firma de código resulta más útil que un nombre de proceso arbitrario, porque los nombres se pueden copiar fácilmente.
Para CI, registra el proveedor de CI, el ID de ejecución, el repositorio, la referencia del flujo de trabajo, el SHA de la confirmación, el actor que activó la ejecución cuando esté disponible, la clase de runner, el sujeto y la audiencia OIDC, el rol de destino, los parámetros de la acción que sea seguro conservar y el resultado. Así, un investigador puede distinguir una publicación etiquetada de un trabajo relanzado manualmente, y un flujo de trabajo de confianza de una concesión accidentalmente amplia.
Mantén los registros de autorización separados de los registros de aplicación. Los registros de aplicación pueden ser modificables, muestrearse o eliminarse como parte de las operaciones normales. Un registro de autorización necesita un comportamiento de solo anexado y una verificación independiente. El encadenamiento de hashes detecta cambios en la secuencia cuando conservas la cadena y la comparas con los registros esperados, pero no impide que un atacante evite los registros futuros. Por eso, envía los registros fuera de la máquina comprometida cuando el diseño lo permita.
Sallyport proyecta registros de sesión y de acciones individuales desde un registro de auditoría cifrado, encadenado mediante hashes y ciego a la escritura, y sp audit verify comprueba la cadena sin conexión y sin una clave de bóveda. Es una evidencia útil para las acciones de agentes atendidos. CI debería producir un rastro equivalente, vinculado a la carga de trabajo, en los sistemas que emiten y aceptan sus identidades.
La fatiga de las aprobaciones es un fallo de diseño local
La aprobación por llamada puede proteger credenciales locales de gran impacto, pero una solicitud en cada lectura inofensiva enseña a las personas a aceptar solicitudes sin leerlas. Cuando ocurre, el control se convierte en un ritual y un atacante solo tiene que esperar al trabajo rutinario.
Usa la aprobación por llamada para acciones cuyas consecuencias sean difíciles de revertir: escrituras en producción, cambios de DNS, administración de toda la organización, pushes forzados al control de código fuente o comandos contra hosts sensibles. Usa la autorización de sesión para acciones repetitivas y de bajo riesgo que un desarrollador pueda delegar razonablemente durante una ejecución del agente. Mantén separada la puerta de la bóveda para que una máquina bloqueada deniegue todas las acciones, aunque exista una aprobación de sesión antigua.
CI tiene su propia versión de la fatiga: puertas de aprobación manual que aparecen en cada trabajo y se aprueban porque el calendario de publicación se ha vuelto prioritario. Una aprobación de entorno protegido puede ser adecuada para un despliegue de producción, pero debe aprobar un artefacto de publicación y un destino claramente identificados. No debería compensar un flujo de trabajo que acepta código no confiable, un rol con afirmaciones amplias o un runner con un estado desconocido.
Una revisión práctica plantea una pregunta incómoda: si esta aprobación se hiciera automáticamente durante un mes, ¿qué podría ejecutarse? En los agentes locales, reduce la superficie de acciones aprobadas hasta que la respuesta sea tolerable. En CI, elimina la aprobación de las acciones normales de la máquina y vincula la autoridad a la identidad del trabajo.
Separa los caminos antes de la próxima solicitud de credenciales
Cuando un agente o una canalización solicite acceso, clasifica al solicitante antes de elegir un mecanismo de secretos. ¿Hay una persona presente que pueda inspeccionar la acción? ¿Es el solicitante una carga de trabajo repetible con afirmaciones que puedas validar? ¿Puede recibir un resultado limitado en lugar de una credencial? ¿Los permisos del destino coinciden con una única tarea nombrada?
Si el solicitante es un agente local, protege las credenciales fuera de su contexto, conserva una ruta de aprobación significativa y registra cada acción de una forma que el desarrollador pueda revisar. Si el solicitante es CI, usa federación de corta duración, vincula los roles a las afirmaciones del flujo de trabajo, aísla el runner y registra los hechos de la carga de trabajo que autorizaron la llamada.
No permitas que un token compartido borre esa frontera porque hoy sea más rápido. El próximo incidente te obligará a reconstruirla mientras intentas averiguar si actuó realmente una persona, un agente o un runner.
FAQ
¿Por qué los agentes de programación con IA y las canalizaciones de CI deberían usar credenciales distintas?
Un agente local funciona dentro de la sesión de trabajo de un desarrollador, donde una persona puede revisar una solicitud e interrumpir el proceso. Un trabajo de CI se ejecuta sin supervisión después de un evento en el control de versiones, por lo que sus permisos deben proceder de una identidad de carga de trabajo con un alcance preciso y caducar con el trabajo.
¿Puedo usar el mismo flujo de aprobación para CI y los agentes locales?
No. Una solicitud de aprobación solo demuestra que alguien hizo clic en un momento concreto; no describe el repositorio, la confirmación, el flujo de trabajo ni el rol de nube previsto. Usa aprobaciones para el trabajo local interactivo y autorización vinculada a la carga de trabajo para CI.
¿Hay algún caso en el que las claves API de larga duración sean aceptables en CI?
Por lo general, es un mal intercambio. Un secreto de larga duración convierte cualquier runner comprometido, filtración en registros, archivo de caché o dependencia maliciosa en una vía de acceso persistente. Es preferible usar tokens de corta duración creados a partir de una afirmación OIDC con restricciones de repositorio, referencia, flujo de trabajo y audiencia.
¿Qué es la identidad de carga de trabajo OIDC en CI?
Un token de carga de trabajo OIDC es una afirmación del proveedor de CI sobre el trabajo que se está ejecutando. Un servicio de nube o de secretos valida esa afirmación y emite una credencial de corta duración para un rol concreto, en lugar de pedir al trabajo que lleve un secreto de nube almacenado.
¿Debería un agente local de IA ver alguna vez una clave API?
Si es posible, no debería recibir ninguna credencial. Dale al agente local una interfaz de acciones que ejecute la solicitud fuera del agente y devuelva el resultado, mientras mantiene las credenciales API o SSH subyacentes en un almacenamiento local protegido.
¿Cómo evito que un trabajo de CI acceda al entorno equivocado?
No. El flujo de trabajo debe declarar cada destino externo que necesita, y cada destino debe tener su propia identidad y sus propios permisos. Un token de despliegue genérico resulta cómodo hasta que una compilación de documentación comprometida puede desplegar en producción.
¿Cuál es el mayor riesgo de seguridad de los runners de CI autoalojados?
Trata el estado del runner como residuo hostil. Usa runners efímeros para los trabajos sensibles, fija las dependencias, evita restaurar directorios que contengan secretos, oculta los secretos en los registros y asume que un trabajo privilegiado puede ser manipulado mediante el código que ha descargado.
¿Cómo audito una credencial de CI?
Primero inspecciona el origen de la credencial. Después revisa la audiencia, la afirmación del repositorio o proyecto, la restricción de rama o entorno, la duración y los permisos del destino. Si no puedes explicar cada uno de esos campos, el token tiene más confianza de la que merece el trabajo.
¿Qué debe registrar una auditoría de las acciones de un agente?
Separa los registros. Un registro de sesión interactiva debe identificar el proceso local y cada acción aprobada, mientras que los registros de CI deben vincular cada acción al ID de ejecución del proveedor, la revisión del repositorio, el flujo de trabajo, la clase de runner y el rol emitido. Un simple mensaje de éxito no es un registro de auditoría.
¿Puede Sallyport ejecutarse dentro de un runner de CI de Linux?
Usa una identidad de nube gestionada, un intermediario de despliegue dedicado o una cuenta de servicio con un alcance preciso detrás de una federación de corta duración. Sallyport está diseñado para el lado atendido de macOS, no como servicio de credenciales de CI sin supervisión.