Мой блог
Как дать ИИ-агенту доступ к серверу без SSH и не сойти с ума
Подробности изложены в материале первоисточника. Во время расследования сбоев я регулярно подключаю ИИ-агента, чтобы оперативно собрать метрики сервера: нагрузку процессора, распределение памяти, задействованные дисковые накопители, активные процессы и системные журналы. При разборе инцидентов модель сопоставляет эти сведения с телеметрией мониторинга, выявляя корневые причины зависаний или ночных падений. Предоставлять ей полноценный доступ по SSH с правами root в таких сценариях — решение крайне рискованное, поэтому я занялся поиском альтернативной архитектуры управления.
Мне потребовалось спроектировать систему, в которой искусственный интеллект взаимодействует с инфраструктурой через четко ограниченный набор функций, привилегии суперпользователя выдаются исключительно точечно, а каждое действие фиксируется в защищенном журнале. Учитывая мою привязанность к концепциям Kubernetes, захотелось перенести этот объектный подход и на Linux: представить операционную систему как иерархическое дерево сущностей вроде процессов, дисковых томов и сервисов, оперируя ими через стандартные команды в духе get и describe. Так родилась связка из демона mcpd и утилиты linuxctl. Ниже я подробно разобрать устройство этого решения, процесс его развертывания и неочевидные архитектурные нюансы.
Что уже есть
Прежде чем писать собственный инструмент, я проанализировал существующие решения в экосистеме Model Context Protocol (MCP), предназначенной для интеграции внешних контекстов с языковыми моделями. Linux-совместимые проекты в этой нише можно разделить на три основные категории.

Первый тип — это удаленный командный оболочечный интерфейс, завернутый в обертку MCP. По сути, это инструмент выполнения произвольных команд, дополненный белым списком. На практике проверка часто ограничивается лишь первым словом инструкции, тогда как конструкция вроде sh -c — ls; rm -rf ~ спокойно обходит фильтры. Фактически мы получаем тот же небезопасный SSH, но с иллюзорным чувством контроля.
Второй вариант — локальные read-only решения, работающие исключительно в режиме чтения через стандартный ввод-вывод (stdio) на машине разработчика. Подход безопасный, однако агент функционирует на локальном ноутбуке, в то время как целевая инфраструктура находится удаленно, и повлиять на нее невозможно — допускается лишь пассивный просмотр.
Третья категория — прямое использование SSH, минуя специализированные протоколы. Ни один из доступных вариантов не покрывал моих потребностей. Я проектирую архитектуры, где ИИ-модель, опираясь на топологию сервиса, самостоятельно агрегирует диагностические данные со всех задействованных нод. Для этого необходим сетевой интерфейс с полноценной аутентификацией, а не изолированный локальный процесс, а также строгая изоляция пользователей и выдача привилегий под каждую отдельную задачу.
Коротко про MCP
Протокол MCP базируется на открытом стандарте JSON-RPC. Подключаясь к серверу, модель запрашивает перечень доступных возможностей. Спецификация выделяет две категории функционала.
Инструменты (Tools) представляют собой функции, вызываемые самой моделью автономно при возникновении необходимости. Каждая единица снабжена уникальным наименованием, текстовым описанием и JSON-схемой параметров. В терминологии протокола это управляемые моделью сущности (model-controlled). В текущей версии mcpd реализовано 38 подобных инструментов, включая processes/top, services/manage и files/read.
Ресурсы (Resources) выступают в роли данных, привязываемых к контексту непосредственно клиентским приложением, а не языковой моделью (application-controlled). Демон mcpd предоставляет 11 статических ресурсов вроде os://release, network://routes или devices://pci, а также 6 универсальных шаблонов вроде service://{name}/status или file:///{path}.
В качестве транспорта поддерживается два варианта. При использовании stdio клиент самостоятельно инициирует сервер как дочерний процесс. В конфигурации HTTP сервер функционирует автономно, принимая входящие подключения клиента. Наш демон использует сетевую архитектуру: он развернут на удаленном сервере, а агент взаимодействует с ним по защищенному протоколу HTTPS.
Данное различие носит принципиальный характер. Весной эксперты продемонстрировали уязвимости подхода stdio: клиент исполняет команду из конфигурационного файла для инициализации сервера, причем выполнит ее независимо от того, какой именно код туда прописан. Подмена конфигурации на машине инженера приводит к выполнению стороннего кода. Подобный изъян присутствует во всех официальных SDK, а разработчики протокола отказались вносить исправления. В нашем демоне эта уязвимость полностью отсутствует: клиент ничего не запускает локально, а стандартные SDK не задействуются.
Как устроен mcpd
Архитектурно демон написан на языке Go и распространяется в виде единого статического бинарного файла. Вместо классического шелла используются специализированные инструменты. Агент не выполняет сырые команды, а обращается к функциям с четкой иерархией вроде группа/команда, например processes/top, disks/usage или logs/journal-control.
Проект построен на принципе Kernel-first. Значительную часть телеметрии демон считывает напрямую из виртуальных файловых систем /proc и /sys, а также запрашивает у systemd через шину D-Bus, избегая вызова и парсинга утилит вроде ps, df или systemctl. Внешние бинарники привлекаются лишь там, где собственная реализация уступает по надежности: smartctl, traceroute, journalctl, dmesg, last и find. При этом запуск происходит строго без использования командной оболочки и с разделенными аргументами.
Каждый отдельный вызов порождает изолированный процесс-воркер от имени конкретного системного пользователя. Главный модуль демона лишь принимает запрос, проверяет токен аутентификации и права доступа. Непосредственное выполнение делегируется короткоживущему рабочему процессу, запущенному с правами того же пользователя операционной системы, что настроен в профиле mcpd. Если пользователь alice ограничен политиками ядра Linux, агент не сможет обойти эти ограничения.
Повышение привилегий до root настраивается точечно для каждого инструмента через файл mcp-sudo.yaml. Для каждого участника прописывается, какие именно команды разрешено выполнять с флагом privileged: true и в каких границах. Инструменты, работающие с путями в файловой системе, требуют обязательного указания параметра paths, иначе конфигурация не пройдет валидацию.
Механизм безопасности дополнен TLS-шифрованием по умолчанию: при первом запуске демон автоматически генерирует самоподписанный сертификат, а клиенты устанавливают доверие по его отпечатку. Любое действие фиксируется в системном журнале с фиксацией пользователя, задействованного инструмента, очищенных от секретов аргументов и статуса выполнения. Мутирующие систему запросы маркируются меткой audit.
Мои стенды
В процессе разработки и тестирования я параллельно использовал три контура окружения. Первый — локальный кластер Kubernetes внутри Docker Desktop на macOS, служивший основной средой для написания кода. Второй — виртуальная машина с операционной системой Ubuntu 24.04, где демон функционировал как классическая служба systemd. Третий — та же самая виртуальная машина, но с запуском демона внутри изолированного Docker-контейнера на соседнем сетевом порту.
Любое изменение считалось завершенным только после успешного развертывания и проверки на всех трех стендах. Ряд неочевидных программных ошибок проявился лишь на специфических конфигурациях оборудования.
Установка
Развертывание на сервере под управлением systemd выполняется одной командой в терминале:
curl -fsSL https://raw.githubusercontent.com/nucleusv/linux-mcp-daemon/main/scripts/install.sh | sudo bash
Установочный скрипт загружает релизную сборку, верифицирует контрольную сумму SHA256, инициализирует базовые конфигурации без учетных записей, создает первого пользователя и выводит его токен вместе с отпечатком сертификата безопасности. Доступны также готовые пакеты форматов .deb и .rpm, а также официальный образ в реестре контейнеров.
Проверка работоспособности выполняется с любой машины, где установлена утилита linuxctl, поддерживающая в том числе macOS. После настройки переменных окружения с адресом сервера, токеном и TLS-отпечатком можно выполнять запросы.
Клиент проектировался с оглядкой на опыт kubectl и оперирует глаголами get, describe, create, update и delete поверх групп инструментов. Утилита не хранит жестко зашитый список команд, а динамически вычитывает актуальную схему у демона, поэтому новые серверные модули мгновенно становятся доступны на стороне клиента. Команда explain позволяет посмотреть доступные операции и их соответствие внутренним инструментам.
Для удобства работы в оболочках bash и zsh предусмотрена система автодополнения по клавише Tab. Подсказки также подгружаются непосредственно от демона, гарантируя, что пользователь видит исключительно разрешенный ему функционал.
Подключаем агента
Интеграция с поддерживаемыми средами разработки и агентами, например Claude Code, выполняется предельно просто. Достаточно указать путь к сертификату безопасности сервера и зарегистрировать MCP-сервер через соответствующую команду с передачей Bearer-токена аутентификации.
После этого модель получает полный обзор серверных инструментов и самостоятельно определяет оптимальные сценарии диагностики. Например, вызов инструмента processes/top возвращает структурированный отчет, сформированный на основе чтения виртуальной файловой системы /proc с вычислением загрузки ядер процессора по аналогии с классической утилитой top.
Обратите внимание: где «узкие» права на самом деле root
Главный раздел официальной документации посвящен не процедуре установки, а потенциальным вектором атак и рискам безопасности. Некоторые конфигурационные разрешения кажутся локальными и безобидными, но на практике эквивалентны получению полного доступа суперпользователя.
Сюда относятся разрешения инструмента files/update с путями, охватывающими директорию /etc, что позволяет модифицировать каталоги конфигурации sudo, планировщик задач cron или системные службы. Аналогично инструмент services/manage дает возможность управлять любым системным юнитом, включая службу удаленного доступа SSH и сам демон управления.
Использование ядра без жестко заданного списка разрешенных ключей через kernel/system-control позволяет переопределить параметр kernel.core_pattern для выполнения произвольного кода с правами root при аварийном завершении процессов. Чтение корневого раздела через files/read открывает доступ к файлу хэшей паролей /etc/shadow и приватным криптографическим ключам самого демона.
Отдельную угрозу представляют сетевые инструменты вроде network/curl без ограничений сетевого доступа: агент может отправить запрос на внутренние служебные адреса облачных провайдеров или внутренние эндпоинты инфраструктуры, если в системных логах или контексте окажутся соответствующие инструкции.
Грабли, на которые я наступил
В процессе проектирования и отладки мне пришлось столкнуться с несколькими показательными инженерными проблемами, потребовавшими исправления архитектуры.
Первая проблема была связана с заведением учетной записи MCP с именем root. Поскольку системный вызов исполнялся от одноименного аккаунта, такой пользователь автоматически получал привилегии суперпользователя на каждый запрос, полностью игнорируя политики ограничения. Теперь конфигурации с подобными именами блокируются на этапе загрузки, а клиентская утилита пресекает попытки их создания.
Вторая сложность касалась обработки символических ссылок. Проверка разрешенных путей производилась по строковому значению, из-за чего симлинк мог перенаправить запрос в защищенную зону вроде /var/www/x → /etc/shadow. Для устранения уязвимости файлы при ограниченных путях открываются покомпонентно с флагом O_NOFOLLOW, а демон явно возвращает отказ при обнаружении симлинков.
Третий нюанс вскрылся при разнице концепций привилегий в Docker и внутри демона. Флаг --privileged контейнера и аргумент вызова privileged: true работают совершенно по-разному. Контейнер, запущенный без изоляции пространств имен процессов, видел собственный PID 1 и выполнял команды от root внутри изолированного окружения, хотя агент полагал, что управляет хост-системой. В актуальных версиях подобные запросы завершаются понятной ошибкой.
Что дальше
В ближайших планах развития проекта — реализация инструментов безопасного чтения для Docker-контейнеров, включая журналы, статистику и состояние в виде единого снимка. Также планируется добавить агрегированную сводку системного здоровья для сокращения числа запросов со стороны агента и внедрение встроенного аудита безопасности для поиска уязвимых SUID-файлов и незащищенных конфигураций.
Проект распространяется на условиях открытой лицензии Apache 2.0. Я буду рад любым вопросам, сообщениям об ошибках и обсуждению того, как вы организуете безопасный доступ искусственного интеллекта к вашей инфраструктуре. Желаю успешных диагностик и спокойных ночей без экстренных оповещений!
