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.

A
license - permissive license
Not graded
quality - not tested
A
maintenance

Maintenance

Maintainers
<1hResponse time
Release cycle
1Releases (12mo)
Commit activity

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
    22
    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

View all related MCP servers

Related MCP Connectors

  • Operate Linux, macOS and Windows from your LLM. Every action runs through an auditable allowlist.

  • Let AI operate servers without SSH. Choose actions, approve risky changes, and audit every step.

  • Agent payments, API key vaulting, and governed mandates. Agents spend within user-defined limits.

View all MCP Connectors

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