Files

14 KiB
Raw Permalink Blame History

forge-tools-ssh

MCP-сервер, который даёт AI-ассистенту аккуратный доступ к серверам по SSH.

Go MCP Tools License

forge-tools-ssh — это небольшая отдельная программа, которая превращает обычный SSH в набор инструментов, понятных AI-ассистенту. Ассистент вызывает инструмент по имени — run, disk_usage, docker_ps, journal_read — и получает готовый результат, не выдумывая каждый раз синтаксис ssh, grep, docker и df.

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

Коротко про MCP

MCP — стандарт подключения AI-ассистентов к инструментам. Сервер — это программа, которая читает запросы ассистента через стандартные ввод/вывод и возвращает результат. Один и тот же сервер работает с любым MCP-клиентом.

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


Возможности

  • 45 инструментов — от запуска команды до разбора SIP-трафика.
  • Подключение через джамп-хост — доступ к закрытому контуру через bastion.
  • PAM-шлюзы — двухстадийная аутентификация через корпоративный шлюз.
  • Проверка синтаксиса JSON/YAML/TOML/XML/INI/ENV/Dockerfile до записи файла — на самом сервере MCP, без зависимостей на удалённой машине.
  • Политика подключений (ssh.json): именованные профили и allowlist хостов, которые оператор задаёт декларативно. Ошибка конфига — отказ, а не тихий обход.
  • Live-reload: правки ssh.json подхватываются без перезапуска.
  • Устойчивость: перезапуск связи при обрыве, таймауты, лимиты на размер и длину вывода, корректная отмена задач.

Архитектура

flowchart LR
    A["AI-ассистент<br/>(MCP-клиент)"] -- "stdio · JSON-RPC" --> B["forge-tools-ssh"]
    B -- "SSH / SFTP" --> C["Сервер"]
    B -- "SSH → bastion" --> D["Сервер в закрытом контуре"]
    B -- "SSH → PAM-шлюз" --> E["Целевой сервер"]

    style B fill:#6E56CF,color:#fff

Сервер — stdio-процесс: он не открывает портов и не слушает сеть. Общение с AI-клиентом идёт через потоки ввода/вывода, а наружу он ходит только по SSH.


Быстрый старт

Сборка

export GOPRIVATE=git.totmin.ru
go build -trimpath -o forge-tools-ssh .

Проверить, что бинарник живой:

./forge-tools-ssh --health   # → ok

Подключение к AI-клиенту

Сервер запускается клиентом как дочерний процесс. Пример конфигурации:

{
  "mcp": {
    "servers": {
      "ssh": {
        "command": "/opt/forge-tools/ssh/forge-tools-ssh",
        "env": {
          "FORGE_TENANT_CONFIG": "/etc/forge/agents/my-agent",
          "SSH_MCP_KEY_PATH": "/etc/forge/agents/my-agent/ssh/id_ed25519"
        }
      }
    }
  }
}

Дальше достаточно попросить ассистента, например: «посмотри, почему упал nginx на prod-web, и покажи последние 100 строк журнала».


Инструменты (45)

Ядро — 5

Инструмент Назначение
connect Установить SSH-соединение (по профилю или ad-hoc в рамках allowlist)
disconnect Закрыть соединение (или все сразу)
run Выполнить команду на удалённом хосте
identity Показать публичный ключ сервера для authorized_keys
info ОС, ядро, hostname, архитектура

Файлы — 6

Инструмент Назначение
read Прочитать файл (лимит 10 МБ)
write Записать файл с валидацией синтаксиса перед записью
edit Правка файла: replace / regex / insert / append / prepend / delete / replace_line
validate Проверить синтаксис файла на стороне MCP-сервера
list_dir Список каталога
sync Потоковая передача файла между двумя хостами

Мониторинг — 7

Инструмент Назначение
usage Загрузка, память, диски
ps Топ процессов по CPU или памяти
logs Хвост лог-файла (опционально с фильтром)
journal_read journalctl / syslog
dmesg_read Кольцевой буфер ядра
diagnose_system Экспресс-диагностика: load, OOM, диски, упавшие службы
list_services Список служб (systemd / OpenRC и др.)

Диск — 2

Инструмент Назначение
disk_usage Место на томе, содержащем указанный путь
disk_usage_all Все точки монтирования, разделы ≥ 80% помечены

Сеть и поиск — 4

Инструмент Назначение
net_stat Слушающие порты (ss / netstat)
search_files Поиск файлов (find)
search_text Поиск текста (grep)
package_manage Пакеты: apt / apk / dnf / yum

Docker — 8

Инструмент Назначение
docker_ps Список контейнеров
docker_logs Логи контейнера
docker_op start / stop / restart
docker_ip IP-адреса контейнера
docker_find_by_ip Найти контейнер по IP
docker_networks Сети Docker
docker_cp_from Копировать файл из контейнера на хост
docker_cp_to Копировать файл с хоста в контейнер

Базы данных — 3

Инструмент Назначение
db_query SQL/CQL/Mongo-запрос внутри контейнера (postgres, mysql, scylladb, cassandra, mongodb)
db_schema Схема: таблицы / коллекции
list_db_containers Найти контейнеры, похожие на БД

VoIP — 10

Инструмент Назначение
voip_discover_containers Найти VoIP-контейнеры по имени/образу
voip_sip_capture Захват SIP-сигналинга в PCAP (sngrep)
voip_call_flow Разбор SIP call flow из PCAP
voip_registrations REGISTER-диалоги и их исход
voip_call_stats Агрегированная статистика вызовов
voip_extract_sdp Кодеки и RTP-порты из SDP
voip_packet_check Быстрая проверка наличия SIP-пакетов
voip_network_capture Захват SIP через tcpdump
voip_rtp_capture Захват RTP для проверки медиа-потока
voip_network_diagnostics ping / traceroute / проверка TCP-портов

Конфигурация

Доступы задаются декларативно в <FORGE_TENANT_CONFIG>/ssh.json. Оператор описывает профили (что и как подключать) и allowlist хостов; ассистент работает только с этими именами. Файл перечитывается на лету при изменении.

{
  // Ключ по умолчанию, если профиль или вызов не задали свой.
  "default_key_path": "${SSH_KEY_PATH}",

  // Glob-паттерны разрешённых хостов. Наличие списка включает проверку,
  // пустой список — ad-hoc без ограничений. Держите список непустым.
  "allowed_hosts": ["10.0.*", "*.internal.example.com"],

  "profiles": [
    {
      "alias": "prod-web",
      "host": "10.0.1.10",
      "username": "deploy",
      "port": 22,
      "key": "${PROD_SSH_KEY}"
    },
    {
      "alias": "bastion",
      "host": "bastion.example.com",
      "username": "deploy"
    },
    {
      "alias": "db-via-pam",
      "host": "pam-gateway.example.com",
      "username": "operator",
      "target": "10.0.2.20",
      "target_password": "${DB_TARGET_PASSWORD}"
    }
  ]
}

Готовый шаблон — ssh.json.example.

Поле профиля Смысл
alias Имя профиля, на которое ссылается ассистент
host Хост или IP
username Пользователь SSH
port Порт (по умолчанию 22)
key Приватный ключ (иначе default_key_path, иначе системный ключ)
via Алиас jump-хоста, через который подключаться
target Хост за PAM-шлюзом (SafeInspect): шлюзу отправляется username@target
target_password Пароль целевого аккаунта для второй стадии аутентификации PAM

Переменные окружения

Переменная Назначение
FORGE_TENANT_CONFIG Каталог с ssh.json. Если не задан — режим без политики.
SSH_MCP_KEY_PATH Путь к системному приватному ключу Ed25519.

Значения в конфиге поддерживают подстановку ${VAR} из окружения — реальные адреса и ключи в репозиторий не попадают.


Безопасность

  • Проваливается закрытым (fail-closed). Если задан allowed_hosts, любой хост вне списка получает отказ. Битый ssh.json — тоже отказ.
  • Креды задаёт оператор, а не модель. Ассистент выбирает профиль по имени и не может придумать произвольный хост, ключ или адрес.
  • Валидация вместо порчи. write проверяет синтаксис перед записью, edit — после правки, и предупреждает, если файл сломан.
  • Экранирование. Все пользовательские строки, попадающие в shell-команды, проходят кавычивание и санитизацию.
  • Лимиты. Таймауты команд, ограничение размера файла и длины вывода, отдельный лимит на чтение.
  • Ключи. Каталог ключей создаётся с правами 0700, приватный ключ — 0600.
  • Осторожно: host key удалённого сервера не проверяется (InsecureIgnoreHostKey). Это осознанный компромисс для работы с динамической инфраструктурой — за целостность канала отвечает сеть.

Перед публикацией убедитесь, что реальные ключи и конфиги с секретами не попали в репозиторий (см. .gitignore).


Разработка

go build ./... && go vet ./...
go test -race ./...
gofmt -l .        # должно быть пусто

Требования: Go 1.27+ и доступ к приватному Go-модулю forge-toolkit (export GOPRIVATE=git.totmin.ru).


Лицензия

Apache-2.0 — см. LICENSE.