Tutorial

Aislar agentes de código IA en un VPS: AGENTS.md trampa

Inteligencia Artificial12 min de lectura9 pasos

El 25 de octubre de 2026, BleepingComputer informó de que un `AGENTS.md` trampa, cargado automáticamente cuando se lanza un agente en la carpeta equivocada, bastó para que Codex ejecutara comandos. Para una agencia, el riesgo se mide por lo que el agente puede alcanzar: claves de despliegue, tokens, repositorios de otros clientes. Esta guía monta un VPS de desarrollo dedicado, con un usuario sin privilegios, un directorio de proyecto estrecho, ningún secreto de producción y una salida de red restringida, y después una revisión de los archivos de instrucciones antes de cada sesión.

Contenido· Lo que muestra la investigación de Forever Security1/13
  1. 01Lo que muestra la investigación de Forever Security
  2. 02Lo que el estudio establece y lo que no dice
  3. 03Lanzar el agente en la carpeta correcta y validar los comandos: ¿por qué un servidor aparte?
  4. 04Requisitos: lo necesario para montar el VPS
  5. 05Montar el VPS en nueve pasos
  6. 06Cada salvaguarda acota algo concreto
  7. 07Revisar los archivos de instrucciones antes de que un agente los siga
  8. 08El caso del paquete function-color: lo que el filtro cambia y lo que no
  9. 09Contar los rechazos y cortar los scripts de instalación
  10. 10Solución de problemas: los errores que va a encontrar
  11. 11Lo que este servidor no resuelve
  12. 12Para una agencia: un VPS por cliente
  13. 13La documentación oficial

Lo que muestra la investigación de Forever Security

El 25 de octubre de 2026, BleepingComputer informó del trabajo de Forever Security. Los investigadores encontraron, en la carpeta ~/Downloads de un cliente, un archivo AGENTS.md trampa que pedía a un agente de código instalar un paquete npm llamado «function-color». Esa carpeta era también el directorio de trabajo del agente, y los agentes cargan automáticamente los archivos de instrucciones de su directorio de trabajo. El escenario reconstruido es este: una página web deja el archivo en silencio en las descargas, el usuario lanza su agente en esa carpeta y el agente sigue las instrucciones con las herramientas y los derechos que ya tiene.

Con Codex de OpenAI, la cadena completa, con una carga inofensiva (abrir la calculadora), tuvo éxito en el 90 % de las pruebas. Con Claude Code, un CLAUDE.md trampa redirigía el tráfico de la API hacia un servidor de los investigadores, lo que exponía el token OAuth y permitía ejecutar código en cerca del 50 % de las pruebas. Los investigadores consideran que no hay nada que corregir por parte de OpenAI ni de Anthropic: el agente funciona como está previsto.

Lo que el estudio establece y lo que no dice

  • Establecido — un archivo de instrucciones colocado en el directorio de trabajo basta para orientar al agente, sin que se explote ningún fallo de software.
  • Condición — el ataque exige que el usuario lance el agente en la carpeta afectada. Según BleepingComputer, OpenAI y Anthropic recomiendan, por cierto, lanzar el agente en el directorio del proyecto.
  • Cargas inofensivas — las tasas del 90 % y de cerca del 50 % proceden de pruebas con una carga sin peligro; no miden ataques reales y no se generalizan a todas las herramientas.
  • Entrega desconocida — los investigadores no pudieron determinar cómo llegó el archivo del cliente a sus descargas: la página web sigue siendo una hipótesis.
  • Cursor CLI — citado como lanzado desde esa carpeta, pero no descrito como probado.
  • Nuestra lectura — un repositorio clonado puede contener su propio archivo de instrucciones. Es una deducción nuestra, no un resultado del estudio.

Lanzar el agente en la carpeta correcta y validar los comandos: ¿por qué un servidor aparte?

La validación humana y el sandbox de la herramienta son salvaguardas reales y conviene mantenerlas activas: el artículo fuente también lo recomienda. Aun así, dependen de una atención constante, de una herramienta bien configurada y de un lanzamiento en la carpeta correcta. El día que falla una de las tres, la pregunta pasa a ser: ¿qué puede alcanzar el agente desviado?

En un puesto de trabajo, la respuesta es larga: claves SSH, tokens de la nube, repositorios de otros clientes, un navegador con la sesión abierta. En un VPS dedicado, es corta y la elige usted. El servidor solo contiene el proyecto, un token de alcance mínimo y un usuario sin privilegios. Se destruye y se recrea sin tocar el puesto. Un VPS no vuelve seguro al agente: acota su radio de acción, y esa es la única promesa de esta guía.

Requisitos: lo necesario para montar el VPS

Los agentes de código llaman a la API de un proveedor: el cálculo del modelo no se hace en su servidor, así que el VPS puede ser modesto. Prevea 2 vCPU, 4 GB de RAM y 40 GB de disco para un proyecto Node o PHP corriente, más si compila o lanza contenedores. Hace falta acceso root, Debian o Ubuntu LTS, una IPv4 dedicada y una clave SSH para conectarse.

En cuanto a la red, solo se abre un puerto de entrada: el 22, para SSH. De salida, el usuario del agente solo alcanza el puerto 8888 del bucle local, donde escucha un proxy que retransmite hacia una lista corta de hosts. Anote por tanto antes de empezar los hosts que necesita su herramienta: el registro npm, la API del proveedor, el repositorio Git del proyecto. Los comandos siguientes están escritos para Debian y Ubuntu, con apt, ufw y nftables, y suponen una cuenta de administración con sudo, distinta del usuario del agente.

Montar el VPS en nueve pasos

  1. Crear un usuario sin privilegios

    sudo adduser --disabled-password --gecos "" agent
    id agent
    sudo -l -U agent
    El segundo comando no debe listar el grupo sudo, y el tercero debe responder que el usuario no está autorizado a ejecutar sudo. La cuenta no tiene contraseña: se entra con sudo -iu agent desde su cuenta de administración. Prevea un VPS por cliente, para que el usuario agent de un servidor solo vea los proyectos de ese cliente.

  2. Reservar un directorio por proyecto

    sudo install -d -o agent -g agent -m 700 /srv/agents/projet-a
    Lance siempre el agente desde esa raíz, nunca desde /, ~ ni una carpeta de descargas: un directorio padre amplio carga archivos de instrucciones que no ha revisado. Para obtener el código, entre en la sesión del agente y clone:
    sudo -iu agent
    git clone <url-du-dépôt> /srv/agents/projet-a
    Use una clave de despliegue de solo lectura, generada en esta VM y propia del repositorio del proyecto.

  3. Cortar el acceso a sus claves

    sudo -iu agent printenv SSH_AUTH_SOCK
    echo 'AllowAgentForwarding no' | sudo tee /etc/ssh/sshd_config.d/10-agents.conf
    sudo sshd -t && sudo systemctl reload ssh
    El primer comando no debe mostrar nada: ningún agente SSH se transmite a la sesión del agente de código. Conéctese a la VM con ssh -o ForwardAgent=no. No deposite ninguna clave de despliegue de producción en este servidor, ni ningún archivo de configuración de otro cliente.

  4. Limitar las variables de entorno al proyecto

    sudo -iu agent
    install -m 600 /dev/null ~/.agent-env
    Edite después ~/.agent-env para dejar solo el token del proveedor, de alcance mínimo, dedicado a esta VM y revocable. Lance el agente con un entorno vaciado, para que no herede ninguna variable de su sesión:
    env -i HOME="$HOME" PATH="$PATH" TERM="$TERM" HTTPS_PROXY=http://127.0.0.1:8888 bash -c 'set -a; . ~/.agent-env; cd /srv/agents/projet-a && exec claude'
    Sustituya claude por el comando de su herramienta. Si la herramienta se conecta con una cuenta (OAuth) en lugar de una clave de API, el token vive en el directorio personal del usuario: es el token que el estudio vio expuesto con Claude Code. Prefiera una clave de API dedicada, que revoca al destruir el servidor.

  5. Cerrar la entrada con ufw

    sudo ufw default deny incoming
    sudo ufw limit 22/tcp
    sudo ufw enable
    Mantenga abierta una segunda sesión SSH durante la activación. ufw filtra por dirección y puerto, no por nombre de host ni por usuario: por eso la salida se configura en los dos pasos siguientes, con un proxy y nftables.

  6. Instalar un proxy de salida con lista blanca

    sudo apt install tinyproxy
    En /etc/tinyproxy/tinyproxy.conf, configure Listen 127.0.0.1, Port 8888, FilterDefaultDeny Yes, Filter "/etc/tinyproxy/allowlist" y escriba después la lista:
    printf '%s\n' '^registry\.npmjs\.org$' '^api\.anthropic\.com$' | sudo tee /etc/tinyproxy/allowlist
    sudo systemctl restart tinyproxy
    Cada línea es una expresión regular sobre el nombre de host. Sustituya la API de ejemplo por la de su proveedor, tomada de su documentación, y añada ^github\.com$ solo si el proyecto lo exige. La documentación de tinyproxy describe un filtrado por dominio, pero no dice que se aplique al host de los túneles HTTPS (CONNECT): no está documentado, así que hay que probarlo. No dé este filtro por supuesto antes de la prueba del paso 8.

  7. Prohibir al agente cualquier otra salida con nftables

    Añada al final de /etc/nftables.conf, después de la línea flush ruleset:
    table inet agent_egress {
    chain output {
    type filter hook output priority 0; policy accept;
    meta skuid "agent" oifname "lo" tcp dport 8888 accept
    meta skuid "agent" limit rate 5/minute log prefix "agent-egress-drop: "
    meta skuid "agent" counter drop
    }
    }
    El usuario agent ya solo puede alcanzar el proxy local; el resto se registra y se descarta. Compruebe la sintaxis antes de activar:
    sudo nft -c -f /etc/nftables.conf
    sudo systemctl enable --now nftables

  8. Comprobar que el filtro aguanta

    sudo -u agent curl -sS --max-time 5 https://example.com
    sudo -u agent curl -sS -x http://127.0.0.1:8888 https://example.com
    sudo -u agent curl -sS -x http://127.0.0.1:8888 -o /dev/null -w '%{http_code}\n' https://registry.npmjs.org/-/ping
    El primer comando debe agotar el tiempo de espera, porque el cortafuegos descarta la conexión directa. El segundo debe fallar por un rechazo del proxy (con curl, un mensaje del tipo CONNECT tunnel failed; el código exacto figura en el registro de tinyproxy). El tercero debe responder 200. Son los resultados esperados: compruébelos en su propia VM. Si el segundo comando tiene éxito, el filtro no cubre los túneles HTTPS: pase a un proxy cuya documentación cubra CONNECT y corríjalo antes de lanzar el agente.

  9. Prever la destrucción del servidor

    Guarde estos comandos en un script versionado. Un VPS sospechoso se elimina y se recrea desde la imagen original, y usted revoca el token del proveedor. Para su propio acceso al servidor, pase por un bastión en lugar de una exposición directa: la guía sobre el bastion host, enlazada al pie de la página, cubre este punto.

Cada salvaguarda acota algo concreto

Desplace la tabla

SalvaguardaLo que acotaLo que deja pasar
Validación humana de los comandosCada comando ejecutadoUn comando de apariencia inocua validado por costumbre
Sandbox de la herramientaLas acciones del agente, según su configuraciónLo que depende de un ajuste olvidado o propio de la herramienta
Usuario sin privilegiosDerechos de root, archivos del sistema, otras cuentasLos archivos del propio usuario, incluido su token
Directorio de proyecto estrechoLos archivos de instrucciones cargados por errorEl contenido del propio proyecto
Salida de red filtradaUna instalación desde un host no previsto, un envío a un host desconocido (una vez verificado el filtro)Todo lo que pasa por un host autorizado, registro o API
Ninguna clave de producción en la VMEl acceso a los servidores de producción y a otros clientesLo que exponen el token del proveedor y el repositorio del proyecto
VPS dedicado y recreableLa persistencia tras una intrusión, el puesto de trabajoLos datos del cliente presentes en la VM

Revisar los archivos de instrucciones antes de que un agente los siga

Un archivo de instrucciones actúa como código, a través del agente: revíselo como tal. Para hacer el inventario, ejecute find /srv/agents -type f \( -name AGENTS.md -o -name CLAUDE.md \) -not -path '*/node_modules/*' . Para detectar un cambio, conserve una huella:
find /srv/agents -type f \( -name AGENTS.md -o -name CLAUDE.md \) -not -path '*/node_modules/*' -exec sha256sum {} + | sort -k2 > ~/instructions.sha256
y compare antes de cada sesión con find /srv/agents -type f \( -name AGENTS.md -o -name CLAUDE.md \) -not -path '*/node_modules/*' -exec sha256sum {} + | sort -k2 | diff ~/instructions.sha256 - . En un repositorio, git log -p -- AGENTS.md CLAUDE.md muestra lo que ha cambiado desde su última revisión.

No todas las herramientas buscan sus archivos en el mismo sitio (directorio actual, carpetas padre, carpeta personal): lea la documentación de la suya y mantenga esas ubicaciones vacías, o conocidas. No lance nunca un agente en ~/Downloads ni en una carpeta padre amplia: es la primera mitigación del estudio.

El caso del paquete function-color: lo que el filtro cambia y lo que no

En el caso documentado, el archivo trampa pedía instalar el paquete npm «function-color». El informe no precisa desde qué registro. Nuestra lectura, que es una mitigación y no un resultado de la investigación: una salida restringida a una lista de hosts debería bloquear o señalar, una vez verificado el filtro en el paso 8, la instalación desde un registro o un servidor no previsto, y el registro del proxy lo muestra.

No bloquea un paquete publicado en el registro autorizado, ya que registry.npmjs.org está en la lista. Frente a ese caso, cuente con la revisión del archivo de instrucciones, con una lista de paquetes aprobados y, sobre todo, con la ausencia de secretos que robar. La guía sobre la cadena de suministro de npm, enlazada más abajo, detalla esa parte.

Contar los rechazos y cortar los scripts de instalación

Lea cada día los rechazos: sudo journalctl -k --since today | grep agent-egress-drop
sudo grep -i filtered /var/log/tinyproxy/tinyproxy.log
Un rechazo repetido hacia un host desconocido es una señal que hay que entender antes de añadir el host a la lista: anote el nombre, busque por qué lo llama la herramienta y decida después. En npm, npm config set ignore-scripts true (ejecutado como agent) impide la ejecución automática de los scripts de instalación de las dependencias.

Solución de problemas: los errores que va a encontrar

Los mensajes siguientes son los que producen los componentes de este montaje. Cada uno tiene un remedio que nunca pasa por debilitar el filtro.

  • Un rechazo de sudo para agent — comportamiento esperado: el usuario no tiene ningún privilegio, y el texto exacto del rechazo depende de la versión de sudo. Haga la operación con su cuenta de administración, sin añadir nunca agent al grupo sudo.
  • Un fallo del túnel CONNECT (con curl, un mensaje del tipo CONNECT tunnel failed) — el proxy probablemente rechazó el host; el código exacto depende de tinyproxy y figura en su registro. Lea su nombre en /var/log/tinyproxy/tinyproxy.log y añádalo a la lista solo si la herramienta lo necesita.
  • Según la herramienta, un tiempo de espera agotado o un fallo de red (por ejemplo ETIMEDOUT o fetch failed) — la herramienta intenta una conexión directa que nftables descarta. Compruebe que HTTPS_PROXY está exportada en la sesión del agente. Algunos clientes ignoran esta variable: fallan entonces de forma cerrada, y su documentación indica si aceptan un proxy.
  • Permission denied al leer la carpeta de otro usuario — es el aislamiento funcionando. Si el agente necesita un archivo, cópielo en el directorio del proyecto.
  • Conexión SSH cortada tras ufw enable — faltaba la regla del puerto 22, o SSH escucha en otro. Desde la consola del VPS, ejecute sudo ufw status y después sudo ufw allow <puerto>/tcp.

Lo que este servidor no resuelve

El VPS acota el radio de acción, no elimina el riesgo. El token del proveedor está en la máquina: un agente desviado puede usarlo mientras no lo haya revocado. El código del cliente también está allí, y una fuga sigue siendo posible por un host autorizado, puesto que un registro o un repositorio Git también acepta datos de entrada. Los investigadores recomiendan mantener las solicitudes de aprobación y el sandbox de la herramienta: este montaje se suma a ellos, no los sustituye. Por último, un servidor de desarrollo que sirve tráfico público necesita su propio endurecimiento, tratado en la lista de verificación enlazada más abajo.

Para una agencia: un VPS por cliente

El aislamiento más fácil de explicar a un cliente es físico: un VPS por cliente, con su usuario, su token y su cortafuegos. Ninguna clave de producción ni ningún otro cliente figura allí, así que un agente desviado no sale de ese perímetro. Conserve un único script de puesta en marcha para todos los servidores: una regla de salida que cambia se reaplica en todas partes. Al final de una misión, elimina el VPS, revoca el token y sigue adelante sin residuos en su puesto. Un VPS de ServOrbit.com ofrece acceso root, una IPv4 dedicada y la elección del sistema operativo: es la base de este esquema.

La documentación oficial

Informe de BleepingComputer sobre la investigación de Forever Security: https://www.bleepingcomputer.com/news/security/ai-coding-agents-hijacked-using-agentsmd-file-in-downloads-folder/

Documentación de nftables: https://wiki.nftables.org/wiki-nftables/index.php/Quick_reference-nftables_in_10_minutes

Documentación de tinyproxy: https://tinyproxy.github.io/

Página de manual de adduser (Debian): https://manpages.debian.org/stable/adduser/adduser.8.en.html

Configuración de npm: https://docs.npmjs.com/cli/using-npm/config

Un VPS por cliente, aislado y recreable

Una agencia que aísla sus agentes de código da a cada cliente su propio VPS: acceso root, IPv4 dedicada, elección del sistema operativo. Un agente desviado queda así dentro del perímetro de un solo cliente. La opción de administración del VPS existe para quien no quiera gestionar el servidor; esta guía, en cambio, se ofrece sin soporte de ServOrbit.com para la puesta en marcha.

¿Necesita ayuda?

Consulte nuestro centro de ayuda y nuestra FAQ, o contacte con nuestro equipo: llamada, WhatsApp o correo electrónico. Soporte en francés, inglés y árabe.

Escribir por WhatsAppse abre en una pestaña nueva