ssh-mcp-server logo # ssh-dynamic-mcp MCP-сервер для управления удалёнными серверами по SSH с **динамическими подключениями на лету** и встроенной **маскировкой секретов** в логах и ошибках.
## 📝 О проекте `ssh-dynamic-mcp` — это форк [classfang/ssh-mcp-server](https://github.com/classfang/ssh-mcp-server) (v1.9.0), расширенный поддержкой **динамического режима**: теперь сервер можно запустить **без какой-либо конфигурации** и подключаться к произвольному хосту прямо в каждом вызове инструмента, передавая `host`, `username` и пароль (или приватный ключ) в параметрах вызова. Ключевые отличия от исходной версии: - 🔌 **Динамический режим** — запуск без аргументов и подключение «на лету» к любому хосту - 🛡️ **Маскировка секретов** — пароли, фразы-пароли и ключи автоматически заменяются на `[REDACTED]` в логах и сообщениях об ошибках - 🔑 **`--password-from-env`** — чтение пароля из переменной окружения вместо аргументов командной строки - 🚫 Новые стабильные коды ошибок, в том числе `SSH_CONFIG_MISSING` при неполных параметрах подключения Остальной функционал исходной версии (статическая конфигурация, несколько серверов, прокси, shell-транспорт, команды-шаблоны, whitelist/blacklist, 2FA) сохранён и работает как раньше. ## 🛠️ Инструменты | Инструмент | Назначение | |------------|------------| | `execute-command` | Выполнить команду на удалённом сервере | | `upload` | Загрузить локальный файл на сервер | | `download` | Скачать файл с сервера | | `list-servers` | Показать список доступных SSH-подключений | ## 🚀 Быстрый старт ### Динамический режим (новое) Запустите сервер вообще **без аргументов** — и подключайтесь к любому хосту прямо при вызове инструмента. ```json { "mcpServers": { "ssh-dynamic": { "command": "node", "args": ["C:/path/to/ssh-dynamic-mcp/build/index.js"] } } } ``` Теперь в каждом вызове можно задать целевой хост и учётные данные: ```json { "tool": "execute-command", "params": { "cmdString": "df -h", "host": "192.168.1.10", "port": 22, "username": "max", "password": "пароль" } } ``` То же относится к `upload` и `download` — передавайте `host`/`username` (+ `password` или `privateKey`) прямо в параметрах вызова, и для операции создастся одноразовое эфемерное подключение, которое будет закрыто после выполнения. > **Как это устроено**: когда в вызове присутствуют `host` и `username`, сервер открывает отдельное соединение только для этой операции (динамический режим). Если их нет — используется `connectionName` для подключения к предварительно настроенному (статическому) серверу. ### Безопасность паролей Пароли в аргументах командной строки видны в списке процессов и командной истории. Чтобы этого избежать, используйте `--password-from-env`: ```json { "mcpServers": { "ssh-dynamic": { "command": "node", "args": [ "C:/path/to/ssh-dynamic-mcp/build/index.js", "--password-from-env", "SSH_MCP_PASSWORD" ] } } } ``` Пароль будет прочитан из переменной окружения `SSH_MCP_PASSWORD`, заданной в среде, где запущен сервер. Секреты (пароль, фраза-пароль приватного ключа) автоматически маскируются знаком `[REDACTED]` в любом логе и сообщении об ошибке, возвращаемом модели, — они **не** попадут в вывод сервера. ## 🔐 Статическая конфигурация (как в оригинале) Весь функционал исходной версии поддерживается. Например, парольная аутентификация: ```json { "mcpServers": { "ssh-mcp-server": { "command": "node", "args": [ "C:/path/to/ssh-dynamic-mcp/build/index.js", "--host", "192.168.1.1", "--port", "22", "--username", "root", "--password", "pwd123456" ] } } } ``` ### Несколько серверов ```json { "mcpServers": { "ssh-mcp-server": { "command": "node", "args": [ "C:/path/to/ssh-dynamic-mcp/build/index.js", "--config-file", "ssh-config.json" ] } } } ``` Файл `ssh-config.json` (массив или объект): ```json [ { "name": "dev", "host": "1.2.3.4", "port": 22, "username": "alice", "password": "secret" }, { "name": "bastion", "host": "9.9.9.9", "port": 22, "username": "ops", "password": "pwd123456", "transportMode": "shell" } ] ``` Выбор подключения при вызове — через `connectionName`: ```json { "tool": "execute-command", "params": { "cmdString": "ls -al", "connectionName": "dev" } } ``` ## ⚙️ Динамические параметры инструментов Для `execute-command`, `upload` и `download` в динамическом режиме доступны: | Параметр | Тип | Описание | |----------|-----|----------| | `host` | string | Целевой хост (IP или hostname) | | `port` | number | SSH-порт (по умолчанию `22`) | | `username` | string | SSH-пользователь | | `password` | string | Пароль | | `privateKey` | string | Путь к приватному ключу или его содержимое | | `passphrase` | string | Фраза-пароль для зашифрованного ключа | > `host` и `username` обязательны для запуска динамического подключения. Если их не передать — будет использован `connectionName` (статический сервер). ## ⏱️ Тайм-ауты и лимиты - `timeout` — тайм-аут одной команды (мс, опционально; по умолчанию 30000 мс) - `commandTimeoutMs` / `shellCommandTimeoutMs` — настраиваемые значения по умолчанию в конфигурации - `connectionTimeoutMs` — лимит установления SSH-соединения (по умолчанию 30000 мс) - `sftpTimeoutMs` — тайм-аут SFTP-операций (по умолчанию 300000 мс) - `maxOutputBytes` — ограничение общего объёма `stdout`+`stderr` одной команды (по умолчанию 10 MiB; `0` отключает) ## 🛡️ Продвинутые возможности (из оригинала) - **Прокси**: `--proxy` поддерживает SOCKS5, HTTP и HTTPS - **Whitelist / blacklist**: `--whitelist`, `--blacklist` ограничивают допустимые команды - **Shell-транспорт**: `--transport-mode shell` для 堡垒机/跳板机 и интерактивных оболочек - **Командные шаблоны**: `--command-template` с `` / `` - **SSH config**: переиспользование `~/.ssh/config` через `--host ` - **2FA / MFA**: `--try-keyboard` + переменная `SSH_MCP_2FA_CODE` для одноразовых кодов - **Ограничение путей**: `--allowed-local-paths` и `--allowed-remote-paths` ## ⚙️ Справка по командной строке ```text Опции: --config-file Загрузить конфигурации из JSON-файла --ssh-config-file SSH-конфиг (по умолчанию: ~/.ssh/config) --ssh Добавить конфигурацию (JSON или key=value) -h, --host Хост или алиас из SSH config -p, --port Порт (по умолчанию: 22) -u, --username SSH-пользователь -w, --password SSH-пароль --password-from-env Читать пароль из переменной окружения (безопаснее, чем -w) -k, --privateKey Приватный ключ -P, --passphrase Фраза-пароль ключа -a, --agent SSH agent socket -W, --whitelist Белый список команд (регэкспы через запятую) -B, --blacklist Чёрный список команд --proxy Прокси (SOCKS5 / HTTP / HTTPS) -s, --socksProxy Старая SOCKS5-прокси --allowed-local-paths Доп. локальные пути для upload/download --allowed-remote-paths Разрешённые удалённые пути (POSIX абсолютные) --transport-mode exec | shell (по умолчанию: exec) --shell-ready-timeout Тайм-аут готовности shell (по умолчанию: 10000) --command-template Шаблон обёртки команд --pty Выделять псевдо-TTY (по умолчанию: включено) --pre-connect Предподключение ко всем серверам при старте --version, -v Версия --help Справка ``` ## 🛡️ Безопасность - **Динамический режим не имеет белого списка команд** — при подключении к произвольным хостам следите за тем, какой команды вы позволяете модели выполнять. - **Редокция активна всегда**: значения паролей и фраз-паролей, переданные в инструменты, автоматически заменяются на `[REDACTED]` в логах и сообщениях об ошибках. - **`--password-from-env`** рекомендован везде, где пароль задаётся через CLI, чтобы он не мелькал в списке процессов и истории команд. - **Приватные ключи** загружаются в память процесса — запускайте сервер в защищённой среде. - **Ограничивайте команды** через `--whitelist` / черный список для статических серверов. - **Пути** `upload`/`download` ограничивайте через `--allowed-local-paths` и `--allowed-remote-paths`, чтобы модель (или отправленный в неё prompt-injection) не читала и не писала чувствительные файлы. ## 📦 Сборка и тесты ```bash npm install npm run build # компиляция TypeScript → build/ npm test # запуск тестов ``` Код: [gitea.wtnet.ru/mshcheglov/ssh-dynamic-mcp](https://gitea.wtnet.ru/mshcheglov/ssh-dynamic-mcp) Оригинал: [github.com/classfang/ssh-mcp-server](https://github.com/classfang/ssh-mcp-server) ## 📄 Лицензия ISC — см. файл [LICENSE](LICENSE).