# Cuentas Unix independientes para agentes de programación con IA de forma segura

Dar a un agente de programación con IA tu propio inicio de sesión Unix es un atajo con consecuencias duraderas. El agente hereda archivos que habías olvidado, credenciales que las herramientas guardaron en caché hace años, una configuración SSH permisiva, scripts de despliegue y la capacidad de hacer que un cambio incorrecto parezca exactamente parte de tu trabajo habitual. Una cuenta remota dedicada no vuelve inofensivo al agente, pero hace que su autoridad sea visible y fácil de contener.

Trata la cuenta como un límite alrededor de una tarea concreta. Si el agente necesita editar un repositorio, ejecutar su conjunto de pruebas y subir una rama, crea una identidad que pueda hacer esas cosas. No empieces con tu cuenta de desarrollador para intentar restarle privilegios después. Los permisos Unix se acumulan a través de grupos, directorios montados, configuración del shell y herramientas que dan por hecho que hay una persona al mando.

## Una cuenta Unix independiente da al agente una identidad distinta

Una cuenta dedicada proporciona al agente un UID, propietario de procesos, directorio personal, conjunto de autorizaciones SSH y registro de auditoría propios. Esas cinco propiedades importan más que una instrucción ingeniosa que le diga al agente que permanezca dentro de un repositorio.

Cuando un agente se ejecuta como `alex`, todos los procesos que inicia pertenecen a `alex`. Ese proceso puede leer todo lo que `alex` puede leer. También puede usar el agente SSH de `alex` si el reenvío o los sockets lo dejan expuesto. Puede inspeccionar el historial del shell, las credenciales de Git, la configuración de la nube, las cachés del gestor de paquetes y los directorios de proyectos que pertenezcan a `alex` o sean legibles por él. Aunque el agente se comporte perfectamente hoy, has hecho que sus permisos futuros dependan de cada comodidad que añadas a tu propia cuenta.

Una cuenta como `agentbuild` deja visible el punto de partida:

```sh
id agentbuild
getent passwd agentbuild
sudo -u agentbuild sh -lc 'umask; pwd; env | sort'
```

En un host Linux habitual, el primer comando debería mostrar un UID y una lista pequeña de grupos. El segundo debería mostrar un directorio personal como `/srv/agentbuild` o `/home/agentbuild`, no el directorio personal de una persona desarrolladora. El último comando detecta un problema que suele pasar desapercibido: las variables de entorno heredadas pueden apuntar a archivos de credenciales, proxies, cachés de tokens o rutas de ejecutables inusuales.

Esta diferencia suele confundirse: una clave SSH independiente no equivale a una cuenta independiente. Una clave nueva que inicia sesión en tu cuenta existente solo cambia la autenticación. No reduce los archivos, comandos ni configuración de red disponibles después del inicio de sesión. La autenticación independiente y la autorización independiente resuelven problemas distintos.

Usa una cuenta estable para una función estable. Si un agente crea pull requests y otro despliega artefactos de lanzamiento, asígnales identidades distintas. Entonces podrás responder sin adivinar a una pregunta operativa incómoda: ¿qué cuenta modificó este archivo, abrió esta conexión de red o creó este proceso?

## El límite de la cuenta no contiene todos los tipos de daño

Una cuenta Unix limita el acceso que controlan la propiedad Unix, los grupos, las listas de control de acceso y los permisos. No limita automáticamente los destinos de red, el uso de CPU, el agotamiento del disco, las vulnerabilidades del kernel, el acceso a archivos legibles por todos ni el acceso concedido por una credencial de servicio compartida.

Aun así, ese límite resulta muy útil. Un agente de programación suele tener capacidad suficiente para modificar código fuente, ejecutar scripts de paquetes, leer configuraciones y usar Git. Los scripts de paquetes pueden ejecutar comandos de shell arbitrarios. Los sistemas de compilación pueden leer variables de entorno. Una dependencia comprometida puede hacer lo mismo. Debes asumir que cualquier código que el agente pida al host ejecutar recibirá los permisos de la cuenta que lo ejecute.

No confundas la separación de cuentas con un contenedor, una máquina virtual o un firewall de red. Cada uno cumple una función distinta:

- Una cuenta Unix separa los archivos locales, la propiedad de los procesos y los permisos habituales de los comandos.
- Un contenedor puede limitar las vistas del sistema de archivos y el uso de recursos, pero un directorio del host montado de forma incorrecta anula esa ventaja.
- Una máquina virtual ofrece un límite más fuerte a nivel del sistema operativo cuando la carga de trabajo lo justifica.
- Los controles de red deciden con qué destinos puede conectarse la cuenta y qué puede salir del host.

Elige la combinación más pequeña que corresponda a las consecuencias de un fallo. Para una copia de pruebas desechable, puede bastar una cuenta dedicada en un worker aislado. En un host de despliegue de producción, combina el límite de la cuenta con SSH restringido, credenciales de despliegue limitadas, registros y una política de salida. Si el agente puede llegar a bases de datos de producción o a material de firma, un UID independiente es claramente insuficiente.

Una recomendación popular, pero equivocada, dice que hay que crear una cuenta y añadirla a los mismos grupos operativos que usa la persona desarrolladora «para que las compilaciones funcionen». Eso reproduce el problema original con otro nombre de usuario. Grupos como `docker`, `libvirt`, los grupos de copias de seguridad, de dispositivos y de registros privilegiados pueden tener una autoridad muy superior a la que sugieren sus nombres. En muchos sistemas, pertenecer a `docker` permite controlar prácticamente el host, porque un miembro puede iniciar un contenedor con el sistema de archivos del host montado.

## Crea una cuenta sin historial de herencia

Crea la cuenta remota sin acceso mediante contraseña, sin grupo de administrador y con un directorio personal que contenga únicamente los archivos que coloques allí de forma intencionada. Empieza con el directorio vacío, porque copiar archivos ocultos es una fuente habitual de autoridad accidental.

Los comandos exactos varían según el sistema operativo. En un host de estilo Debian o Ubuntu, un administrador puede crear una cuenta local con un directorio personal dedicado así:

```sh
sudo adduser --disabled-password --gecos '' --home /srv/agentbuild agentbuild
sudo passwd -l agentbuild
sudo install -d -m 700 -o agentbuild -g agentbuild /srv/agentbuild/.ssh
sudo -u agentbuild touch /srv/agentbuild/.hushlogin
```

`--disabled-password` impide la autenticación normal mediante contraseña para la cuenta nueva. `passwd -l` hace explícita esa intención en los sistemas compatibles con el bloqueo de contraseñas. No trates ninguna de las dos opciones como tu único control SSH: el servidor SSH tiene sus propios ajustes de autenticación mediante contraseña, y una clave existente todavía puede autenticar si instalas una.

En sistemas con `useradd`, usa opciones que creen el directorio personal y un grupo privado, y después verifica el resultado en lugar de confiar en la memoria. BSD y macOS usan herramientas distintas para gestionar cuentas, así que consulta `dscl`, `sysadminctl` o el manual de administración del sistema local en vez de pegar comandos de Linux en otro host.

Comprueba inmediatamente la propiedad:

```sh
namei -l /srv/agentbuild/.ssh
sudo -u agentbuild sh -lc 'touch ~/permission-test && ls -ln ~/permission-test'
sudo rm /srv/agentbuild/permission-test
```

La salida de `namei` recorre cada componente de la ruta. Ningún directorio padre debería conceder permisos de escritura a un grupo no relacionado, porque eso podría permitir que alguien sustituyera el directorio `.ssh` o manipulara los archivos que contiene. El archivo de prueba debería mostrar el UID y el GID primario numéricos de la cuenta.

No copies tu `.bashrc`, `.zshrc`, `.gitconfig` ni el directorio del editor en este directorio personal «para ahorrar tiempo». Estos archivos suelen añadir registros de paquetes privados, alias auxiliares, ajustes SSH, gestores de credenciales y hooks del shell. Añade ajustes individuales después de poder explicar por qué los necesita el agente. De todos modos, un shell sencillo y no interactivo debería ser el valor predeterminado para la automatización remota.

Establece una máscara de creación conservadora en el entorno de ejecución del agente. Un `umask` de `077` hace que los nuevos archivos normales sean privados para la cuenta, salvo que un comando elija explícitamente otros permisos. De vez en cuando esto revelará una suposición de la compilación. Bien. Corrige deliberadamente el punto en el que la compilación necesita compartir archivos, en lugar de hacer que todos los archivos generados sean legibles por cualquier usuario local.

## Los repositorios compartidos necesitan una regla de propiedad diseñada

El agente debería trabajar en una copia que le pertenezca o en un directorio de proyecto cuyos grupos y permisos puedas explicar. Un directorio en el que desarrolladores, herramientas de despliegue y agentes escriben como usuarios no relacionados se vuelve imposible de entender después del primer arreglo urgente de permisos.

El patrón más limpio es una copia que pertenezca por completo a la cuenta del agente. Una persona puede revisar los cambios mediante el control de versiones o leer los archivos con acceso de grupo controlado. Esto elimina la mayor parte de la confusión de la colaboración local, y Git proporciona el mecanismo de entrega que realmente importa.

A veces el agente debe escribir en un árbol de compilación común. En ese caso, crea un grupo de proyecto dedicado y activa el bit de ID de grupo en el directorio compartido para que los archivos nuevos hereden el grupo:

```sh
sudo groupadd projectbuild
sudo usermod -aG projectbuild agentbuild
sudo install -d -m 2770 -o releasebot -g projectbuild /srv/project-build
sudo setfacl -m u:agentbuild:rwx /srv/project-build
sudo setfacl -d -m g:projectbuild:rwx /srv/project-build
```

Este ejemplo necesita adaptarse a tu modelo de propiedad. La cuestión no es que las listas de control de acceso estén de moda. La cuestión es nombrar la superficie de colaboración y limitar el acceso de escritura a esa superficie. No respondas a un error de permisos ejecutando `chmod -R 777` ni cambiando todo un árbol de código fuente a un grupo de administradores compartido. Ambos enfoques ocultan el fallo hasta que alguien escribe donde no debe.

Un fallo sutil aparece cuando un directorio de compilación contiene enlaces simbólicos. El agente puede tener permiso de escritura en `/srv/project-build`, mientras que un enlace simbólico dentro de él apunta a `/etc`, a un directorio de lanzamientos o al directorio personal de una persona. Inspecciona los scripts de configuración y los directorios generados antes de conceder acceso recursivo amplio. El límite de la cuenta solo protege las rutas que el sistema de archivos evalúa realmente con los permisos de esa cuenta.

Git tiene una comprobación de propiedad relacionada. Las versiones modernas de Git pueden rechazar un repositorio que parezca pertenecer a otro usuario por «propiedad dudosa». No lo soluciones añadiendo directorios arbitrarios a los ajustes globales de `safe.directory` del agente. Haz que la copia pertenezca a la cuenta que ejecuta Git. Si no puedes evitar una copia compartida, documenta su propiedad y añade solo esa ruta concreta después de entender por qué Git la rechazó.

## El acceso SSH debe identificar al controlador y limitar la sesión

Usa una clave pública SSH dedicada para el controlador del agente y, cuando el flujo lo permita, vincula las restricciones a esa clave en `authorized_keys`. Una cuenta independiente con un shell interactivo sin restricciones es mejor que compartir un inicio de sesión de desarrollador, pero sigue dando a un proceso autónomo una superficie de comandos amplia.

OpenSSH documenta los controles disponibles en `sshd_config(5)` y `authorized_keys`. `PasswordAuthentication no` desactiva los inicios de sesión mediante contraseña a nivel del servidor. `AllowUsers` puede limitar quién inicia sesión. Las opciones por clave pueden desactivar el reenvío de puertos, el reenvío del agente, el reenvío X11 y la asignación de terminales pseudo-TTY. Son controles normales, no una configuración SSH exótica.

Para un agente que solo necesite recibir un comando de Git o ejecutar un wrapper fijo, una entrada de `authorized_keys` puede tener este aspecto:

```text
restrict,command="/usr/local/libexec/agent-git-wrapper" ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI... agent-controller
```

La opción `restrict` de OpenSSH es una abreviatura que desactiva varias funciones de reenvío y sesión, según la versión del servidor y la página del manual. El `command` forzado hace que el servidor ejecute el wrapper en lugar de aceptar el comando solicitado por el cliente. Comprueba la versión de OpenSSH instalada antes de confiar en una opción, porque los hosts antiguos pueden no admitir todas las restricciones.

El wrapper debe validar sus propios parámetros. Un comando forzado no convierte un script descuidado en un límite de seguridad. Si pasa a `sh -c` un comando proporcionado por el cliente sin entrecomillarlo, el cliente a menudo puede recuperar la ejecución de comandos. Mantén el wrapper pequeño, usa rutas fijas, rechaza los argumentos inesperados y registra la solicitud.

Para un agente de programación general que necesite un shell para inspeccionar, editar, compilar y probar, los comandos forzados pueden ser demasiado restrictivos. Conserva la clave y la cuenta dedicadas, desactiva los reenvíos que no necesites y limita las direcciones de origen cuando la topología de red lo permita. La cuenta remota nunca debería aceptar tu clave SSH personal solo porque ya está en tu portátil.

Inspecciona la configuración SSH efectiva, no solo el archivo que editaste:

```sh
sudo sshd -T | grep -E 'passwordauthentication|permitrootlogin|allowusers|allowgroups'
ssh -vvv agentbuild@build-host true
```

El primer comando muestra los ajustes efectivos del servidor. El segundo expone la ruta de autenticación y los métodos rechazados desde el lado del cliente. Haz la prueba con la clave real del agente. Probar con tu propia clave demuestra muy poco.

## Las reglas de sudo suelen borrar la protección que acabas de añadir

No concedas sudo sin restricciones a una cuenta de agente. `agentbuild ALL=(ALL) NOPASSWD: ALL` hace que el UID independiente sea casi decorativo, porque cualquier comando o script que ejecute el agente puede convertirse en root.

La gente añade esta regla cuando una compilación necesita una acción privilegiada y luego la deja porque la compilación por fin funcionó. Así es como una excepción limitada se convierte en control permanente del host. Si una tarea realmente necesita elevación, pregunta primero si debería realizarla un servicio propiedad de root, un worker de despliegue o una acción administrativa independiente.

El manual `sudoers(5)` advierte que la coincidencia de comandos tiene límites. Los argumentos importan. Los comodines pueden coincidir con más elementos de lo que los administradores esperan. Permitir un editor, un intérprete, un gestor de paquetes, un script de shell que el agente pueda modificar o un comando que cargue configuración desde un directorio con permisos de escritura puede conducir directamente a la ejecución arbitraria como root.

Un patrón más seguro usa un wrapper propiedad de root con un comportamiento fijo. Supón que un agente debe reiniciar un único servicio conocido después de colocar un artefacto ya revisado en un directorio fijo. El wrapper debería usar rutas absolutas de comandos, rechazar argumentos, comprobar la propiedad y los permisos de sus entradas y realizar únicamente ese reinicio. Entonces la regla sudo nombra exactamente ese wrapper:

```text
Cmnd_Alias AGENT_RELEASE = /usr/local/sbin/restart-project-service
agentbuild ALL=(root) NOPASSWD: AGENT_RELEASE
```

Coloca la regla en un archivo gestionado con `visudo` y haz que tanto el wrapper como su directorio padre pertenezcan a root y no sean modificables por `agentbuild`. Esto no garantiza la seguridad. Hace comprobable una afirmación limitada: la cuenta puede ejecutar un único programa propiedad de root sin una cadena de comandos controlada por quien lo llama.

Si no puedes describir la acción privilegiada permitida en una sola frase, todavía no concedas sudo. Divide el trabajo hasta que puedas hacerlo. La dificultad es una señal de diseño, no un motivo para pegar una regla amplia.

## Las credenciales necesitan su propio límite, separado de la cuenta de inicio de sesión

Una cuenta restringida sigue siendo peligrosa si su directorio personal contiene un archivo de credenciales de la nube, un token de despliegue amplio o una clave privada SSH que llegue a todos los hosts. No traslades tu montón de credenciales desde tu directorio personal al del agente y des por terminado el trabajo.

Emite credenciales para la acción, el alcance y el entorno que el agente realmente necesita. Una credencial de control de código que pueda subir cambios a un repositorio es distinta de una credencial del registro de producción. Una clave de despliegue limitada a un host es distinta de una clave privada aceptada en toda una infraestructura. Mantén visibles esas diferencias en los nombres de las cuentas, los comentarios y los registros de revocación.

Evita secretos de larga duración en variables de entorno del shell. Las variables de entorno se filtran fácilmente mediante salidas de diagnóstico, procesos secundarios, informes de fallos y reglas de inspección de procesos que varían según el sistema operativo. A veces son inevitables, pero merecen una vida breve y una ruta de lanzamiento muy controlada.

Sallyport mantiene las credenciales HTTP y SSH en su bóveda cifrada y ejecuta la acción solicitada sin entregar el texto sin cifrar de la credencial a un agente compatible con MCP. Esto resulta útil cuando el agente necesita realizar una llamada autenticada, pero no debería recibir un archivo de tokens ni una clave privada en su cuenta remota.

Esto crea dos comprobaciones independientes. La cuenta Unix remota determina qué puede hacer el proceso en esa máquina. La pasarela de acciones determina si un proceso de agente puede solicitar una credencial que permita una acción HTTP o SSH. No las combines en un único control: una pasarela de credenciales no puede reparar una cuenta remota que pueda leer archivos de producción, y una cuenta limitada no puede impedir que un agente use un token que ya se le entregó.

Para la automatización SSH, evita copiar tu clave privada personal en `/srv/agentbuild/.ssh`. Crea una credencial dedicada y restringe el servidor que la acepta. Si el destino remoto admite comandos forzados o restricciones de origen, úsalos. Si no los admite, limita la cuenta en ese destino. Revocar debería significar eliminar una credencial de agente, no cambiar la clave que usas para la administración habitual.

## Los registros deben permitir distinguir la intención de la ejecución

Registra la sesión del agente, los comandos de la cuenta cuando sea práctico y los cambios que realice. Los registros que solo indiquen `agentbuild logged in` no ayudarán cuando necesites reconstruir si una persona solicitó una acción, un agente la propuso o un script remoto la ejecutó.

Unix ya proporciona evidencias útiles. Los registros de autenticación SSH identifican la clave aceptada y la dirección de origen. La contabilidad de procesos o las funciones de auditoría pueden seguir la ejecución, según el host. El control de versiones registra las confirmaciones y los archivos modificados. Los registros de compilación almacenan comandos y artefactos. Mantén los relojes sincronizados para poder comparar estos registros.

No dependas únicamente del historial del shell. Los comandos no interactivos pueden no entrar en el historial, los usuarios pueden editarlo y un agente puede ejecutar herramientas que a su vez inicien otros comandos. El historial del shell sirve para depurar, pero no es un registro fiable.

Después de una ejecución, una revisión útil plantea cuatro preguntas concretas:

1. ¿Qué controlador se autenticó como la cuenta del agente?
2. ¿Qué comandos o trabajos de compilación se ejecutaron bajo ese UID?
3. ¿Qué archivos fuera del espacio de trabajo previsto cambiaron?
4. ¿Con qué sistemas remotos y servicios autenticados contactó el proceso?

La respuesta a la tercera pregunta detecta pronto la expansión de permisos. Compara las rutas modificables por la cuenta con el espacio de trabajo previsto antes de que un incidente te obligue a hacer ese inventario. Una lista de procesos también puede revelar sorpresas: si un agente de compilación supuestamente breve deja workers en segundo plano, esos workers conservan los permisos de la cuenta después de que termine la sesión de orquestación.

Sallyport registra las sesiones de los agentes y las acciones individuales en un registro de auditoría cifrado y encadenado mediante hashes. `sp audit verify` puede verificar la cadena sin conexión y sin una clave de bóveda. Ese registro es más sólido cuando también conservas las evidencias del host remoto que muestran qué hizo la acción SSH autorizada después de llegar allí.

## Compartir el inicio de sesión de un desarrollador falla de formas previsibles

El patrón de fallo suele comenzar con una petición razonable: permitir que el agente ejecute las mismas pruebas que tú. La persona desarrolladora dirige el agente a un host remoto existente y autoriza su clave SSH normal porque la copia, las cachés de paquetes y las dependencias de compilación ya funcionan.

El agente ejecuta un comando de prueba. El arranque de la prueba lee el entorno de la persona desarrolladora y encuentra un token del registro. La instalación de una dependencia ejecuta un script postinstall. Ese script puede leer el directorio personal de la persona desarrolladora, inspeccionar la configuración SSH, usar cualquier asistente de credenciales accesible y conectarse con el alcance de red existente de esa persona. No hace falta explotar un fallo del kernel. El proceso simplemente tiene la misma autoridad que la persona desarrolladora.

Más tarde, un script de despliegue falla porque espera una ruta de artefactos modificable. Alguien lo arregla con un cambio amplio de grupo. Ahora la cuenta puede escribir en un directorio de lanzamientos. Una segunda solución añade sudo sin contraseña porque un reinicio de servicio está bloqueado. Para entonces, la supuesta configuración del agente ha heredado una identidad de desarrollador, un acceso amplio de escritura al sistema de archivos, credenciales reutilizables y escalada a root.

Una cuenta dedicada cambia el recorrido de este fallo. La prueba inicial puede fallar porque la cuenta no puede leer una configuración del registro ni escribir en una caché compartida antigua. Ese fallo es útil. Te indica que debes emitir una credencial restringida para el registro, crear un directorio de caché propio o rediseñar la compilación. Cada corrección se convierte en una concesión explícita que puedes revisar.

Espera cierta fricción inicial. Si la cuenta nueva funciona perfectamente en el primer intento contra una configuración de desarrollador madura, inspecciónala con atención. Puede significar que el host ya ofrece demasiados datos y autoridad a todos los usuarios locales.

## Prueba el límite como el agente y elimina lo que te sorprenda

Prueba la cuenta desde una sesión administrativa independiente antes de permitir trabajo desatendido. No hagas la prueba cambiando el indicador de tu shell y suponiendo que la identidad cambió correctamente. Autentica con la clave dedicada, usa el comando de inicio real y observa el resultado desde fuera de la sesión.

Ejecuta esta comprobación compacta de aceptación después de cada cambio importante de acceso:

```sh
ssh -i ./agentbuild_key agentbuild@build-host 'id; umask; pwd; find ~ -maxdepth 1 -printf "%M %u %g %p\\n"'
ssh -i ./agentbuild_key agentbuild@build-host 'sudo -n true; echo sudo_status=$?'
ssh -i ./agentbuild_key agentbuild@build-host 'find /srv/project-build -xdev -type f -perm -0002 -print'
```

La primera línea confirma la identidad, el directorio de trabajo, la máscara de creación y los permisos del directorio personal. La segunda normalmente debería devolver un estado distinto de cero, porque la cuenta no debería tener sudo general. La tercera busca archivos normales modificables por todos en el directorio de proyecto compartido. En sistemas cuyo `find` no tenga la opción GNU `-printf`, usa `ls -ld` y `stat`.

Después, prueba deliberadamente las acciones denegadas. Intenta leer el directorio personal de una persona, escribir fuera del espacio de trabajo, usar la ruta de una clave de despliegue personal y abrir una sesión de reenvío SSH si el reenvío debería estar desactivado. Una restricción que nunca pruebas es solo una intención escrita en la configuración.

Revisa también la cuenta después de ejecutar trabajos reales. Elimina claves autorizadas obsoletas, pertenencias a grupos, cachés, listas de control de acceso temporales y permisos de despliegue cuando la tarea ya no los necesite. La limpieza de permisos rara vez ocurre durante una fecha límite. Inclúyela en los criterios de finalización del trabajo.

Empieza creando la cuenta sin acceso más allá de su propio directorio personal y una copia de prueba. Añade una sola capacidad cuando un comando real falle y puedas expresar con precisión el permiso necesario. Ese método parece más lento durante la configuración. Es mucho más rápido que averiguar qué partes del inicio de sesión de un desarrollador copió, usó o dañó un proceso autónomo.
