# Acceso sudo para agentes de IA: controla cada elevación

Conceder sudo a un agente de IA porque ya puede abrir un shell remoto es un error de categoría. La ejecución remota responde a «¿puede llegar a esta máquina?». La elevación responde a «¿puede esta solicitud modificar un estado protegido?». Esas decisiones necesitan pruebas, controles y registros distintos.

He visto a equipos reducirlo todo a una sola función de comodidad: un agente se conecta por SSH, ejecuta `sudo` y deja una transcripción del chat. Parece ordenado hasta que un comando defectuoso reinicia el servicio equivocado, sustituye un archivo de configuración o sigue una instrucción oculta en un repositorio. Entonces nadie puede decir qué proceso tenía la autoridad, quién lo aprobó ni por qué parecía necesario el acceso de root.

El **acceso sudo para agentes de IA** debería significar una excepción limitada y atribuible para una operación concreta. Nunca debería significar que el agente ha adquirido el poder permanente de un administrador durante el resto de una tarea.

## Un shell remoto y sudo responden a preguntas distintas

Una sesión SSH demuestra que un cliente se autenticó en una cuenta remota. No demuestra que todos los comandos escritos o generados en esa sesión merezcan autoridad de root. Mantén la cuenta normal lo bastante útil para descubrir información, compilar, probar, leer registros y preparar despliegues. Coloca el pequeño conjunto de cambios protegidos detrás de una ruta de elevación independiente.

La diferencia se difumina porque el shell hace que la escalada de privilegios parezca un detalle de sintaxis:

```sh
ssh deploy@api-02 'sudo systemctl restart payment-worker'
```

Esa línea oculta al menos cinco decisiones: qué proceso del agente la emitió, a qué host iba dirigida, si el nombre del servicio es correcto, por qué hace falta reiniciarlo y si una persona aceptó las consecuencias. SSH puede autenticar la conexión. `sudo` puede cambiar el usuario efectivo. Ninguna de las dos herramientas registra por sí sola el motivo ni ofrece un punto de decisión adecuado para el trabajo autónomo.

Los comandos remotos ordinarios deben seguir siendo ordinarios. Un agente puede ejecutar `systemctl status`, inspeccionar un registro de servicio que ya tiene permiso para leer, comparar una configuración generada o ejecutar una comprobación de estado sin pedir root. Esta separación ofrece una ventaja práctica: el agente puede reunir pruebas antes de pedir que alguien apruebe un cambio.

No corrijas en exceso poniendo cada comando del shell detrás de una confirmación humana. Eso genera fatiga de aprobación y hace que la gente empiece a aceptar tarjetas que ya no lee. Coloca la fricción donde una acción cruza un límite protegido: gestión de servicios, cambios en paquetes del sistema, escrituras privilegiadas en archivos, cambios de cuentas, reglas de red, secretos, configuración de arranque y operaciones sobre datos de producción.

La forma de escribir un comando no determina su riesgo. `sudo cat /var/log/...` puede exponer contenido sensible. `sudo systemctl restart ...` puede interrumpir un servicio que usan los clientes. Un `sudo install` aparentemente inofensivo puede sustituir un ejecutable. Clasifica el efecto, el objetivo y si los argumentos permiten escapar hacia un comportamiento arbitrario como root.

## Las reglas de sudoers son listas de permitidos, no un argumento de seguridad

El manual de `sudoers` indica que sudo determina si un usuario puede ejecutar un comando según la ruta del comando y, cuando se configura así, sus argumentos. Es un mecanismo útil, pero no convierte una lista amplia de permitidos en una delegación segura.

Una regla como esta delega mucho más de lo que muchas personas pretenden:

```sudoers
autobot ALL=(root) NOPASSWD: /usr/bin/systemctl *
```

Permite a la cuenta iniciar, detener, reiniciar, activar, desactivar, enmascarar e inspeccionar cualquier unidad que admita el `systemctl` local. Un agente que pueda influir en un archivo de unidad, un archivo de entorno o el ejecutable de un servicio puede convertir un reinicio permitido en ejecución de código como root. La regla tampoco incluye un motivo del cambio ni una caducidad.

Las restricciones de argumentos solo ayudan cuando el programa permitido tiene una interfaz pequeña y estable, y no puede interpretar entradas controladas por un atacante como una ruta, una expresión de shell, un complemento, un editor, un paginador o una fuente de configuración. Los administradores suelen pasar esto por alto porque el comando resulta familiar. Un comando no se vuelve limitado solo porque la ruta de su binario sea absoluta.

Prefiere un wrapper diseñado para un propósito concreto, con una operación fija y una validación explícita. Por ejemplo, un wrapper de reinicio puede aceptar un nombre de servicio definido de antemano en lugar de argumentos arbitrarios de `systemctl`:

```sh
#!/bin/sh
set -eu

case "${1:-}" in
  payment-worker|report-worker) ;;
  *) echo "unsupported service" >&2; exit 64 ;;
esac

exec /usr/bin/systemctl restart "$1"
```

Después, limita sudo a ese wrapper y a los argumentos exactos cuando la plataforma lo admita:

```sudoers
autobot ALL=(root) /usr/local/sbin/restart-approved-service payment-worker, \
                    /usr/local/sbin/restart-approved-service report-worker
```

Esto evita que se multipliquen los subcomandos de `systemctl`. No responde a si el reinicio tiene sentido en ese momento. El wrapper también necesita ser propiedad de root, tener directorios padre no escribibles y estar protegido frente a variables de entorno controladas por el agente. Si el agente puede editar el wrapper o sustituir algo que este ejecuta, la regla ha fallado.

Evita la recomendación popular de «usar NOPASSWD para la automatización». A la gente le gusta porque los trabajos desatendidos dejan de fallar ante solicitudes de contraseña. La solicitud de contraseña nunca fue el control importante para un agente autónomo. Sustituirla por una elevación sin restricciones solo elimina la última pausa visible. Sustitúyela por una autorización concreta y un registro responsable, no por comodidad vacía.

## Una solicitud de elevación necesita un motivo que un revisor pueda juzgar

El motivo forma parte de la decisión de autorización, no es una frase decorativa que se copia en un ticket después de los hechos. El agente debería crear la solicitud antes de la elevación, y la interfaz de aprobación debería mostrar la operación propuesta junto con ese motivo.

Exige que la solicitud reúna estos campos:

- El objetivo exacto, como `api-02` y `payment-worker`.
- La operación privilegiada solicitada, incluidos los argumentos fijos.
- El motivo, vinculado a un incidente, despliegue, tarea de mantenimiento o condición observada.
- El efecto previsto y una acción de reversión.
- Una caducidad lo bastante breve para que una solicitud abandonada no se convierta en autoridad permanente.

El motivo debe incluir pruebas que una persona pueda evaluar. «Necesito sudo para arreglar la compilación» significa que el agente todavía no ha diagnosticado lo suficiente. «Sustituir `/etc/acme/client.conf` por la configuración de la versión revisada después de que la renovación actual del certificado informe de un error de análisis; restaurar la versión anterior si la validación falla» identifica un archivo, una condición y una vía de recuperación.

Mantén la solicitud estructurada aunque también conserves una explicación de texto libre. Los campos estructurados dificultan que un agente cambie silenciosamente el objetivo entre la planificación y la ejecución. También permiten revisar lo ocurrido sin pedir a alguien que reconstruya una decisión a partir de varias ventanas de chat.

Usa un identificador de solicitud inmutable. La aprobación debe autorizar ese identificador, el objetivo especificado y la operación especificada. No apruebes una instrucción en lenguaje natural como «gestiona la interrupción» y dejes que el agente decida después qué comandos de root entran en ella. Eso convierte una aprobación humana en un cheque en blanco sin límite.

Un registro de solicitud útil puede tener este aspecto:

```json
{
  "request_id": "elev-7f4c2",
  "agent_session": "run-91b0",
  "host": "api-02",
  "operation": "/usr/local/sbin/restart-approved-service payment-worker",
  "reason": "Deployment 482 left payment-worker unhealthy; status shows repeated configuration parse errors.",
  "expected_effect": "Service restarts with the reviewed configuration.",
  "rollback": "Restore the previous configuration revision and restart the service.",
  "expires_at": "2025-03-08T14:25:00Z"
}
```

El registro de ejecución debe devolver `request_id`. Sin ese vínculo, un aprobador puede haber aceptado una operación mientras el agente realizaba otra.

## El proceso del agente necesita su propia identidad

Una cuenta `deploy` compartida hace que todos los actores parezcan iguales después del daño. Asigna a la ejecución del agente una identidad que registre qué ejecutable la inició, qué persona o servicio la puso en marcha, qué repositorio y tarea recibió y cuándo termina su autoridad.

La identidad humana y la identidad del proceso son hechos distintos. Un desarrollador puede iniciar un agente, pero el proceso del agente emite el comando horas después, tras leer archivos, resultados de herramientas y respuestas de red. El registro de auditoría debe conservar ambos hechos. «Jamie inició la ejecución 91b0» y «el proceso de agente firmado 91b0 solicitó la elevación» permiten a un investigador distinguir el patrocinio de la ejecución.

La firma de código puede ayudar a identificar el ejecutable local que solicitó el acceso. No puede demostrar que la instrucción del modelo fuera segura ni hacer confiable un repositorio comprometido. Trata la identidad del proceso como un límite sobre quién puede solicitar, no como una prueba de que la solicitud sea sensata.

No permitas que el agente reciba una contraseña de root reutilizable, una credencial SSH privada que llegue directamente a una cuenta de administrador ni una marca de tiempo de sudo de larga duración que pueda renovar indefinidamente. Cada una convierte una solicitud limitada en una capacidad portátil. En cuanto el agente puede copiar esa capacidad a un espacio de trabajo, registro, artefacto de compilación o proceso hijo, pierdes el control sobre su propagación.

Las credenciales de corta duración reducen la exposición, pero no convierten un comando incorrecto en uno bueno. Usa la caducidad junto con una operación definida, un proceso vinculado y un motivo. Si falta cualquiera de esos elementos, tienes un token de acceso con un nombre más agradable.

## Un shell de root da al agente demasiado margen para reinterpretar instrucciones

Nunca apruebes `sudo -i`, `sudo su`, `sudo sh` ni un `sudo bash` sin restricciones para una sesión de agente. Un shell de root autoriza todos los comandos posteriores, incluidos los que se construyen a partir de resultados de herramientas que no existían cuando alguien aprobó el primero.

La misma advertencia se aplica a formas de comando que ocultan un intérprete arbitrario:

```sh
sudo python3 -c "$AGENT_TEXT"
sudo env CONFIG="$AGENT_TEXT" /usr/local/sbin/apply-config
sudo tee /etc/some-file.conf
```

Cada ejemplo parece limitado hasta que inspeccionas su canal de entrada. Python ejecuta código arbitrario. `env` puede alterar el comportamiento de un programa de formas que quien lo llama no previó. `tee` da autoridad para escribir sobre una ruta arbitraria propiedad de root si la ruta no está limitada. Una política de comandos que ignora los argumentos y el entorno es solo una política de nombres de archivo.

Un diseño mejor separa el diagnóstico de la ejecución. El agente puede inspeccionar los hechos sin privilegios, generar un cambio propuesto y enviarlo a revisión. Después, un helper privilegiado y limitado recibe únicamente las entradas aprobadas. El helper debe volver a validar esas entradas porque la validación durante la revisión y la validación durante la ejecución protegen frente a fallos distintos.

Imagina un agente de despliegue que detecta un fallo de servicio. Con sudo amplio, podría editar una sobrecarga de unidad, recargar el gestor y reiniciar el servicio. Una cadena maliciosa en una configuración controlada por el repositorio puede convertirse en una línea `ExecStart`, y el reinicio la ejecutaría con autoridad de root. En un diseño limitado, el agente puede leer el estado y preparar la configuración revisada, pero un helper de despliegue privilegiado solo acepta el resumen criptográfico del contenido de un artefacto aprobado. Rechaza archivos de unidad, rutas arbitrarias y sobrecargas no gestionadas.

Eso requiere más trabajo que entregar un shell. También marca la diferencia entre una operación conocida y un intérprete capaz de inventar operaciones nuevas después de la aprobación.

## La aprobación debe seguir el riesgo de la operación

Una aprobación para toda una sesión puede tener sentido para acciones remotas rutinarias y limitadas. No lo tiene para cada uso de una capacidad administrativa. Trata la autorización de sesión y la autorización por llamada como controles distintos.

La autorización de sesión responde a «¿puede este proceso de agente identificado usar canales remotos ordinarios durante esta ejecución?». Impide que un proceso local desconocido suplante silenciosamente a un agente conocido. Debe terminar cuando el proceso sale, y un operador debe poder revocarla de inmediato.

La autorización por llamada responde a «¿puede ejecutarse ahora esta acción concreta?». Úsala para operaciones con un radio de impacto grande, credenciales escasas, impacto en producción o efectos irreversibles. La tarjeta de aprobación debe empezar por la identidad del proceso y mostrar después el objetivo, la operación, el motivo, la caducidad y el efecto previsto. Ocultar la identidad bajo un montón de prosa contradice el objetivo.

No obligues al operador a analizar cien líneas de shell generado. Da al agente un vocabulario de operaciones con nombre y detalles representados de forma clara. «Reiniciar el trabajador de pagos en api-02» se puede revisar. Un bloque de shell con comillas anidadas hace que la persona acepte algo que no puede entender.

La ruta de aprobación también necesita una ruta de rechazo que ayude al agente a recuperarse. Devuelve una denegación clara y, cuando corresponda, una instrucción como «requiere un registro de cambio de despliegue» o «esta operación no está disponible en producción». No permitas que el agente reintente cambiando ligeramente la redacción hasta que alguien cansado la acepte. La denegación debe cerrar la solicitud, salvo que una persona cree otra con información sustancialmente distinta.

En una emergencia, conserva la misma disciplina. Un ingeniero de guardia puede aprobar una solicitud limitada con una caducidad breve y una referencia al incidente. La urgencia puede justificar una revisión más rápida, pero no eliminar el registro ni conceder un shell interactivo de root.

## Los registros de auditoría deben sobrevivir al agente que creó la solicitud

Una transcripción de terminal ayuda a depurar, pero no puede cargar con todo el peso de un registro de auditoría. El agente puede omitirla, modificar archivos locales o ejecutar comandos mediante una ruta que la transcripción no capturó. Registra la autorización y la ejecución en un almacén que el agente no pueda reescribir.

Registra la decisión por separado de la acción. El registro de aprobación debe mostrar quién aprobó, cuándo lo hizo, la identidad del proceso, el contenido exacto de la solicitud y la caducidad. El registro de ejecución debe mostrar si se ejecutó el helper, a qué host llegó, qué devolvió, cuándo terminó y qué identificador de solicitud lo autorizó.

Incluye un resumen limitado del resultado o un resumen de salida en lugar de volcar datos sensibles en un diario visible para demasiadas personas. Quien investiga necesita pruebas suficientes para ver que `payment-worker` se reinició y volvió a estar saludable. No necesita una contraseña de base de datos copiada porque apareció por casualidad en stderr.

La evidencia de manipulación importa porque los registros de auditoría se vuelven interesantes después de un incidente. Una cadena de hashes vincula cada registro con el anterior. Si alguien cambia o elimina una entrada, la verificación detecta la discontinuidad. Mantén la verificación independiente del agente y, si es posible, independiente de las credenciales usadas para ejecutar las acciones. Un registro que necesita la credencial de administrador comprometida para verificarse resulta menos útil justo cuando más lo necesitas.

Por ejemplo, un verificador sin conexión debería mostrar una secuencia como esta:

```text
$ sp audit verify --file agent-audit.enc
verified records: 184
chain start: 8c1a...e72d
chain end: 4bf0...193a
status: valid
```

Lo importante no es el nombre del comando. Es que el verificador pueda detectar un registro cifrado modificado sin pedir al agente que se explique. Conserva copias fuera de la máquina donde trabaja el agente, porque un atacante que controle esa máquina puede eliminar todo el diario.

Sallyport aplica esta separación al mantener las credenciales fuera del agente y proyectar diarios de sesión y de acciones individuales desde un registro de auditoría cifrado, encadenado mediante hashes y ciego a la escritura. Este modelo resulta útil cuando los agentes necesitan realizar acciones HTTP o SSH, pero nunca deberían poseer las credenciales que las autorizan.

## Los helpers privilegiados necesitan interfaces limitadas y pruebas con entradas hostiles

Un helper es código sensible para la seguridad aunque tenga veinte líneas. Pruébalo como si cada argumento, variable de entorno, directorio de trabajo y archivo referenciado proviniera de un atacante, porque un agente de IA puede ser inducido a pasar material hostil sin intención de causar daño.

Empieza con un inventario de operaciones. Anota cada efecto privilegiado que el agente necesita legítimamente, como reiniciar un servicio específico o instalar un artefacto de versión firmado. Si no puedes describir un efecto sin decir «ejecutar un comando arbitrario», la operación todavía no está diseñada.

Para cada helper, responde estas preguntas antes de incluirlo en sudoers:

1. ¿Qué entradas exactas puede proporcionar quien lo llama y cómo valida el helper cada una?
2. ¿Qué rutas del sistema de archivos, ejecutables, archivos de configuración y variables de entorno influyen en su comportamiento?
3. ¿Puede alguna entrada aceptada activar un shell, intérprete, paginador, editor, cargador de complementos o descarga de red?
4. ¿Verifica el helper la propiedad, los permisos y la identidad del contenido antes de actuar?
5. ¿Qué registro emite cuando falla la validación y cuando la ejecución tiene éxito?

Ejecuta pruebas negativas, no solo el camino correcto. Pasa una travesía de rutas con `../`, metacaracteres de shell, un nombre de servicio vacío, una entrada demasiado grande, Unicode inesperado y un nombre válido que apunte a un estado inseguro. Intenta sustituir un archivo referenciado entre la validación y su uso. Comprueba si un directorio de registros, temporal o padre que permita escritura deja al agente redirigir la salida de root.

También revisa las dependencias después de las actualizaciones. Un helper seguro frente a una versión de un comando puede volverse inseguro cuando una nueva opción acepta una ruta de configuración o carga una extensión. Las interfaces limitadas reducen esta carga de mantenimiento, pero no la eliminan.

## Despliega la elevación como una excepción controlada

Empieza eliminando las formas más peligrosas de privilegio permanente: cuentas de administrador compartidas, `NOPASSWD` sin restricciones, secretos de root reutilizables en la configuración del agente y shells de root. No necesitas detener toda la automatización para hacer ese cambio. Conserva primero el diagnóstico de solo lectura y después traslada una operación protegida cada vez a un helper explícito y una ruta de aprobación.

Elige una operación que ocurra con suficiente frecuencia para probar el proceso, pero que tenga una reversión acotada, como reiniciar un trabajador identificado después de un despliegue revisado. Exige que el agente envíe el host, el motivo, el efecto, la reversión y la caducidad. Haz que el aprobador rechace las solicitudes vagas. Esa fricción enseña al flujo del agente qué pruebas debe reunir antes de pedir poder.

Revisa las solicitudes denegadas con la misma seriedad que las exitosas. Un grupo de solicitudes para escribir archivos arbitrarios puede mostrar que la interfaz de despliegue carece de una operación necesaria. También puede mostrar que el agente intenta eludir un límite una y otra vez. Son problemas distintos, y un registro de auditoría permite diferenciarlos.

Después, practica la revocación. Mata una sesión mientras el agente trabaja, deniega una solicitud ya emitida después de su caducidad y verifica que un identificador de solicitud copiado no pueda autorizar una segunda acción. Los equipos suelen probar las pantallas de aprobación y saltarse esta parte. La revocación es el control que necesitas cuando el agente empieza a comportarse de otra forma a mitad de una ejecución.

No midas la madurez por el número de comandos que un agente puede ejecutar como root. Mídela por si cada elevación sigue siendo limitada, atribuible, temporal, revisable y recuperable cuando el agente o sus entradas fallan.
