Гардрейлы — это политика, песочница — физика: SRE-агент в контейнере без сети и без секретов
Продолжение серии про полуавтономного SRE-агента. Раньше tool-сервер работал под пользователем автора на его же ноутбуке, рядом с SSH-ключами в ~/.ssh и облачными кредами в ~/.oci: все проверки были «швейцаром перед незапертой дверью». Здесь автор переносит исполняющую половину агента в контейнер и показывает и конфиг, и результат атаки через prompt injection.
- Агент разрезан пополам: reasoning-loop с ключом от модели остаётся снаружи, tool-сервер уезжает в контейнер.
- Контейнер сделан скучным намеренно: read_only: true, tmpfs /scratch на 256 MB, непривилегированный пользователь, cap_drop: [ALL], no-new-privileges, лимиты памяти и pids, внутренняя сеть без выхода наружу. Docker-сокет внутрь не монтируется.
- Секретов внутри нет вообще. Токены держит egress-прокси снаружи и отдаёт именованные маршруты: чтение Prometheus, логов, истории деплоев и создание PR только в ветку remediation/*. Всё остальное — 403 с записью в лог и алертом при повторах.
- Контейнер эфемерный: свой на каждое расследование, уничтожается по закрытии инцидента или по исчерпании таймбюджета. Персистентность, ради которой атакующий и старается, требует уже побега из контейнера.
- Тест, ради которого всё делалось: в логи упавшего сервиса подложена инструкция скачать и выполнить скрипт. Санитайзер пометил её как недоверенные данные; при ослабленном санитайзере модель попыталась сделать запрос — заблокировал allowlist; при намеренно ослабленном allowlist запрос ушёл и умер как залогированный 403 на прокси.
- Честные границы: Docker — это namespaces и cgroups, а не гипервизор, поэтому для по-настоящему враждебного ввода автор взял бы gVisor или Firecracker. Прокси теперь самая ценная цель в системе, и песочница со временем «подгнивает» — конфиг сравнивается с эталоном в CI.
- Перенести tool-сервер своего агента в контейнер по compose-файлу из статьи: он помещается на один экран, и это его отдельное достоинство.
- Вынести токены в egress-прокси с белым списком маршрутов вместо переменных окружения внутри контейнера.
- Метрика: подложить в лог инструкцию на выкачивание скрипта и убедиться, что попытка заканчивается залогированным 403, а не исходящим соединением.