# Шлюз действий AI-агента и MITM-прокси: точки контроля

Шлюз действий AI-агента и proxy man-in-the-middle могут находиться между агентом и внешним сервисом. Это внешнее сходство часто приводит к неверным архитектурным решениям. В одной модели сервис выполняет аутентифицированное действие от имени агента и хранит учетные данные. В другой он передает или перехватывает трафик, который агент уже решил создать.

От этого различия зависит, где можно сказать «нет», что способен украсть агент, что означает одобрение и будет ли запись аудита описывать осознанную операцию или восстановленный поток пакетов. Если требование звучит как «агент никогда не должен владеть рабочими учетными данными», прокси обычно оказывается неверной основной границей. У него может оставаться своя задача, но он не исправит ситуацию, если секрет уже находится в процессе клиента.

## Шлюз выполняет именованные действия, а прокси обрабатывает соединения

**Шлюз действий AI-агента** получает запрос на внешнюю операцию, выбирает сохраненные учетные данные, выполняет операцию и возвращает результат. Агент просит выполнить работу, но не получает bearer-токен, пароль или закрытый ключ, с помощью которого можно повторить эту работу в другом месте.

Forward-прокси получает сетевое соединение от клиента и пересылает его к назначению. Запрос по-прежнему принадлежит клиенту. При обычном HTTP прокси может читать метод, URL, заголовки и тело, потому что клиент отправляет ему HTTP. Для HTTPS распространенный вариант это туннель CONNECT: прокси устанавливает TCP-соединение с целью и передает зашифрованные байты в обоих направлениях.

MITM-прокси меняет эту схему HTTPS. Он завершает TLS-соединение клиента, изучает или изменяет расшифрованное HTTP-сообщение, а затем создает отдельное TLS-соединение с исходным сервером. Клиент должен доверять центру сертификации, которым управляет прокси, потому что прокси предъявляет сертификат целевого хоста.

Эти модели отвечают на разные вопросы:

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

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

Представим, что агенту поручили создать развертывание. В архитектуре с прокси агент обычно подготавливает `POST /deployments`, выбирает тело JSON и отправляет запрос. Прокси может разрешить, отклонить, записать его или добавить заголовок `Authorization`. В архитектуре с исполнителем агент вызывает действие вроде `create_deployment` с аргументами. Шлюз определяет настроенное назначение и секрет, выполняет HTTP-вызов и возвращает разрешенные действием статус и тело ответа.

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

## TLS превращает перехват в задачу центра сертификации

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

RFC 9110 описывает CONNECT как запрос на создание туннеля к целевому хосту и порту. После создания туннеля прокси передает байты. TLS-рукопожатие происходит внутри туннеля между клиентом и исходным сервером. Прокси может записать целевой хост, порт, время, объем данных и результат соединения, но не прочитает `POST /v1/...` или API-ключ в зашифрованном заголовке.

Чтобы изучать HTTPS, перехватывающий прокси должен стать TLS-конечной точкой для клиента. TLS 1.3, описанный в RFC 8446, требует от клиента проверить цепочку сертификатов и имя хоста. Прокси может пройти эту проверку только в том случае, если клиент доверяет центру сертификации, который способен выпускать сертификаты для перехватываемых сайтов.

Это создает реальную операционную работу:

1. Установить и защитить частный центр сертификации на каждой машине или в каждой среде выполнения агента.
2. Настроить доверие к нему в языковых средах, менеджерах пакетов, инструментах командной строки, контейнерах и встроенных клиентах.
3. Обработать клиентов, которые используют закрепление публичных сертификатов или собственное хранилище сертификатов.
4. Защитить расшифрованные тела запросов и учетные данные после их получения прокси.
5. Объяснить, почему процесс, созданный для недоверия к неизвестным издателям сертификатов, теперь доверяет вашему центру перехвата.

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

Исполнителю, хранящему учетные данные, не нужно подделывать сертификат каждого назначения, чтобы увидеть операцию. Он сам выступает HTTP-клиентом для принадлежащей ему операции. Он создает обычное TLS-соединение с назначением, проверяет публичный сертификат назначения, как и положено клиенту, и добавляет сохраненные учетные данные в момент выполнения.

Это не отменяет вопросов TLS. Шлюз должен правильно проверять сертификаты и защищать хранилище секретов. Но ему не нужно распространять частный центр перехвата в процесс агента только ради получения информации об операции.

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

## Хранение учетных данных меняет ущерб от скомпрометированного агента

Полезная граница безопасности это не «агент отправил сетевой запрос через наш сервер». Важно, может ли агент получить повторно используемые полномочия.

Bearer-токены хорошо показывают проблему. API-сервер обычно принимает bearer-токен от любого процесса, который может до него добраться. Если агент получил строку, он может записать ее в файл, поместить в вывод инструмента, передать другой конечной точке или использовать после окончания сеанса одобрения. Редактирование журналов после этого не поможет. Учетные данные уже покинули предназначенную для них границу.

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

Например, правило «добавлять этот токен для `api.example.internal`» дает вызывающему процессу фактические полномочия токена на всем этом хосте. Агент не видит строку токена, но может попросить прокси выполнить разрушительные вызовы. Это может быть приемлемо для узко ограниченной сервисной учетной записи. Но это не тот же контроль, что разрешение именованного действия развертывания при отказе от других административных endpoint.

Шлюз действий может привязать учетные данные к пути действия, который их использует. Агент передает аргументы, а не материал для авторизации. До создания сетевого запроса шлюз может показать человеку идентификатор учетных данных и предполагаемую операцию. Он также может отклонить запрос, который не соответствует настроенному каналу, не пытаясь установить соединение.

Ключевое различие проходит между **нераскрытием секрета** и **ограничением полномочий**.

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

Ограничение полномочий означает, что агент не может превратить разрешенную интеграцию в универсальный аутентифицированный транспорт. Для этого нужны определения действий, обработка назначения, проверка аргументов и контроль до побочного эффекта. Обычный прокси автоматически этого не дает.

SSH делает проблему еще очевиднее. Аутентификация по публичному ключу доказывает, что клиент владеет закрытым ключом во время обмена по протоколу. Если закрытый ключ есть у агента, агент может аутентифицироваться везде, где этот ключ принят. Если у агента есть только SSH-команда, направленная через шлюз, шлюз или его помощник должен выполнить аутентификацию, не экспортируя ключ.

RFC 4253 описывает транспортный протокол SSH и связанную с ним архитектуру аутентификации. Практический вывод прост: нельзя безопасно «добавить закрытый SSH-ключ в заголовок запроса». Либо процесс владеет полномочиями подписи, либо другой процесс устанавливает для него аутентифицированное соединение.

## Авторизация должна происходить до внешнего побочного эффекта

Точка контроля полезна только тогда, когда действует до того, что вы собираетесь контролировать. Запись запроса после его принятия исходным сервером дает свидетельство, но не дает права вето.

MITM-прокси часто предлагают системы политик и одобрений, основанные на атрибутах запроса: хосте, URL, методе, заголовках, теле, идентификаторе клиента или категории назначения. Это может быть сильным контролем, когда прокси видит расшифрованный трафик и понимает протокол приложения. Но тогда появляется проблема правил. Кто-то должен решить, безопасен ли `/projects/123/members`, превращает ли JSON-тело безобновительное изменение в выдачу полномочий и не обходит ли закодированный запрос проверку строки.

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

Шлюз действий может использовать меньший словарь. Решение об авторизации может учитывать вызывающий процесс, настроенные учетные данные, запрошенное действие и переданные аргументы. Человеку не нужно изучать непрозрачную команду `curl` и угадывать, какой сохраненный секрет будет добавлен дальше.

Sallyport использует фиксированную лестницу решений вместо языка политик. Заблокированное хранилище отклоняет любое действие. Для нового процесса агента по умолчанию требуется одобрение сеанса, причем в одобрении указываются полномочия процесса на подпись кода. Для отдельных учетных данных можно также требовать одобрение при каждом использовании. Это намеренно грубые средства контроля, но они срабатывают до того, как приложение выполняет HTTP- или SSH-операцию.

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

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

## Видимость запросов и полномочия действий это разные свойства

Команды часто требуют «полной видимости», хотя на самом деле им нужны две разные вещи: запись о том, какой агент инициировал работу, и запись о каждом внешнем действии. Видимость на уровне пакетов полезна для отладки, но плохо заменяет журнал действий.

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

Шлюз действий начинается с границы операции. Он может записать запуск агента, запросивший операцию, и отдельный выполненный вызов. Эти записи отвечают на разные вопросы:

- Какой авторизованный процесс агента имел активный сеанс?
- Какое внешнее действие запросил этот сеанс?
- Какие учетные данные или канал использовал исполнитель?
- Какой результат вернулся или где вызов завершился ошибкой?

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

Sallyport строит журналы Sessions и Activity из одного зашифрованного аудиторского журнала с хеш-цепочкой. Команда `sp audit verify` проверяет эту цепочку офлайн по шифротексту и не требует ключа хранилища. Это отделяет проверку от возможности расшифровывать секреты и подходит расследователю, которому нужно подтвердить историю без доступа к учетным данным.

У конкретного процесса проверки должен быть понятный людям формат вывода. Например:

```text
$ sp audit verify
Verifying audit chain...
Entries checked: 184
Chain status: valid
```

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

## HTTP и SSH по-разному ограничивают посредничество

HTTP кажется простым благодаря заголовкам, URL и глаголам. Это впечатление может привести к тому, что любую интеграцию начинают рассматривать как задачу добавления заголовка.

Для API с HTTP bearer-токеном исполнитель может хранить запись, описывающую способ аутентификации и область его применения. Когда агент запрашивает операцию, исполнитель сам добавляет bearer-заголовок. Для схемы с пользовательским заголовком он добавляет настроенный заголовок, не возвращая его значение агенту. Basic-аутентификация подчиняется тому же правилу хранения, хотя исполнитель должен считать имя пользователя и пароль секретами, а не обычным текстом конфигурации.

Практическая основа здесь это разделение входных данных. Агент может передать такие данные запроса:

```json
{
  "method": "POST",
  "path": "/repos/acme/widget/deployments",
  "body": {
    "environment": "staging",
    "revision": "7d3c1a"
  }
}
```

Но агент не должен передавать это:

```json
{
  "authorization": "Bearer token-value-goes-here"
}
```

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

Для HTTP-посредничества все равно нужны границы. Если шлюз принимает произвольные URL, агент может направить запрос к внутренним сервисам, endpoint метаданных облака или посторонним хостам через путь с учетными данными. Если он принимает произвольные заголовки, агент может попытаться использовать контрабанду запросов или изменить смысл аутентификации. Если он принимает произвольные тела, нужно признать, что сам API становится языком политик.

У SSH другие границы. Действию может потребоваться выполнить удаленную команду, скопировать файл или опросить хост. Шлюзу нужны назначение и закрытый ключ, который он хранит у себя. Вспомогательный процесс без состояния может создать SSH-соединение и вернуть стандартный вывод, стандартную ошибку и код завершения, не помещая ключ в среду агента.

Граница команды имеет значение. Запрос вроде:

```text
host: build-host
command: git rev-parse HEAD
```

имеет более узкую поверхность для проверки, чем локальная оболочка агента с доступом к `~/.ssh`, произвольными настройками прокси и неограниченной командной строкой. Но это все равно аутентифицированная удаленная команда. Если учетные данные позволяют удаленно выполнить `rm -rf`, шлюз не сделает операцию безопасной, просто изменив транспорт. Ограничьте полномочия удаленной учетной записи и выбирайте учетные данные для тех задач, которые они действительно выполняют.

Прокси может передавать SSH через TCP-туннель, но не сможет изучить SSH-команду, просто маршрутизируя порт 22. Для чтения содержимого протокола SSH ему пришлось бы стать SSH-конечной точкой и установить другое SSH-соединение с исходным сервером, взяв на себя все связанные с этим требования к доверию к хосту, аутентификации, записи и совместимости. Название «прокси» не уменьшает объем инженерной работы.

## Прокси остается полезным, если контролирует правильный уровень

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

Используйте forward-прокси или egress-шлюз, когда нужно ограничить сети или имена хостов, доступные среде выполнения, направлять трафик по известному маршруту, контролировать исходящий доступ обычных инструментов или собирать метаданные соединений. Такие ограничения могут остановить агента от обращения к неразрешенному хосту, даже если агент ничего не знает о вашей модели действий.

Используйте MITM-прокси, когда вы владеете клиентами, можете управлять центром доверия, хотите диагностировать или контролировать поведение множества обычных HTTP-клиентов и готовы отвечать за обработку расшифрованного трафика. Этим условиям часто соответствуют среды тестирования безопасности и управляемые парки устройств.

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

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

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

## Правдоподобная архитектура с прокси ломается на передаче управления

Представим агента для программирования, работающего в среде разработки. Ему нужно опросить систему учета задач, создать развертывание и проверить хост сборки по SSH. Команда устанавливает HTTPS-прокси и задает такие переменные среды:

```text
HTTPS_PROXY=http://proxy.internal:8080
HTTP_PROXY=http://proxy.internal:8080
```

Прокси добавляет API-токен для системы учета задач. Команда считает токен защищенным, потому что агент не читает его из файла конфигурации.

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

Команда пытается исправить это списками разрешенных путей. Вскоре появляются исключения для пагинации, загрузки вложений, перенаправлений, альтернативных версий API и сценария, где запись следует за чтением. Теперь прокси несет на себе политику приложения, которая меняется при каждом изменении API сервиса.

Тем временем один инструмент не поддерживает `HTTPS_PROXY`. Другой использует частное хранилище сертификатов и не проходит TLS-перехват. Третий работает в контейнере с другим набором центров сертификации. Кто-то добавляет обход, чтобы работа продолжалась. Позже именно этот обход выберет агент, которому подменили инструкцию, или скомпрометированная зависимость.

SSH приходит последним и показывает несоответствие. Прокси не может добавить закрытый SSH-ключ в туннель. Команда временно монтирует ключ в среду агента или запускает процесс, который получает доступ к локальному SSH-агенту. В этот момент система теряет свойство, ради которого все создавалось.

Архитектура с исполнением действий меняет точку передачи управления. Агент вызывает инструмент MCP через локальное соединение stdio. Исполнитель хранит API-учетные данные или SSH-ключ, устанавливает внешнее соединение и возвращает результат. MCP передает запрос инструмента, но не дает агенту свободный доступ к секретам. Спецификация Model Context Protocol определяет границу взаимодействия с инструментом, однако за хранение учетных данных отвечает разработчик.

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

## Выбирайте архитектуру исходя из полномочий, которыми вы не готовы делиться

Начните с фразы, которую нужно сделать правдой. Если это «процесс может обращаться только к одобренным назначениям», поставьте на пути сетевой контроль. Если это «процесс никогда не должен владеть этим API-ключом или закрытым SSH-ключом», поручите другой процесс выполнить аутентифицированное действие. Если это «человек должен одобрять каждое использование этих учетных данных», убедитесь, что одобрение происходит до вызова удаленного сервиса исполнителем.

Затем проверьте утверждение на обходные пути. Может ли агент прочитать переменную среды с токеном? Имеет ли он доступ к файлу учетных данных, локальному SSH-агенту, сеансу браузера, сервису метаданных облака или неограниченной исходящей сети? Может ли он попросить общий HTTP-инструмент вызвать другой путь с теми же добавленными учетными данными? Приведет ли неудачный TLS-перехват к отключению проверки сертификатов или созданию прямого маршрута?

Короткая полезная проверка состоит из четырех вопросов:

1. Какой процесс хранит каждый секрет в памяти?
2. Какой процесс создает аутентифицированное соединение?
3. Где человек может отклонить вызов до того, как его увидит удаленный сервис?
4. Какая запись связывает конкретный запуск агента с выполненной операцией?

Если на первые два вопроса ответ «агент», никакое правило прокси не меняет основной риск. Если ответ «прокси», решите, готовы ли вы поддерживать TLS-перехват и политику приложения. Если ответ «исполнитель, хранящий учетные данные», убедитесь, что у него узкая поверхность действий и агент не может его обойти.

Sallyport создан для последнего варианта на macOS: его оболочка `sp mcp` позволяет агентам с поддержкой MCP запрашивать HTTP- и SSH-действия, пока приложение хранит секреты и выполняет эти действия. Это не MITM-прокси, и так его представлять не следует.

Первое практическое действие обычно не выглядит впечатляюще: уберите прямые учетные данные из среды агента. Пока это не сделано, одобрения и журналы трафика остаются ограждениями вокруг процесса, который по-прежнему носит ключи.
