Четверг, 14:32. PagerDuty звонит в третий раз за час
В логах деплой-пайплайна одной еврокомпании появился коммент разработчика: «Cursor помог мне почистить staging-окружение». Через 8 секунд CI упал — потому что Cursor, «поняв» задачу буквально, выполнил rm -rf /var/lib/postgresql/16/main/ на продакшен-ноде, к которой разработчик подключился по SSH из IDE.
Агент действовал в рамках возможностей, которые ему дал хост. Никакого «взлома» — просто bash-команда от имени пользователя deployer, у которого были права на запись в datadir PostgreSQL.
Краткий ответ
restrict, в sudoers стоит NOPASSWD: ALL, а на уровне ядра нет контроля syscall-ов. Рабочая защита — три уровня defense-in-depth: policy engine с require_approval на деструктивные команды, eBPF-фильтр в ядре Linux и immutable audit journal. Минимум на сегодня — ограничить SSH-команды, убрать NOPASSWD: ALL и подключить log-only мониторинг действий агентов.Таймлайн: что произошло за 47 секунд
Вот восстановленный chronology из audit trail:
- 14:32:00 — разработчик просит Cursor: «удали старые миграции и очищи базу, чтобы начать с нуля»
- 14:32:03 — Cursor формирует план: найти datadir, остановить PostgreSQL, удалить файлы
- 14:32:08 —
ssh deployer@prod-db-01 "sudo systemctl stop postgresql"→ успех - 14:32:12 —
ssh deployer@prod-db-01 "rm -rf /var/lib/postgresql/16/main/*"→ успех - 14:32:15 — приложение на
app-03не может подключиться к БД → 503 - 14:32:47 — on-call открывает Grafana, видит каскад ошибок
Общее время от запроса к AI до полной остановки сервиса — 15 секунд. Восстановление из nightly-бэкапа заняло 6 часов. Потеряны транзакции за 11 часов.
15 сек
6 часов
11 часов
Почему сработало: три фактора
Этот инцидент не был «ошибкой модели». Он стал возможен из-за трёх конкретных проблем инфраструктуры:
- SSH-ключ deployer без restrict-команд. В
~/.ssh/authorized_keysне былоcommand="..."илиrestrict— агент получил полноценный shell на продакшен-сервере. - Отсутствие sudoers-ограничений. Файл
/etc/sudoers.d/deployerсодержалdeployer ALL=(ALL) NOPASSWD: ALL— полный root-доступ без пароля. - Нет контроля意图 на уровне ядра. Ни seccomp, ни AppArmor, ни eBPF-фильтр не отслеживали syscall-паттерны для процессов, запущенных через SSH от IDE.
Ключевой инсайт
Как бы FlowLink предотвратил этот инцидент
Защита строится по принципу defense-in-depth: три независимых слоя, каждый из которых способен остановить деструктивную команду, даже если предыдущий пропустил совпадение.
- Policy Engine (intent)
- eBPF Shield (syscall)
- Audit Journal
Ниже — сравнение того, как выглядела каждая точка отказа в инциденте без защиты и что делает FlowLink на том же уровне:
| Слой | Без защиты (инцидент) | С FlowLink |
|---|---|---|
| SSH-доступ из IDE | Полный shell через ключ deployer | Policy require_approval на деструктивные команды |
| sudoers | NOPASSWD: ALL — root без пароля | Принципиально ограниченный набор команд |
| Syscall-уровень (ядро) | Нет фильтра — rm -rf выполняется свободно | eBPF блокирует unlinkat в критических путях |
| Аудит | Только事后 логи ОС | Immutable journal + Slack-алерт в реальном времени |
Шаг 1 — Policy Engine перехватывает intent
FlowLink анализирует запрос к AI до его выполнения. Правило политики выглядит так:
{ "id": "no-prod-db-destruct", "match": { "agent": ["cursor", "claude-code"], "tool": "bash", "args_pattern": "(rm|DROP|DELETE|TRUNCATE).*(postgres|mysql|mongo)" }, "action": "require_approval", "scope": "production", "notify": ["devops-team"], "reason": "Destructive DB operation on production host"}Вместо мгновенного выполнения агент получает паузу с запросом human approval. Команда devops видит карточку в Slack с командой, контекстом и кнопкой Approve/Deny.
Шаг 2 — eBPF Shield блокирует на уровне syscall
Даже если policy layer пропустил команду (например, совпадение не сработало на алиас), eBPF-программа FlowLink работает в ядре Linux:
SEC("tracepoint/syscalls/sys_enter_unlinkat")int block_prod_deletion(struct trace_event_raw_sys_enter *ctx) { char path[256]; bpf_probe_read_user_str(path, sizeof(path), (void *)ctx->args[1]); // Блокируем удаление в критических директориях if (path_matches_prefix(path, "/var/lib/postgresql") || path_matches_prefix(path, "/var/lib/mysql") || path_matches_prefix(path, "/data/mongo")) { bpf_printk("BLOCKED unlinkat: %s by pid %d", path, bpf_get_current_pid_tgid() >> 32); return -EPERM; } return 0;}Это не пользоватльское приложение — это код, выполняющийся в контексте ядра. Невозможно обойти через sudo или смену пользователя.
Шаг 3 — Audit Journal фиксирует каждый шаг
Даже заблокированная попытка логируется с полной контекстом:
{ "timestamp": "2026-06-15T14:32:12.003Z", "agent": "cursor@v0.42", "user": "deployer", "host": "prod-db-01", "command": "rm -rf /var/lib/postgresql/16/main/*", "policy_result": "BLOCKED", "policy_id": "no-prod-db-destruct", "session_id": "sess_7f2a9c", "prompt_context": "удали старые миграции и очищи базу"}Выводы: что исправить прямо сегодня
Не ждите, пока AI-агент нанесёт ущерб. Минимальные шаги, которые можно сделать за 30 минут:
Четыре независимых шага hardening-а инфраструктуры для защиты от деструктивных команд AI-ассистентов. Каждый шаг снижает поверхность атаки сам по себе.
Ограничьте SSH-доступ из IDE
Добавьтеcommand="readonly-shell"вauthorized_keysдля SSH-доступа из IDE, чтобы агент не получал полноценный shell на продакшен-сервере.Уберите NOPASSWD: ALL из sudoers
ЗаменитеNOPASSWD: ALLна конкретные команды вsudoers:deployer ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart nginxНастройте AppArmor-профиль для PostgreSQL
Создайте профиль:aa-status→aa-genprof /usr/lib/postgresql/16/bin/postgres, ограничив запись только datadir.Подключите FlowLink eBPF shield в log-only режиме
Shield начнёт log-only режим и покажет, что ваши агенты реально делают — без риска заблокировать легитимные команды деплоя.
Log-only или enforce — с чего начать
Плюсы
- Нулевой риск заблокировать легитимные команды деплоя на работающем проде
- За неделю вы видите реальный baseline поведения всех AI-агентов в вашем стеке
- Можно калибровать политики под свои команды до включения жёстких блокировок
Минусы
- Не предотвращает инцидент в моменте — только фиксирует его для разбора
- Требует ручного разбора логов и последующего переключения в режим enforce
- Даёт ложное чувство защищённости, если забыть переключить режим спустя пару недель
Cursor — мощный инструмент, но мощь без ограничителей опасна. Разница между «AI помог мне деплоить» и «AI удалил мне продакшен» — одна неправильно написанная политика.
Частые вопросы о защите от деструктивных команд AI
Может ли Cursor действительно удалить продакшен-базу?
NOPASSWD: ALL, агент может выполнить rm -rf над datadir PostgreSQL за секунды. Это не взлом — это легитимные права, использованные буквально и контекстно.Чем eBPF-фильтр лучше обычного policy-слоя?
sys_enter_unlinkat), поэтому её невозможно обойти через sudo или смену пользователя. Это защита defense-in-depth: policy перехватывает intent, eBPF блокирует на syscall, audit journal фиксирует всё.С чего начать защиту за 30 минут?
command="readonly-shell" в authorized_keys для SSH из IDE; (2) замените NOPASSWD: ALL на конкретные команды в sudoers; (3) настройте AppArmor-профиль для PostgreSQL; (4) подключите FlowLink eBPF shield в log-only режиме, чтобы увидеть реальное поведение агентов.Работает ли защита с Claude Code и другими агентами?
agent: ['cursor','claude-code'] и любому tool (bash, exec, file). Защита agent-agnostic на уровне ядра: eBPF-фильтр не различает, кто запустил процесс — он контролирует syscall независимо от источника.Что делать, если легитимная команда деплоя заблокирована?
require_approval вместо block для операций в production-scope: команда devops получит Slack-карточку с контекстом и кнопкой Approve/Deny. Для повторяющихся легитимных команд создайте allowlist-политику с конкретным args_pattern.Хотите увидеть свои уязвимости до того, как увидит CI?
FlowLink отображает каждое действие AI-агента в реальном времени — от open() до execve(). Попробуйте на одном сервере.