Skip to main content
Glama

ssh-mcp

Централизованный MCP-шлюз, который предоставляет AI-агентам контролируемый доступ к SSH-инфраструктуре через Streamable HTTP.

ssh-mcp работает как единый HTTP-сервис. Несколько AI-клиентов — агенты, CI-пайплайны, дашборды — подключаются к одному шлюзу. SSH-учётные данные остаются на шлюзе. Политики авторизации, аудит-логирование и ограничение частоты запросов применяются централизованно до выполнения любой SSH-команды.

License: MIT Docker MCP Security M8ven Live Monitored


Оглавление


Related MCP server: MCP SSH Orchestrator

Архитектура

Локальный MCP через stdio (распространённый паттерн)

AI client
   │
   ▼
local MCP process ──► SSH target

Каждый агент запускает собственный процесс. SSH-учётные данные находятся на каждой машине. Централизованного контроля нет.

ssh-mcp (централизованный HTTP-шлюз)

AI clients ───────┐
CI agents ────────┼──► ssh-mcp ──► SSH targets
Dashboards ───────┘      │
                         ├─ API-key authentication
                         ├─ per-client authorization
                         ├─ rate limiting
                         ├─ audit logging
                         └─ connection pooling

Одно развёртывание обслуживает всех клиентов. Учётные данные, политики и логи находятся в одном месте.


Зачем нужен ssh-mcp?

  • Централизованный HTTP-шлюз — Одно развёртывание обслуживает всех AI-агентов, CI-пайплайны и дашборды через Streamable HTTP

  • Авторизация для каждого клиента — Разные API-ключи предоставляют разные наборы команд на разных серверах

  • Многоуровневые политики команд — Блокирующие паттерны, обнаружение опасных shell-конструкций и разрешающие списки для каждой цели работают вместе

  • Централизованный SSH-доступ — SSH-учётные данные находятся на шлюзе, а не на машине каждого агента

  • Журнал аудита — Каждая команда, каждый клиент, каждый результат — структурированные JSONL-логи с трассировкой запросов

  • Операционная устойчивость — Пул соединений, автоматические выключатели и повторные попытки с экспоненциальной задержкой

  • Наблюдаемость — Метрики Prometheus и эндпоинты здоровья для мониторинга


Контроль доступа для нескольких агентов

Разным агентам нужны разные разрешения. ssh-mcp обеспечивает это на шлюзе:

monitoring agent  →  API key A  →  read-only commands  →  all servers
deployment agent  →  API key B  →  deploy commands      →  web servers only
database agent    →  API key C  →  db commands           →  database server only
                  ┌─ monitoring agent (read-only, all servers)
                  ├─ deployment agent (deploy commands, web only)
MCP clients ──────┼─ database agent (db commands, db server only)
                  └─ ...
                         │
                         ▼
                      ssh-mcp
                         │
                  centralized policies
                         │
              ┌──────────┼──────────┐
              ▼          ▼          ▼
             web         db      monitoring
           servers    servers     servers

Минимальная конфигурация, демонстрирующая эту настройку:

{
  "version": 1,
  "ssh_targets": {
    "web-1": { "host": "10.0.1.10", "username": "deploy" },
    "db-1":  { "host": "10.0.1.20", "username": "dbadmin" }
  },
  "allowed_commands": {
    "default": {
      "web-1": { "allow": ["uptime", "df -h", "free -m"] }
    },
    "api_keys": {
      "deploy-key": {
        "web-1": { "allow": ["systemctl restart app", "deploy *"] }
      },
      "db-key": {
        "db-1": { "allow": ["systemctl restart postgres", "pg_dump *"] }
      }
    }
  }
}

Проблема

Большинство MCP SSH-серверов запускаются как локальные stdio-процессы — по одному на клиента, без общего состояния, без централизованной авторизации и без журнала аудита. Когда нескольким AI-агентам, CI-пайплайнам или дашбордам нужен SSH-доступ, каждый из них независимо управляет своими SSH-ключами и запускает собственный MCP-процесс. Это приводит к:

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

  • Отсутствию журнала аудита — команды невидимы для команды эксплуатации

  • Расползанию SSH-ключей — ключи разбросаны по всем машинам, где работают агенты

  • Отсутствию ограничения частоты запросов — вышедший из-под контроля агент может перегрузить цель

  • Отсутствию пула соединений — каждый клиент открывает и закрывает SSH-сессии независимо

ssh-mcp решает эту проблему, разворачивая один MCP-сервер как HTTP-шлюз. Все клиенты подключаются к нему; он подключается к вашим SSH-целям. Авторизация, аутентификация, ограничение частоты запросов, пул соединений и аудит-логирование выполняются в одном месте.


Варианты использования

Управление серверами несколькими агентами

Запустите команду AI-агентов с разными уровнями доступа. Агент развёртывания может выполнять systemctl restart nginx на веб-серверах; агент мониторинга может выполнять journalctl где угодно; агент баз данных может запускать только psql на сервере БД. Каждый агент аутентифицируется собственным API-ключом; у каждого ключа свой набор разрешений.

Интеграция с CI/CD-пайплайнами

Направьте ваш CI-пайплайн на ssh-mcp вместо управления SSH-ключами на каждом раннере. Один API-ключ на пайплайн, сетевые правила для вашей CI-подсети и разрешающие списки команд гарантируют, что ваши скрипты развёртывания выполняют ровно то, что должны, — и ничего больше.

Централизованное получение логов и конфигураций

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

Дашборды состояния серверов

Создайте дашборд на базе MCP, который опрашивает uptime, free, df и ps по всему вашему парку серверов. Пул соединений переиспользует SSH-сессии, автоматический выключатель изолирует отказавшие цели, а метрики Prometheus на /metrics питают ваш существующий стек мониторинга.

Комплаенс и аудит

Каждая команда логируется в виде структурированного JSONL: кто что запускал, на каком сервере, с какого IP, было ли это разрешено и сколько времени заняло. Поле matched_via точно показывает, какой именно уровень авторизации принял решение. Изменения конфигурации логируются отдельно с состоянием до и после.


Модель безопасности

ssh-mcp применяет эшелонированную защиту (defense-in-depth) на каждом уровне. Полная модель безопасности описана в docs/SECURITY.md.

Граница безопасности: ssh-mcp добавляет уровень авторизации, аутентификации и аудита перед SSH. Он не заменяет права нижележащих SSH-учётных записей. Если команда разрешена, SSH-пользователь выполняет её с теми привилегиями, которыми обладает эта учётная запись. Сам шлюз следует защищать с помощью TLS и средств контроля сетевого доступа. Логи могут содержать вывод команд, и к ним следует относиться соответствующим образом.

Цепочка авторизации команд

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

Уровень

Что проверяется

1. Проверка цели

Известно ли имя сервера?

2. block_patterns

Соответствует ли команда заблокированному regex?

3. Опасные паттерны

Содержит ли $(), обратные кавычки или переводы строк?

4. Защита от перенаправлений

Направлены ли перенаправления shell на /dev/, /proc/, /sys/?

5. Сегментация

После удаления перенаправлений и разбиения по &&, `

, ;, |`, каждый сегмент проходит всю цепочку

6. Правила default

Правила разрешения/запрета для всех клиентов

7. Правила api_keys

Правила разрешения/запрета для каждого ключа

8. Правила networks

Правила разрешения/запрета для каждого CIDR

9. Запрет

Неявное правило по умолчанию

Аутентификация

API-ключи передаются через заголовки X-API-Key или Authorization: Bearer. Ключи хешируются с помощью PBKDF2-HMAC-SHA256 (100 000 итераций, случайная 16-байтовая соль) и проверяются сравнением за константное время. Исходные ключи никогда не хранятся.

Очистка входных данных

Команды, имена целей и строки логов очищаются перед обработкой: удаляются нулевые байты, удаляются управляющие символы, выполняется NFKC-нормализация, и всё прогоняется через защиту от ReDoS для block_patterns.

Предотвращение обхода путей

SFTP-передачи проходят 8-уровневую проверку пути, включая проверку нулевых байтов, удаление управляющих символов, нормализацию dot-сегментов, разрешение символьных ссылок и соблюдение корня песочницы.

Ограничение частоты запросов

Ограничитель частоты запросов со скользящим окном для каждого IP-адреса клиента (60 запросов / 60 секунд, /health исключён). При превышении лимита возвращается HTTP 429 с заголовком Retry-After.

Ограничение частоты запросов настраивается в разделе settings.rate_limit:

"settings": {
  "rate_limit": {
    "enabled": true,                        // set false to disable entirely
    "max_requests_per_minute": 60,          // max requests per client IP in the window
    "window_seconds": 60.0,                 // sliding-window duration
    "cleanup_interval_seconds": 300.0       // expired-entry GC interval
  }
}

Примечание: ограничитель частоты запросов создаётся один раз при запуске контейнера из начальной конфигурации и не пересоздаётся при горячей перезагрузке конфигурации. Чтобы отключить ограничение частоты запросов, необходимо установить settings.rate_limit.enabled в false в конфигурации, которая присутствует при загрузке (например, config/ssh-mcp-config.json в смонтированном томе). Это полезно для клиентов с большим объёмом запросов или тестовых наборов, отправляющих много запросов с одного IP.


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

Предварительные требования

  • Docker с Docker Compose

  • Пара SSH-ключей (или пароли для каждой цели) для серверов, к которым вы хотите подключаться

1. Создайте каталог

mkdir -p config logs
ssh-keygen -t ed25519 -f ssh_key -N ""
cp default-config.json config/ssh-mcp-config.json

2. Добавьте SSH-цель

Откройте config/ssh-mcp-config.json и добавьте одну цель:

{
  "version": 1,
  "ssh_targets": {
    "web-server": {
      "host": "192.168.1.10",
      "port": 22,
      "username": "deploy",
      "private_key": "/app/ssh_key"
    }
  },
  "block_patterns": [ "\\brm\\s+-rf\\b", "\\bdd\\s+if=" ],
  "allowed_commands": {
    "default": [
      { "targets": ["*"], "commands": ["hostname", "uptime", "free", "df", "ps", "ls", "cat"] }
    ]
  },
  "settings": {}
}

3. Запустите сервер

docker compose up -d --build

4. Проверьте, что сервер запущен

curl http://localhost:9080/health
# {"status": "ok", "connection_pool": {...}}

5. Подключите MCP-клиент

Любой MCP-клиент, поддерживающий Streamable HTTP, может подключиться. Направьте его на http://localhost:9080/mcp, передавая заголовок с API-ключом. Подробности см. в разделе Конфигурация MCP-клиента.

6. Выведите список серверов и выполните команду

curl -X POST http://localhost:9080/mcp \
  -H "Content-Type: application/json" \
  -H "X-API-Key: your-api-key" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
      "name": "ssh_list_servers",
      "arguments": {}
    }
  }'

curl -X POST http://localhost:9080/mcp \
  -H "Content-Type: application/json" \
  -H "X-API-Key: your-api-key" \
  -d '{
    "jsonrpc": "2.0",
    "id": 2,
    "method": "tools/call",
    "params": {
      "name": "ssh_execute_command",
      "arguments": {"server_name": "web-server", "command": "uptime"}
    }
  }'

Конфигурация MCP-клиента

Любой MCP-клиент, поддерживающий транспорт Streamable HTTP, может подключиться. Формат конфигурации зависит от клиента — используйте URL и заголовки из таблицы ниже.

Параметр

Значение

Транспорт

Streamable HTTP

URL

https://ssh-mcp.example.com/mcp

Аутентификация

заголовок X-API-Key или Authorization: Bearer

Универсальная конфигурация Streamable HTTP

{
  "mcpServers": {
    "ssh": {
      "url": "http://localhost:9080/mcp",
      "headers": {
        "Authorization": "Bearer <your-api-key>"
      }
    }
  }
}

Python-клиент

import requests

MCP_URL = "https://ssh-mcp.example.com/mcp"
API_KEY = "your-api-key"


def call_tool(name: str, arguments: dict) -> dict:
    response = requests.post(
        MCP_URL,
        headers={
            "Content-Type": "application/json",
            "X-API-Key": API_KEY,
        },
        json={
            "jsonrpc": "2.0",
            "id": 1,
            "method": "tools/call",
            "params": {"name": name, "arguments": arguments},
        },
    )
    response.raise_for_status()
    return response.json()


print(call_tool("ssh_list_servers", {}))
print(call_tool("ssh_execute_command", {
    "server_name": "web-server",
    "command": "uptime",
}))

Сырой JSON-RPC

Отправляйте вызовы инструментов как JSON-RPC-запросы tools/call на /mcp:

curl -X POST http://localhost:9080/mcp \
  -H "Content-Type: application/json" \
  -H "X-API-Key: your-api-key" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
      "name": "ssh_execute_command",
      "arguments": {"server_name": "web-server", "command": "uptime"}
    }
  }'

Инструменты

Все вызовы инструментов — это JSON-RPC-запросы tools/call на /mcp. Все инструменты возвращают строку (JSON или обычный текст).

Tool

Параметры

Описание

ssh_list_servers

(нет)

Перечисляет настроенные SSH-цели (host, port, username — без секретов)

ssh_list_allowed_commands

server_name (str)

Перечисляет команды, которые текущий клиент может выполнять на цели (объединение правил default + api_key + network)

ssh_execute_command

server_name (str), command (str), timeout (int, по умолчанию 30), sudo (bool, по умолчанию false)

Выполняет команду по SSH; возвращает stdout (stderr добавляется как [STDERR], код выхода — как [EXIT: n])

ssh_download_file

server_name (str), remote_path (str)

Скачивает файл по SFTP; авторизация эквивалентна cat <path>

ssh_upload_file

server_name (str), remote_path (str), content (str), permissions (str, по умолчанию "0644")

Загружает файл по SFTP; авторизация эквивалентна tee <path>

ssh_check_connection

server_name (str), timeout (int, по умолчанию 10)

Проверяет SSH-подключение, выполняя checkcommand цели; возвращает флаг успеха, вывод и код выхода

Примеры

# List available servers
call_tool("ssh_list_servers", {})
# {"web-server": {"host": "192.168.1.10", "port": 22, "username": "deploy"}}

# List what this client can run on web-server
call_tool("ssh_list_allowed_commands", {"server_name": "web-server"})
# ["cat", "df", "du", "free", "grep", "head", "hostname", ...]

# Execute a command
call_tool("ssh_execute_command", {
    "server_name": "web-server",
    "command": "uptime",
})
# " 07:12:33 up 10 days,  2:15,  1 user,  load average: 0.08, 0.03, 0.01"

# Download a file
call_tool("ssh_download_file", {
    "server_name": "web-server",
    "remote_path": "/etc/hostname",
})
# "web-server\n"

# Upload a file
call_tool("ssh_upload_file", {
    "server_name": "web-server",
    "remote_path": "/tmp/backup.sql",
    "content": "CREATE TABLE ...;\n",
    "permissions": "0640",
})
# "OK: Uploaded 19 bytes to /tmp/backup.sql"

# Check SSH connectivity
call_tool("ssh_check_connection", {"server_name": "web-server"})
# {"success": true, "output": "ping", "error": null, "exit_code": 0, "checkcommand": "echo ping"}

# Check with custom timeout
call_tool("ssh_check_connection", {"server_name": "web-server", "timeout": 5})

Примечание о sudo: Параметра sudo_password не существует. Если для sudo требуется пароль, он берётся из поля password цели в конфигурации. Флаг sudo оборачивает команду в sudo -S -p '' (пароль из конфигурации) или sudo -n (без пароля).

Ответы об ошибках

При сбое инструмент возвращает:

{
  "error": true,
  "error_type": "AuthorizationError",
  "message": "Command rejected: target 'foo' not found",
  "retryable": false,
  "request_id": "abc-123"
}

Распространённые значения error_type: AuthorizationError, PathValidationError, FileTransferError, SSHAuthenticationError, SSHTimeoutError, MCPSSHError. Флаг retryable равен true для SSHTimeoutError. Нарушения лимита запросов вместо этого возвращают HTTP 429.


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

Расположение файла конфигурации

Сервер читает <config_dir>/ssh-mcp-config.json. Значение config_dir задаётся через CLI-флаг --config или переменную окружения MCP_SSH_CONFIG_PATH (по умолчанию: /config). Если файл не существует, сервер записывает встроенный default-config.json.

Структура верхнего уровня

{
  "version": 1,
  "ssh_targets": { ... },
  "block_patterns": [ ... ],
  "allowed_commands": {
    "default": [ ... ],
    "api_keys": [ ... ],
    "networks": [ ... ]
  },
  "settings": { ... }
}

При загрузке конфигурация проверяется по схеме config.schema.json (JSON Schema Draft 2020-12). Неизвестные ключи вызывают жёсткую ошибку.

ssh_targets

Объект, ключами которого являются идентификаторы серверов. Для каждой цели требуются host, port, username и хотя бы одно из значений private_key или password.

"ssh_targets": {
  "web-server": {
    "host": "192.168.1.10",
    "port": 22,
    "username": "deploy",
    "private_key": "/app/ssh_key",
    "checkcommand": "echo ping"
  }
}

Поле

Обязателен

По умолчанию

Описание

host

Да

Имя хоста или IP-адрес

port

Нет

22

SSH-порт

username

Да

Имя пользователя SSH

private_key

*

Путь к файлу закрытого SSH-ключа в файловой системе сервера

password

*

Пароль SSH (также может быть задан через secrets.json или переменные окружения)

checkcommand

Нет

"echo ping"

Команда, выполняемая ssh_check_connection для проверки подключения

* Требуется хотя бы одно из значений: private_key или password.

private_key — это путь в файловой системе сервера (в Docker — монтируется в контейнер), а не встроенный ключ.

block_patterns

Список regex-шаблонов. Любая команда, соответствующая шаблону, запрещается независимо от других уровней разрешающего списка. Шаблоны при загрузке проверяются на наличие конструкций, вызывающих катастрофический возврат (защита от ReDoS), а во время выполнения компилируются с таймаут-защитой.

allowed_commands

Три подобъекта управляют тем, какие команды может выполнять каждый клиент:

  • default — правила для всех клиентов (если более специфичный уровень не сработает первым)

  • api_keys — правила для отдельных ключей, сопоставляемые по key_hash

  • networks — правила для отдельных CIDR-подсетей, сопоставляемые по исходному IP-адресу клиента

Каждое правило содержит список targets (идентификаторы серверов или "*" для всех) и список commands (базовые имена команд или "*" для любой команды).

"allowed_commands": {
  "default": [
    { "targets": ["*"], "commands": ["hostname", "uptime", "free", "df", "ps"] }
  ],
  "api_keys": [
    {
      "name": "ci-bot",
      "key_hash": "pbkdf2:sha256:100000$<salt>$<hash>",
      "rules": [
        { "targets": ["web-server"], "commands": ["systemctl", "journalctl"] }
      ]
    }
  ],
  "networks": [
    {
      "name": "home-lan",
      "range": "192.168.1.0/24",
      "rules": [
        { "targets": ["*"], "commands": ["*"] }
      ]
    }
  ]
}

settings

Параметр

По умолчанию

Описание

max_output_length

50000

Максимальный объём вывода команды, возвращаемый клиенту (целое число или строка размера)

command_timeout_max

120

Жёсткий предел таймаута команды (секунды)

retry_max_attempts

3

Количество повторов при временных сбоях SSH

retry_backoff_base_seconds

1.0

Базовый экспоненциальный бэкофф (секунды)

circuit_breaker_failure_threshold

5

Число сбоев до размыкания circuit breaker для цели

circuit_breaker_timeout_seconds

60.0

Таймаут восстановления разомкнутого circuit breaker (секунды)

log_level

"INFO"

Уровень журналирования: DEBUG, INFO, WARNING, ERROR

max_log_output

4096

Максимальное число символов вывода, сохраняемых в записях журнала

compress_rotated

true

Сжимать (gzip) ротированные файлы журнала

pool_max_connections_per_target

5

Максимум пулированных SSH-подключений на цель

pool_idle_timeout_seconds

300.0

Таймаут простоя подключения (секунды)

pool_cleanup_interval_seconds

60.0

Интервал очистки пула (секунды)

max_concurrent_ssh_connections

20

Глобальный предел по всем целям; при превышении возвращается HTTP 503

watcher_debounce_seconds

2.0

Минимальный интервал между перезагрузками конфигурации; 0 отключает

trusted_proxies

[]

Доверенные IP-адреса обратных прокси (IPv4/IPv6)

Настройки SFTP (settings.sftp)

Параметр

По умолчанию

Описание

sftp.sandbox_root

"/"

Корневой каталог для проверки SFTP-путей

sftp.max_path_length

4096

Максимально допустимая длина SFTP-пути (байты); 0 отключает

Секреты

Пароли SSH-целей и хеши API-ключей можно вынести из основной конфигурации в <config_dir>/secrets.json или переменные окружения MCP_SSH_SECRET_*. Приоритет:

environment variables  >  secrets.json  >  ssh-mcp-config.json

Источник секрета

Эффект

secrets.json

Переопределения password для целей и key_hash для ключей (сопоставление по имени)

MCP_SSH_SECRET_PASSWORD_<TARGET_ID>

Переопределяет ssh_targets[<TARGET_ID>].password

MCP_SSH_SECRET_API_KEY_<KEY_NAME>

Переопределяет key_hash для записи api_keys с именем <KEY_NAME>

<TARGET_ID> и <KEY_NAME> приводятся к верхнему регистру, где - заменяется на _. Значения API-ключей должны быть хеш-строками, а не исходными ключами.

Переменные окружения и CLI-флаги

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

CLI-флаг

По умолчанию

Устаревший аналог

MCP_SSH_CONFIG_PATH

--config

/config

CONFIG_DIR

MCP_SSH_SSH_KEY

--ssh-key

ssh_key

SSH_KEY_PATH

MCP_SSH_LOG_DIR

--log-dir

/logs

LOG_DIR

MAX_OUTPUT_LENGTH

--max-output

50000

CONFIG_API_ENABLED

false

CONFIG_API_TOKEN

(обязателен, когда API включён)

--fix-permissions

False

--print-default-config

CLI-флаги имеют приоритет над переменными окружения. Любой ключ settings можно переопределить во время выполнения с помощью MCP_SSH_SETTING_<KEY> (в верхнем регистре, -_).

Горячая перезагрузка

Сервер опрашивает файл конфигурации на предмет изменений (интервал — 15 с, антидребезг — 2 с). При обнаружении изменения он перезагружает конфигурацию, проверяет её и атомарно подменяет новой. Обратные вызовы при изменении конфигурации (пересборка правил авторизации, обновление пула подключений) выполняются после успешной подмены. Когда доступно, используется файловый мониторинг на основе watchdog.


Наблюдаемость

Проверка состояния

GET /health возвращает {"status": "ok"} плюс статистику пула подключений. HEALTHCHECK контейнера использует эту конечную точку.

Метрики Prometheus

GET /metrics отдаёт метрики в отдельном реестре, все с префиксом mcpssh_:

Метрика

Тип

Метки

mcpssh_requests_total

Counter

tool, status (success/error/denied)

mcpssh_ssh_connections_total

Counter

target

mcpssh_ssh_connection_duration_seconds

Histogram

target

mcpssh_auth_denials_total

Counter

reason

mcpssh_command_duration_seconds

Histogram

target

mcpssh_pool_active_connections

Gauge

target

mcpssh_pool_idle_connections

Gauge

target

mcpssh_pool_created_total

Counter

target

Структурированное журналирование

Сервер mcp-ssh поддерживает подключаемые цели журналирования, настраиваемые через settings.logging.log_targets в файле конфигурации. Каждая цель — независимый драйвер, получающий все записи журнала.

Поведение по умолчанию

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

Типы целей журналирования

Цель

Значение конфигурации

Формат

Описание

Stdout

"stdout"

Text

Записывает в stdout. Цель по умолчанию.

JSON File

"jsonfile"

JSONL

Записывает по одному JSON-объекту в строку в файл.

Text File

"file"

Text

Записывает человекочитаемый текст в файл.

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

{
  "settings": {
    "log_level": "INFO",
    "logging": {
      "log_targets": [
        { "target": "stdout" },
        { "target": "jsonfile", "filepath": "logs/ssh-mcp.log" }
      ],
      "max_log_output": 4096,
      "compress_rotated": true
    }
  }
}

Уровень логирования

  • Файл конфигурации: задайте settings.log_level, чтобы управлять уровнем по умолчанию.

  • Переменная окружения: задайте MCP_SSH_LOG_LEVEL, чтобы переопределить значение по умолчанию из файла конфигурации (например, MCP_SSH_LOG_LEVEL=DEBUG).

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

Устаревшая конфигурация

Если settings.logging отсутствует, сервер возвращается к одной цели — JSONL-файлу в каталоге журналов (по умолчанию /logs). Это обеспечивает обратную совместимость с существующими конфигурациями.

Текстовый формат

Цели stdout и текстового файла используют формат:

2025-01-15 10:30:00 INFO ssh_execute_command: Command executed on server1

Формат JSON

Цели JSON-файла записывают по одному JSON-объекту в строку:

{"timestamp": "2025-01-15T10:30:00+00:00", "event": "ssh_execute_command", "level": "INFO", "message": "Command executed on server1", "request_id": "abc-123", "log_level": "INFO", "log_format_version": 1}

Ротация файлов

Файловые цели выполняют ротацию при превышении max_file_size_mb (по умолчанию: 10 МиБ), сохраняя backup_count резервных копий (по умолчанию: 5). Ротированные файлы сжимаются gzip, когда compress_rotated имеет значение true.

События изменения конфигурации

Событие

Значение

config.load

Начальная конфигурация загружена при запуске

config.reload

Конфигурация перечитана с диска (с success, changed_keys, targets_added, targets_removed)

config.migrated

Применена миграция схемы (from_version, to_version)

config.default_created

Скопирована встроенная конфигурация по умолчанию

config.fallback

Выполнен откат к значениям по умолчанию в памяти

config.callback_error

Обратный вызов изменения конфигурации вызвал исключение


API конфигурации и веб-панель управления

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

Что вы получаете

  • Веб-панель управления — адаптивное одностраничное приложение с 5 страницами: SSH-цели, шаблоны блокировок, правила команд, настройки и резервные копии. Войдите с помощью своего API-токена и управляйте всем из браузера.

  • REST API — полный CRUD для каждого раздела конфигурации, а также проверка конфигурации, хеширование API-ключей, управление резервными копиями и встроенная проверка SSH-подключения.

  • Утилита хеширования API-ключей — преобразует API-ключи в виде открытого текста в строки PBKDF2, готовые для конфигурации. Больше не нужно угадывать формат хеша.

  • Резервное копирование и восстановление — автоматическое резервное копирование конфигурации при каждой записи; просматривайте, восстанавливайте или удаляйте резервные копии из панели управления или API.

  • Атомарные и потокобезопасные записи — все записи конфигурации проверяются, сериализуются с помощью блокировки потоков и атомарно записываются на диск.

  • Swagger UI и ReDoc — автоматически генерируемая интерактивная документация API по адресам /api/docs и /api/redoc.

Включение API конфигурации

Задайте эти переменные окружения в вашем файле compose.yaml или .env:

Переменная

По умолчанию

Описание

CONFIG_API_ENABLED

false

Установите true, чтобы включить API конфигурации

CONFIG_API_TOKEN

(обязателен при включении)

Bearer-токен для аутентификации API-запросов

services:
  mcp-ssh:
    environment:
      CONFIG_API_ENABLED: "true"
      CONFIG_API_TOKEN: "your-secret-token-here"

API-эндпоинты

Все эндпоинты смонтированы по пути /api в том же приложении Starlette ASGI, что и MCP-сервер.

Состояние и утилиты

Метод

Путь

Описание

GET

/api/health

Проверка состояния API конфигурации (аутентификация не требуется)

POST

/api/hash-key

Хеширует API-ключ в виде открытого текста в строку PBKDF2-HMAC-SHA256

GET

/api/config/schema

Возвращает JSON Schema конфигурации (аутентификация не требуется)

POST

/api/config/validate

Проверяет словарь конфигурации без записи на диск

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

Метод

Путь

Описание

GET

/api/config

Получить полную конфигурацию (секреты скрываются)

PUT

/api/config

Заменить полную конфигурацию

GET

/api/config/{section}

Получить один раздел конфигурации (settings, ssh_targets, allowed_commands, block_patterns)

PUT

/api/config/{section}

Заменить один раздел конфигурации

SSH-цели

Метод

Путь

Описание

GET

/api/config/ssh_targets/{name}

Получить конкретную SSH-цель (секреты удалены)

PUT

/api/config/ssh_targets/{name}

Создать или заменить SSH-цель

DELETE

/api/config/ssh_targets/{name}

Удалить SSH-цель

POST

/api/config/ssh_targets/{name}/check

Проверить SSH-подключение с помощью checkcommand цели

Правила команд

Метод

Путь

Описание

GET

/api/config/allowed_commands

Вывести список разрешённых правил команд (через GET /api/config/{section})

PUT

/api/config/allowed_commands

Заменить разрешённые правила команд (через PUT /api/config/{section})

Шаблоны блокировок

Метод

Путь

Описание

GET

/api/config/block_patterns

Вывести список шаблонов блокировок (через GET /api/config/{section})

PUT

/api/config/block_patterns

Заменить все шаблоны блокировок

POST

/api/config/block_patterns

Добавить шаблон блокировки

PUT

/api/config/block_patterns/{index}

Заменить один шаблон блокировки по индексу

DELETE

/api/config/block_patterns/{index}

Удалить один шаблон блокировки по индексу

Резервные копии

Метод

Путь

Описание

GET

/api/backups

Вывести список резервных копий конфигурации (сначала новые)

POST

/api/backups/{name}/restore

Восстановить конфигурацию из резервной копии

DELETE

/api/backups/{name}

Удалить файл резервной копии

Аутентификация

Все API-запросы (кроме /api/health и /api/config/schema) требуют токен Bearer в заголовке Authorization:

curl -H "Authorization: Bearer your-secret-token-here" http://localhost:9080/api/config

Веб-панель управления

Когда она включена, адаптивное одностраничное приложение доступно по адресу http://localhost:9080/ui/ — полноценный интерфейс управления, созданный с помощью Tailwind CSS. Никаких перезагрузок страниц: toast-уведомления для каждой операции и модальные диалоги для редактирования.

Страница

Возможности

SSH-цели

Просмотр, добавление, редактирование, удаление целей; встроенная проверка подключения через checkcommand; табличное представление с хостом/портом/именем пользователя

Шаблоны блокировок

Добавление, редактирование (по индексу), удаление отдельных шаблонов; просмотр полного списка шаблонов

Правила команд

Редактирование правил по умолчанию, API-ключей и сетевых правил; полноценный редактор правил со списками целей и команд

Настройки

Редактирование всех параметров сервера: SFTP-песочница, ограничение скорости, журналирование, пул соединений, автоматический выключатель и другое

Резервные копии

Просмотр, восстановление и удаление резервных копий конфигурации; метка времени и размер для каждой копии

Дополнительные возможности:

  • Вход на основе токена с управлением сессией (хранится в sessionStorage)

  • Проверка конфигурации — изменения проверяются перед записью

  • Хеширование API-ключей — хешируйте ключи в виде открытого текста прямо из панели управления

  • Адаптивный дизайн — работает на компьютерах и мобильных устройствах

  • Toast-уведомления — обратная связь об успехе/ошибке для каждой операции

Swagger / ReDoc

Интерактивная документация API автоматически генерируется FastAPI:

  • Swagger UI: http://localhost:9080/api/docs

  • ReDoc: http://localhost:9080/api/redoc


Развертывание

Docker Compose

Файл compose.yaml определяет один сервис mcp-ssh, который содержит как MCP-сервер, так и (опционально) API конфигурации и веб-панель управления. API конфигурации включается через переменную окружения CONFIG_API_ENABLED (по умолчанию: false).

mcp-ssh — MCP SSH Gateway + API конфигурации

Путь на хосте

Путь в контейнере

Режим

./config

/config

rw

./logs

/logs

rw

./ssh_key

/app/ssh_key

ro

./ssh_key.pub

/app/ssh_key.pub

ro

Порт 9080 на хосте открыт наружу (соответствует порту 8080 в контейнере). В качестве образа выполнения используется python:3.13-alpine с закреплённым по хешу дайджестом. Процесс запускается от непривилегированного пользователя mcpssh. На этапе сборки sbom формируется SBOM в формате CycloneDX.

API конфигурации и веб-панель управления (опционально)

Включите API конфигурации, установив CONFIG_API_ENABLED=true в вашем файле .env или в окружении:

# Generate an auth token
openssl rand -hex 32
CONFIG_API_ENABLED=true
CONFIG_API_TOKEN=<your-token>

Когда эта функция включена, API конфигурации монтируется по пути /api на том же HTTP-сервере, что и MCP-шлюз. Он предоставляет:

  • REST API по адресу http://localhost:9080/api/... — полный CRUD для SSH-целей, шаблонов блокировок, правил команд, резервных копий и настроек

  • Веб-панель управления (GUI) по адресу http://localhost:9080/ui/ — одностраничное приложение для визуального управления политиками (SSH-цели, шаблоны блокировок, правила команд, настройки, резервные копии)

  • Документация API по адресам http://localhost:9080/api/docs (Swagger UI) и http://localhost:9080/api/redoc (ReDoc)

Makefile

Команда

Описание

make build

Собрать образ Docker (ghcr.io/gelse/ssh-mcp:latest)

make up

docker compose up -d

make down

docker compose down

make test

Запустить модульные тесты

make config-test

Запустить модульные тесты config-api

make integrationtest

Собрать тестовый образ, запустить интеграционные тесты

make clean-test

Удалить тестовые артефакты и контейнеры

Скачивание из GHCR

Образ Docker автоматически собирается и публикуется в GitHub Container Registry:

docker pull ghcr.io/gelse/ssh-mcp:latest

Ограничения и модель угроз

Чем ssh-mcp не является

  • Не оболочка. Вы не можете получить интерактивную терминальную сессию. Всё выполнение — разовые вызовы команд.

  • Не файловый менеджер. SFTP ограничен загрузкой/скачиванием одного файла с проверкой пути и соблюдением песочницы. Никакого вывода списка каталогов, никаких рекурсивных операций.

  • Не сетевой брандмауэр. Ограничение частоты запросов применяется по IP-адресу с фиксированными значениями по умолчанию. Оно защищает от неконтролируемых клиентов, а не от целеустремлённых злоумышленников.

Модель угроз

Угроза

Смягчение

Инъекция команд через объединение в цепочку (cmd1 && cmd2)

Сегментация команд — каждый сегмент проходит полную цепочку авторизации

Перенаправление вывода оболочки в чувствительные пути (> /etc/passwd)

Механизм проверки цели перенаправления запрещает перенаправление в /dev/, /proc/, /sys/

Path traversal в SFTP

8-уровневая проверка пути: проверка нулевого байта, удаление управляющих символов, нормализация dot-сегментов, разрешение символических ссылок, соблюдение корневой директории песочницы

ReDoS через block_patterns

Статическая проверка при загрузке + защитные таймауты во время выполнения

Перебор API-ключа

PBKDF2-HMAC-SHA256 с проверкой за постоянное время; ограничение частоты запросов по IP

Инъекция в логи

Очистка символов новой строки во всех полях, контролируемых пользователем, перед записью в журнал

Секреты в конфигурации

Разделение secrets.json, переменные окружения MCP_SSH_SECRET_*, права доступа к файлам 0600

Вне области действия

  • Завершение TLS (обрабатывается вашим обратным прокси)

  • Аутентификация пользователей помимо API-ключей (нет OAuth, нет mTLS на уровне приложения)

  • Мультиплексирование SSH-сессий (нет поддержки tmux/screen)

  • Защита журнала аудита от подделки (журналы — локальные файлы; используйте собственную доставку логов для обеспечения неизменяемости)


Разработка

Структура проекта

  • server.py — фабрика приложения FastMCP + точка входа CLI

  • lib/ — 30 модулей с единой ответственностью (auth, config, SSH client, file transfer, logging и т. д.)

  • config-api/ — Configuration API + Web Dashboard (FastAPI, смонтирована по адресу /api, когда CONFIG_API_ENABLED=true)

  • tests/ — 36 файлов модульных тестов + интеграционные тесты с реальными контейнерами Docker

Технологический стек

Python 3.13, FastMCP 3.4.x, paramiko 5.0, Starlette 1.4, FastAPI 0.115+, Pydantic 2.10+, httpx 0.28+, uvicorn 0.34+

Запуск тестов

# Unit tests (fast inner loop)
source .venv/bin/activate
python -m pytest tests/test_<module>.py -x

# Full unit test suite
make test

# Integration tests (requires Docker)
make integrationtest

Добавление нового инструмента

Полный пример в AGENTS.md описывает добавление нового обработчика @mcp.tool() от начала до конца: константы, типы, реэкспорты, обработчик, тесты, коммит.

Нет инструментов линтинга/проверки типов

В проекте нет конфигурации ruff, mypy, pyright или flake8. Форматирование следует настройкам по умолчанию .editorconfig (4 пробела для Python, строки по 88 символов).


Дорожная карта

  • Графический интерфейс конфигурации для визуального управления политиками


Лицензия

Лицензия MIT — подробности см. в LICENSE.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections. Dates show when Glama detected each change.

No tool schema history has been recorded yet.

Maintenance

ActivityMaintained
ResponsivenessResponsive

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    D
    maintenance
    Enables secure remote access operations through SSH, SFTP, rsync, VPN, and tunneling with enterprise-grade policy enforcement and audit logging. Provides AI assistants with secure, policy-driven access to remote systems while maintaining comprehensive audit trails and zero-trust security.
    1
    Apache 2.0
  • A
    license
    B
    quality
    A
    maintenance
    Provides policy-driven, auditable SSH access to server fleets for AI assistants with zero-trust security controls, command whitelisting, and comprehensive audit logging to safely manage infrastructure.
    13
    27
    Apache 2.0
  • A
    license
    A
    quality
    C
    maintenance
    Enables AI assistants to securely execute remote SSH commands, perform file transfers, and monitor system status through a standardized interface. It features robust security controls including command whitelisting, blacklisting, and credential isolation to prevent unauthorized operations.
    10
    29
    MIT
  • A
    license
    Not graded
    quality
    A
    maintenance
    Enables AI assistants to securely execute SSH commands on remote servers with connection pooling, session isolation, and a web audit panel.
    3
    MIT

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/gelse/ssh-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server