Mark Emelianov

Жёсткие контроли · Агенты · Черновик

Крошечный хост-агент: без модели, без входящего порта

Статус: это письменный замысел, черновик; он ещё не построен и не используется. Внедрение планируется сначала только с фактами и только на чтение, команды в последнюю очередь.

Большинство разговоров об ИИ-агентах — о том, что они могут. Эта статья — о детали, которая намеренно задумана делать очень мало: о небольшом агенте, который жил бы на хосте. В нём не было бы модели. Он сообщал бы факты о хосте, выполнял бы только команды из фиксированного списка, получал бы указания в виде подписанных сообщений через git и не слушал бы ни одного входящего порта.

Это самый маленький пример идеи, проходящей через всю серию: когда компонент нельзя сделать надёжным, просто вежливо попросив его, сделайте его настолько маленьким, чтобы доверять почти нечему.

Для чего он нужен

Некоторыми хостами ИИ-ассистент вообще не управляет. Их всё равно нужно знать и, в узких пределах, ими управлять: что на них работает, в каком они состоянии и горстка рутинных действий. Крошечный хост-агент задуман так, чтобы стоять на таком хосте и делать именно это и ничего больше.

Он делал бы три вещи. Сообщал бы факты о хосте. Получал бы указания как подписанные сообщения. Выполнял бы команду, только если она есть в его списке разрешённых.

Робот-хост-агент слева, сервис git посередине и робот-подписывающий справа. Подписывающий отправляет подписанные сообщения в сервис git, хост забирает их и сообщает ему факты в ответ. Полоса говорит, что хост всегда обращается наружу, а внутрь ничего не попадает: входящего порта нет.
Рисунок 1: хост забирает, подписывающий отправляет, хост никогда не слушает

Почему каждый выбор конструкции

Каждый выбор нужен ради того, что он отнимает у поверхности атаки.

Без модели. С моделью можно спорить: текст, который она читает, может направлять её действия. Небольшую фиксированную программу нельзя уговорить на действие, и она достаточно мала, чтобы человек прочитал и проверил её за один присест. Убрав модель, мы убираем с хоста целый класс манипуляций.

Список разрешённых. Агент отказывался бы от любой команды, которой нет в его списке. Тогда ошибка, плохое сообщение или поддельный запрос смогут достичь только того, что список допускает. Неизвестное значит отказ.

Подписанные сообщения. Хост действовал бы только по указаниям, подпись которых он может проверить. Возможности достучаться до канала недостаточно; указывать хосту может только владелец ключа подписи.

Git как канал. Хост сам спрашивал бы у git о новых сообщениях, а не ждал вызова. Каждое указание становится коммитом, так что история — запись того, что просили и кто, а для транспорта не нужно ничего особенного.

Нет входящего порта. Поскольку хост всегда обращался бы наружу, а внутрь ничего не попадало бы, нет слушающей службы, которую можно сканировать, прощупывать или использовать по сети.

Пять строк. Каждый выбор конструкции слева — без модели, список разрешённых, подписанные сообщения, git как канал и отсутствие входящего порта — указывает на то, что он убирает из поверхности атаки справа. Полоса говорит, что каждый выбор что-то отнимает, а следующая картинка показывает, что остаётся.
Рисунок 3: что убирает каждый выбор конструкции

Одно сообщение, пять фиксированных шагов

Каждое сообщение проходило бы одни и те же шаги в одном и том же порядке: забрать его, проверить подпись, сверить команду со списком разрешённых, выполнить одну команду из списка, сообщить результат и факты о хосте. Если какой-то шаг не проходит, сообщение останавливается на нём. Без подписи, неизвестное или не из списка значит, что ничего не выполняется, и посередине нет рассуждения, на которое можно было бы надавить.

Пять блоков в ряд, у каждого маленький робот: забрать новое сообщение, проверить его подпись, сверить команду со списком разрешённых, выполнить одну команду из списка, сообщить результат и факты о хосте. Полоса говорит, что любой шаг, который не проходит, останавливает сообщение, так что без подписи, неизвестное или не из списка значит, что ничего не выполняется.
Рисунок 2: одно сообщение, пять фиксированных шагов

Чего это стоило бы

У малого и строгого есть цена.

От чего этот замысел не защищает

Назвать пределы важнее, чем перечислить сильные стороны.

Ключ подписи. Тот, у кого ключ подписи, может отправлять действительные сообщения. Список разрешённых ограничивает, о чём можно просить, но вредное действие, которое есть в списке, было бы выполнено. Как хранится ключ и как его заменять — открытый вопрос замысла.

Повтор. Настоящее подписанное сообщение можно отправить снова позже. Замысел требует способа отличать свежие сообщения от старых, например срока действия или счётчика; без него повтор возможен.

Скомпрометированный сервис git. Сервис не может подделать подписи, но может удерживать сообщения, задерживать их или показывать старые. Это влияет на доступность и свежесть, а не на подлинность.

Плохой список разрешённых. Список разрешённых — настоящая граница прав. Он безопасен настолько, насколько безопасны его записи и принимаемые ими аргументы; широкая запись — это широкое разрешение.

Скомпрометированный хост. Захваченный хост может сообщать о себе ложные факты. Ущерб ограничен собственными путями этого хоста: по этому замыслу он не добрался бы до других хостов.

Пять карточек, у каждой маленький робот: ключ подписи как открытый вопрос замысла, повтор старого подписанного сообщения, скомпрометированный сервис git, плохой список разрешённых и скомпрометированный хост, который может сообщать ложные факты, ограниченные его собственными путями. Полоса говорит, что малые списки разрешённых, свежие сообщения, аккуратные ключи и мониторинг сужают ущерб, но эти риски остаются.
Рисунок 4: от чего этот замысел не защищает

Эти риски сужались бы малыми списками разрешённых с фиксированными аргументами, свежими сообщениями, аккуратным обращением с ключами и мониторингом того, что сообщает хост. Они не устраняются.

Как это вписывается в остальную серию

Хост-агент был бы нижним слоем той же картины. Агенты выше предлагают, люди одобряют, а подписанные, записанные сообщения несут одобренное указание вниз. Внизу была бы деталь, которая следует своему списку и ведёт запись, и которой почти нечего атаковать.