Skip to main content
Glama

SSH MCP Server (Secured)

npm version CI/CD License: MIT

Защищённый форк zibdie/SSH-MCP-Server с фильтрацией команд по белому/чёрному списку, поддержкой сетевых устройств и управлением массовыми подключениями для безопасного удалённого управления серверами через MCP (Model Context Protocol).

Ключевые возможности

  • Разовое выполнение: ssh_run подключается, выполняет команду и отключается за один вызов инструмента — не нужно передавать connectionId

  • Белый/чёрный список команд: контроль над тем, какие команды можно выполнять

  • Обнаружение опасных шаблонов: блокирует fork-бомбы, инъекции команд и деструктивные шаблоны

  • Поддержка сетевых устройств: Cisco, Juniper, MikroTik, FortiGate, Palo Alto, Sophos с постоянными shell-сессиями и автоматическим подавлением пейджера

  • Поддержка вложенных оболочек (Jump Shell): SSH-подключение к хосту с последующим входом во вложенный CLI (telnet к хосту, FreeSWITCH fs_cli и т. д.) — команды выполняются внутри вложенной оболочки, с упорядоченным списком запасных jump-команд

  • Управление массовыми подключениями: загрузка десятков подключений из CSV/JSON-файлов

  • Учётные данные через переменные окружения: пароли автоматически извлекаются из переменных окружения по connectionId — никаких секретов в чате

  • Выполнение на нескольких подключениях: запуск команд на всех или выбранных подключениях одновременно

  • Мониторинг работоспособности подключений: отслеживание keepalive, обнаружение мёртвых подключений, автоматическая очистка

  • Настраиваемые политики безопасности: через конфигурационный файл или переменные окружения

  • Журнал аудита: запись всех попыток выполнения заблокированных команд

Related MCP server: SSH MCP Server

Установка

Быстрая настройка (рекомендуется)

# Add to Claude CLI
claude mcp add ssh-mcp-secured npx '@marian-craciunescu/ssh-mcp-server-secured@latest'

Ручная установка

npm install -g @marian-craciunescu/ssh-mcp-server-secured
{
  "mcpServers": {
    "ssh-mcp-secured": {
      "command": "ssh-mcp-server-secured"
    }
  }
}

Использование

1. Одиночное подключение

Подключитесь к хосту с помощью ssh_connect. Достаточно указать host, username и connectionId — пароль автоматически извлекается из переменных окружения:

Connect to host 172.168.0.2 with user admin connectionId=router1

LLM вызывает ssh_connect с:

{
  "host": "172.168.0.2",
  "username": "admin",
  "deviceType": "cisco",
  "connectionId": "router1"
}

Пароль не передаётся в вызове инструмента. Сервер автоматически ищет ROUTER1_PASSWORD в переменных окружения.

Соглашение о разрешении учётных данных

connectionId преобразуется в префикс переменной окружения: в верхний регистр, неалфавитно-цифровые символы заменяются на _.

connectionId

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

Переменная окружения для enable-пароля

router1

ROUTER1_PASSWORD

ROUTER1_ENABLE_PASSWORD

my-connection

MY_CONNECTION_PASSWORD

MY_CONNECTION_ENABLE_PASSWORD

dc1.switch.3

DC1_SWITCH_3_PASSWORD

DC1_SWITCH_3_ENABLE_PASSWORD

Дополнительно, если username не указан, также извлекается <PREFIX>_USERNAME.

Задайте учётные данные в вашей MCP-конфигурации:

{
  "mcpServers": {
    "ssh-mcp-secured": {
      "command": "ssh-mcp-server-secured",
      "env": {
        "SSH_FILTER_MODE": "blacklist",
        "ROUTER1_PASSWORD": "admin123",
        "ROUTER1_ENABLE_PASSWORD": "enable123",
        "SERVER1_PASSWORD": "rootpass",
        "SERVER1_USERNAME": "root"
      }
    }
  }
}

Учётные данные хранятся в MCP-конфиге (или внедряются через CI/CD, vault и т. д.) и никогда не появляются в чате или вызовах инструментов. Если пароль явно указан в вызове инструмента, он имеет приоритет над переменной окружения.

SSH-опции для устаревших устройств

При подключении к старым устройствам, требующим нестандартных алгоритмов (аналог ssh -o), используйте параметр sshOptions:

На естественном языке:

Подключись к 10.0.0.1 порт 2222 как пользователь, connectionId old-switch, с KexAlgorithms +diffie-hellman-group-exchange-sha1 и HostKeyAlgorithms +ssh-rsa

{
  "host": "10.0.0.1",
  "port": 2222,
  "username": "admin",
  "connectionId": "old-switch",
  "sshOptions": {
    "KexAlgorithms": "+diffie-hellman-group-exchange-sha1",
    "HostKeyAlgorithms": "+ssh-rsa"
  }
}

Это эквивалентно:

ssh -p 2222 admin@10.0.0.1 -o KexAlgorithms=+diffie-hellman-group-exchange-sha1 -o HostKeyAlgorithms=+ssh-rsa

Префикс + у значения означает добавление к стандартным значениям ssh2. Без + значение полностью заменяет стандартные значения.

Опция

Эквивалент SSH2

Сценарий использования

KexAlgorithms

algorithms.kex

Устаревший обмен ключами (например, diffie-hellman-group1-sha1)

HostKeyAlgorithms

algorithms.serverHostKey

Устаревшие ключи хоста (например, ssh-rsa, ssh-dss)

Ciphers

algorithms.cipher

Устаревшие шифры (например, aes128-cbc)

MACs

algorithms.hmac

Устаревшие MAC (например, hmac-sha1)

sshOptions поддерживается в ssh_connect, ssh_connect_with_jump_command и JSON-файлах, загружаемых через ssh_load_connections.

Keyboard-interactive аутентификация включается автоматически (tryKeyboard: true). Устаревшие устройства, которые отклоняют стандартную парольную аутентификацию и требуют keyboard-interactive, будут работать без дополнительной настройки.

2. Массовые подключения из файла

Загрузите несколько подключений из CSV- или JSON-файла с помощью ssh_load_connections. Пароли извлекаются из переменных окружения по тому же соглашению connectionId:

Формат CSV (connections.csv):

host,username,port,deviceType,connectionId
172.168.0.2,admin,22,cisco,router1
10.1.2.15,noc,22,cisco,router2
192.168.1.1,root,22,linux,server1

В файле нет паролей. Сервер извлекает ROUTER1_PASSWORD, ROUTER2_PASSWORD, SERVER1_PASSWORD из переменных окружения.

ПРИМЕЧАНИЕ: CSV не может содержать объекты, поэтому SSH-опции для устаревших устройств должны задаваться через отдельные переменные окружения или в JSON-файле.

Формат JSON (connections.json):

[
  {
    "host": "172.168.0.2",
    "username": "admin",
    "deviceType": "cisco",
    "connectionId": "router1"
  },
  {
    "host": "10.1.2.15",
    "username": "noc",
    "deviceType": "cisco",
    "connectionId": "router2",
    "sshOptions": {
      "KexAlgorithms": "+diffie-hellman-group-exchange-sha1",
      "HostKeyAlgorithms": "+ssh-rsa"
    }
  }
]

Профили: Определяйте переиспользуемые профили подключений для подключения к устройствам одного типа с похожими настройками (например, все коммутаторы Cisco). Профили могут включать стандартные SSH-опции для устаревших устройств, чтобы не повторять их в каждом подключении.

Приоритет разрешения: явные аргументы > переменные окружения профиля > переменные окружения connectionId

export PROFILE_CISCO_USER=admin
export PROFILE_CISCO_PASSWORD=secret123
export PROFILE_CISCO_DEVICE_TYPE=cisco
export PROFILE_CISCO_PORT=2222
export PROFILE_CISCO_SSH_OPTIONS='{"KexAlgorithms":"+diffie-hellman-group-exchange-sha1","HostKeyAlgorithms":"+ssh-rsa"}'

НИЖЕ приведён пример того, как переменные окружения профиля разрешаются при загрузке подключений из CSV/JSON. Значение PROFILE_CISCO_SSH_OPTIONS разбирается как JSON и применяется ко всем подключениям с deviceType равным cisco.

Пример переменной окружения

Поле

Значение

PROFILE_CISCO_USER

username

admin

PROFILE_CISCO_PASSWORD

password

secret123

PROFILE_CISCO_DEVICE_TYPE

deviceType

cisco

PROFILE_CISCO_SSH_OPTIONS

sshOptions (разбирается как JSON)

{"KexAlgorithms":"+diffie-hellman-group-exchange-sha1","HostKeyAlgorithms":"+ssh-rsa"}

PROFILE_CISCO_JUMP_COMMAND

jumpCommand

telnet lh

PROFILE_CISCO_PRESET

preset

topex

PROFILE_CISCO_PORT

port

2222

PROFILE_CISCO_WHITELIST

белый список команд профиля (через запятую или JSON-массив)

show ospf neigh,show version

PROFILE_CISCO_BLACKLIST

чёрный список команд профиля (через запятую или JSON-массив)

show running config,conf t

PROFILE_CISCO_DISABLE_PAGER

переключатель пейджера профиля (true/false)

false

ssh_connect host=10.0.0.1 profile=CISCO connectionId=SWITCH1"

Фильтрация команд по профилю

В дополнение к глобальным SSH_WHITELIST / SSH_BLACKLIST, каждый профиль может нести собственный фильтр команд через PROFILE_<NAME>_WHITELIST и PROFILE_<NAME>_BLACKLIST. Они накладываются поверх глобального фильтра во время выполнения для любого подключения, открытого с этим профилем:

  • Чёрный список профиля всегда блокирует — даже команды, которые разрешил бы глобальный фильтр (например, блокировка show running config).

  • Белый список профиля повторно разрешает конкретные команды и, когда присутствует, становится авторитетным: всё, что не перечислено, блокируется (например, разрешение show ospf neigh, пока чёрный список по-прежнему блокирует остальное).

  • При прямом конфликте побеждает чёрный список.

export PROFILE_ROUTERS_BLACKLIST="show running config,conf t,configure terminal"
export PROFILE_ROUTERS_WHITELIST="show ospf neigh,show version,show ip interface brief"
ssh_connect host=10.0.0.1 profile=ROUTERS connectionId=router1
# show ospf neigh        → allowed (profile whitelist)
# show running config    → blocked (profile blacklist)

PROFILE_<NAME>_DISABLE_PAGER=false отключает подавление пейджера для подключений, использующих этот профиль, переопределяя глобальное значение по умолчанию SSH_DISABLE_PAGER.

Использование:

Load connections from /path/to/connections.csv and connect to all

Примечание: При желании вы по-прежнему можете указывать пароли напрямую в CSV/JSON — разрешение через переменные окружения срабатывает только тогда, когда поле пароля отсутствует или пусто.

3. Типы сетевых устройств

Сервер поддерживает различные типы устройств с соответствующим управлением подключением:

Тип устройства

Поведение

Сценарий использования

linux

Стандартный режим SSH exec (по умолчанию)

Linux/Unix-серверы

cisco

Постоянная оболочка, поддержка enable-режима

Маршрутизаторы и коммутаторы Cisco IOS/IOS-XE

cisco_xe

Постоянная оболочка (terminal length 0)

Cisco IOS-XE

cisco_xr

Постоянная оболочка (terminal length 0)

Cisco IOS-XR

cisco_asa

Постоянная оболочка (terminal length 0)

Межсетевые экраны Cisco ASA

cisco_nexus

Постоянная оболочка (terminal length 0)

Cisco Nexus (NX-OS)

juniper

Постоянная оболочка (set cli screen-length 0)

Устройства Juniper JunOS

mikrotik

Постоянная оболочка

MikroTik RouterOS

fortinet

Постоянная оболочка (config system console / set output standard)

Межсетевые экраны FortiGate / FortiOS

paloalto

Постоянная оболочка (set cli pager off)

Межсетевые экраны Palo Alto PAN-OS

sophos

Постоянная оболочка (пейджер обрабатывается автоматически во время выполнения)

Межсетевые экраны Sophos XG/XGS (SFOS)

network

Универсальная постоянная оболочка

Другие сетевые устройства

jump_shell

Постоянная оболочка + вложенный CLI

Используется внутренне ssh_connect_with_jump_command

Сетевые устройства используют PTY-выделенные постоянные shell-сессии вместо стандартного exec(), потому что многие сетевые операционные системы закрывают SSH-канал после каждой exec-команды.

4. Разовая команда (ssh_run)

ssh_connect + ssh_execute + ssh_disconnect — это три вызова инструмента, и в двух средних модели необходимо дословно копировать сгенерированный connectionId. ssh_run сводит это к одному вызову:

{
  "host": "10.1.2.15",
  "profile": "ROUTERS",
  "command": "show version"
}

Подключается, выполняет команду и закрывает подключение. Возвращает вывод команды — не нужно отслеживать connectionId.

При сбое соединение остается открытым, чтобы вы могли повторить другую команду. Результат — структурированный объект (возвращается и как JSON-текст, и в structuredContent):

{
  "status": "error",
  "connectionId": "10_1_2_15_2026_08_12_sessionid_a1b2c3",
  "command": "show bogus",
  "error": "Command exited with code 2",
  "exitCode": 2,
  "output": "% Invalid input detected",
  "retry": "The SSH connection is still open. Call ssh_execute with this connectionId to run a different command, then ssh_disconnect when finished."
}

Повторите попытку с помощью ssh_execute, используя этот connectionId, затем вызовите ssh_disconnect. Брошенные соединения завершаются по таймауту SSH_IDLE_TIMEOUT (по умолчанию 120 с).

Профили, белый/черный списки, фильтр хостов, журнал аудита, обработка пейджера и выгрузка больших объемов вывода работают точно так же, как с ssh_connect + ssh_execute. Команда, заблокированная фильтром, отклоняется до открытия какого-либо SSH-сеанса.

Успехом считается: команда выполнена, а ее код возврата — 0 или отсутствует. Сетевые устройства на пути постоянной оболочки не сообщают коды возврата, поэтому такие команды считаются успешными, если само выполнение не завершилось ошибкой. В Linux ненулевой код возврата считается ошибкой и оставляет соединение открытым.

Вложенный CLI с запасными вариантами (ssh_run_with_jump)

Тот же однократный вызов, но сначала входит во вложенный CLI. jumpCommands — это список, который перебирается по порядку, пока один из вариантов не достигнет вложенного приглашения:

{
  "host": "10.0.0.1",
  "username": "admin",
  "preset": "topex",
  "jumpCommands": ["telnet lh", "telnet 127.0.0.1"],
  "command": "view portsoncard *"
}

Если telnet lh не достигает приглашения, пробуется telnet 127.0.0.1. Каждая попытка — это новое соединение, поэтому полуоткрытый telnet от неудачной попытки не может повредить следующую. Если ни один из кандидатов не сработал, в ошибке перечисляется, что вернул каждый из них.

Все кандидаты используют один jumpPromptPattern (указывается напрямую или через preset). Если кандидатам нужны разные шаблоны приглашений, используйте ssh_connect_with_jump_command. PROFILE_<NAME>_JUMP_COMMAND задает единственного кандидата, если jumpCommands не указан.

5. Выполнение на нескольких подключениях

Выполните команду на конкретных подключениях с помощью ssh_execute_on_multiple:

{
  "command": "show version",
  "connectionIds": ["router1", "router2", "switch1"]
}

Или выполните на ВСЕХ подключениях:

{
  "command": "show ip interface brief",
  "connectionIds": ["*"]
}

6. Jump-оболочка (вложенный CLI через SSH)

Используйте ssh_connect_with_jump_command, когда нужно подключиться по SSH к хосту, а затем войти во вложенную интерактивную оболочку перед выполнением команд. Это покрывает такие сценарии, как:

  • Telnet к шлюзу Topex VoIP с SSH-хоста перехода

  • FreeSWITCH fs_cli на удаленном сервере

  • Любой CLI, требующий интерактивного сеанса после SSH

Как это работает:

SSH → open shell → send jump command (e.g. "telnet lh") → wait for nested prompt (e.g. "topexsw>") → ready

Все последующие команды ssh_execute для этого connectionId выполняются внутри вложенной оболочки.

Пример для шлюза Topex (с пресетом):

{
  "host": "10.0.0.1",
  "username": "admin",
  "connectionId": "topex1",
  "preset": "topex",
  "jumpCommand": "telnet lh"
}

Пресет topex автоматически подставляет jumpPromptPattern: "topexsw>\\s*$" и jumpExitCommand: "quit". Вам нужно указать только jumpCommand.

Затем выполняйте команды внутри CLI Topex:

{
  "command": "view portsoncard *",
  "connectionId": "topex1"
}

Пример FreeSWITCH (пресет заполняет все):

{
  "host": "10.0.0.5",
  "username": "root",
  "connectionId": "fs1",
  "preset": "freeswitch"
}

Пресет freeswitch автоматически подставляет jumpCommand: "fs_cli", jumpPromptPattern: "freeswitch@...>" и jumpExitCommand: "/exit". Затем:

{
  "command": "sofia status",
  "connectionId": "fs1"
}

Полностью свой вариант (без пресета):

{
  "host": "10.0.0.1",
  "username": "admin",
  "connectionId": "custom1",
  "jumpCommand": "telnet 192.168.1.100",
  "jumpPromptPattern": ">\\s*$",
  "jumpExitCommand": "quit",
  "jumpReadyTimeout": 8000
}

Встроенные пресеты:

Пресет

jumpCommand

Шаблон приглашения

Команда выхода

freeswitch

fs_cli

freeswitch@...>

/exit

topex

(указывает пользователь)

topexsw>

quit

Пресеты можно переопределять — любой явно указанный параметр имеет приоритет.

Восстановление оболочки: если оболочка отключилась, ssh_execute автоматически переоткрывает оболочку и заново входит в jump-оболочку.

Отключение: ssh_disconnect корректно отправляет команду выхода во вложенный CLI перед закрытием SSH-соединения.

7. Журналирование

Установите уровень журнала через переменную окружения:

Переменная

Значения

По умолчанию

SSH_LOG_LEVEL

DEBUG, INFO, WARN, ERROR

INFO

SSH_LOG_FILE

Путь к файлу журнала

(нет)

Формат журнала:

[2026-01-22T20:26:02.044Z] [INFO ] ✓ SSH connection established to 172.168.0.2:22
[2026-01-22T20:26:02.046Z] [DEBUG] ♥ Keepalive #1 sent to 172.168.0.2 | {"uptime":"10s"}
[2026-01-22T20:26:12.047Z] [WARN ] ⚠ CONNECTION CLOSED BY REMOTE HOST: router1

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

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

Переменная

Значения

По умолчанию

Описание

SSH_FILTER_MODE

whitelist, blacklist, disabled

blacklist

Режим фильтрации команд

SSH_ALLOW_SUDO

true, false

true

Разрешить команды sudo

SSH_LOG_BLOCKED

true, false

true

Записывать заблокированные команды в stderr

SSH_MCP_CONFIG

путь к файлу

-

Путь к файлу конфигурации JSON

SSH_WHITELIST

через запятую или JSON

-

Переопределить команды белого списка

SSH_BLACKLIST

через запятую или JSON

-

Переопределить команды черного списка

SSH_DANGEROUS_PATTERNS

массив JSON

-

Переопределить опасные regex-шаблоны

SSH_LOG_LEVEL

DEBUG, INFO, WARN, ERROR

INFO

Подробность журнала

SSH_LOG_FILE

путь

-

Журнал в файл

SSH_HOST_FILTER_MODE

whitelist, blacklist, disabled

disabled

Режим фильтрации хостов

SSH_HOST_WHITELIST

IP-адреса через запятую

-

Белый список разрешенных IP-адресов хостов

SSH_HOST_BLACKLIST

IP-адреса через запятую

-

Черный список разрешенных IP-адресов хостов

SSH_IDLE_TIMEOUT

секунды

120

Таймаут простоя соединения

SSH_FAILED_CONNECTIONS_LOG

путь к файлу

./ssh-failed-connections.json

/var/log/ssh-failed.jsonl

SSH_AUDIT_ENABLED

true, false

true

Записывать аудит сеанса по командам (команда + полный вывод) в JSONL

SSH_AUDIT_DIR

путь

./audit

Каталог для ежедневных файлов audit_ГГГГ-ММ-ДД.jsonl

SSH_ENABLE_LARGE_OUTPUT

true, false

false

Выгружать чрезмерно большой вывод команды в конечную точку загрузки и возвращать URI вместо встроенного текста

SSH_MAX_OUTPUT_LENGTH

целое число (символы)

10000

Порог размера вывода, выше которого вывод выгружается

SSH_FILE_UPLOAD_ENDPOINT

URL

-

Конечная точка POST для большого вывода. Получает {content, filename}, должна вернуть {file_id, artifact_uri}

SSH_DISABLE_PAGER

true, false

true

Подавлять интерактивные пейджеры (less/---(more)---) в оболочке и exec

SSH_DISABLE_PAGER_CMD_<ТИП_УСТРОЙСТВА>

строка

значение по умолчанию для устройства

Переопределить команду отключения пейджера для типа устройства (напр. SSH_DISABLE_PAGER_CMD_CISCO)

SSH_PAGER_REGEX

строка regex

встроенный

Переопределить шаблон определения приглашения пейджера

SSH_PAGER_ADVANCE_KEY

строка

" " (пробел)

Клавиша для перехода к следующей странице пейджера

SSH_MAX_PAGER_PAGES

целое число

1000

Предел безопасности для автоматически пролистываемых страниц на команду

Любые дополнительные переменные окружения, следующие соглашению <ИД_ПОДКЛЮЧЕНИЯ>_PASSWORD, автоматически используются для разрешения учетных данных (см. Соглашение о разрешении учетных данных).

Примеры конфигурации MCP

Белый/черный список хостов:

Режим черного списка с пользовательскими заблокированными командами:

{
  "ssh_mcp": {
    "command": "ssh-mcp-server-secured",
    "args": [],
    "env": {
      "SSH_FILTER_MODE": "blacklist",
      "SSH_ALLOW_SUDO": "true",
      "SSH_LOG_BLOCKED": "true",
      "SSH_BLACKLIST": "rm,rmdir,mkfs,fdisk,shutdown,reboot,halt,poweroff,passwd,useradd,userdel,iptables,crontab,conf t,configure terminal"
    }
  }
}

Режим белого списка (строгий — разрешать только определенные команды):

{
  "ssh_mcp": {
    "command": "ssh-mcp-server-secured",
    "args": [],
    "env": {
      "SSH_FILTER_MODE": "whitelist",
      "SSH_ALLOW_SUDO": "false",
      "SSH_LOG_BLOCKED": "true",
      "SSH_WHITELIST": "ls,cat,grep,tail,head,df,du,free,uptime,ps,systemctl,journalctl,docker,kubectl,ping,curl,dig,ss,netstat,show,display"
    }
  }
}

Сетевые операции с переменными окружения учетных данных:

{
  "ssh_mcp": {
    "command": "ssh-mcp-server-secured",
    "args": [],
    "env": {
      "SSH_FILTER_MODE": "blacklist",
      "SSH_ALLOW_SUDO": "true",
      "SSH_LOG_LEVEL": "DEBUG",
      "SSH_BLACKLIST": "conf t,configure terminal,rm,shutdown,reboot",
      "ROUTER1_PASSWORD": "admin123",
      "ROUTER1_ENABLE_PASSWORD": "enable123",
      "ROUTER2_PASSWORD": "pass123",
      "SERVER1_PASSWORD": "pass1234"
    }
  }
}

Теперь в чате достаточно сказать connect to 172.168.0.2 as admin connectionId=router1 — никаких паролей в открытом виде.

Через npx (без глобальной установки):

{
  "ssh_mcp": {
    "command": "npx",
    "args": ["@marian-craciunescu/ssh-mcp-server-secured"],
    "env": {
      "SSH_FILTER_MODE": "blacklist",
      "SSH_ALLOW_SUDO": "true"
    }
  }
}

Файл конфигурации

Создайте config.json или ssh-mcp-config.json:

{
  "commandFilter": {
    "mode": "whitelist",
    "allowSudo": false,
    "logBlocked": true,
    "whitelist": [
      "ls", "cat", "grep", "df", "ps", "systemctl", "docker", "show", "ping"
    ],
    "blacklist": [
      "rm", "shutdown", "reboot", "passwd", "conf t", "configure terminal"
    ],
    "dangerousPatterns": [
      ";\\s*rm\\s+-rf",
      "curl.*\\|\\s*bash"
    ]
  }
}

Режимы фильтрации

Режим черного списка (по умолчанию)

Команды из черного списка блокируются. Все остальное разрешено. Поддерживаются многословные записи, такие как configure terminal и conf t.

✓ ls -la
✓ docker ps
✓ show ip interface brief
✗ rm -rf /tmp/files       → Blocked: 'rm' is in blacklist
✗ configure terminal      → Blocked: 'configure terminal' is in blacklist
✗ shutdown now            → Blocked: 'shutdown' is in blacklist

Режим белого списка

Разрешены только команды из белого списка. Все остальное блокируется.

✓ ls -la                  → Allowed: 'ls' is whitelisted
✓ show version            → Allowed: 'show' is whitelisted
✗ vim /etc/hosts          → Blocked: 'vim' not in whitelist
✗ make install            → Blocked: 'make' not in whitelist

Смешанный режим

Оба списка активны одновременно, и фильтр работает по принципу запрета по умолчанию: команда должна соответствовать записи белого списка, чтобы быть выполненной. При конфликте побеждает самое длинное совпадающее вхождение, независимо от того, из какого оно списка. Это позволяет разрешить широкий префикс, вырезать из него опасное подмножество, а затем снова разрешить более узкое исключение.

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

SSH_FILTER_MODE=mixed
SSH_WHITELIST=show, show running-config interface, show running-config | include, ping -c , ls -lha, terminal length 0
SSH_BLACKLIST=show running-config, conf t, configure terminal, reload, rm, shutdown, ping

Итоговые решения:

✓ show version                            → 'show' (4) beats nothing
✓ show interfaces terse                   → 'show' (4) beats nothing
✗ show running-config                     → 'show running-config' (19) beats 'show' (4)
✓ show running-config interface Gi0/1     → 'show running-config interface' (29) beats 'show running-config' (19)
✓ show running-config | include hostname  → 'show running-config | include' (29) beats 'show running-config' (19)
✓ ping -c 4 8.8.8.8                       → 'ping -c' (7) beats 'ping' (4)
✗ ping 8.8.8.8                            → only 'ping' (4) matches, and it is blacklisted
✓ terminal length 0                       → whitelisted, so the server can disable its own pager
✓ ls -lha                                 → exact whitelist entry
✗ ls -la                                  → matches NEITHER list → blocked by deny-by-default
✗ reload                                  → blacklisted, no whitelist match
✗ rm -rf /tmp/x                           → 'rm' (2) blacklisted, no whitelist match

Две вещи, которые нужно знать:

  • ls -lha разрешена, а ls -la — нет. Записи белого списка — это буквальные префиксы, а не шаблоны. В смешанном режиме все, что вы явно не разрешили, блокируется, поэтому перечисляйте точные формы команд, которые собираетесь выполнять.

  • При точном совпадении побеждает белый список. Если одна и та же строка есть в обоих списках, команда разрешена.

Включите команды отключения пейджера (terminal length 0, set cli screen-length 0) в белый список. Сервер сам их отправляет при открытии оболочки, иначе в смешанном режиме они были бы заблокированы.

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

✗ show version | rm -rf /   → Blocked: dangerous pattern /\|\s*rm/i

Отключенный режим

Без фильтрации команд (с осторожностью).

Порядок проверки команд

  1. 检查过滤是否已禁用

  2. 检查 sudo 权限

  3. 检查危险模式(正则表达式)——始终优先,任何白名单都无法覆盖

  4. mixed 模式下:对整个命令的白名单和黑名单进行最长匹配;如果两者都不匹配,则默认拒绝。在此结束。

  5. 针对黑名单检查完整命令(支持多词)

  6. 从管道/命令链中提取基础命令

  7. 针对黑名单/白名单检查每个基础命令

  8. 在全局结果之上应用按配置文件(按连接)的白名单/黑名单

使用 ssh_get_command_filter 查看生效的规则,并询问某个特定命令为何会被允许或阻止。

稳定的连接 ID

如果你没有向 ssh_connect 传递 connectionId(或传递 default),服务器会生成一个稳定的、结构化的 ID,并在连接响应中返回它

<IP>_YYYY_MM_DD_sessionid_<6 random chars>

示例:10_0_0_1_2026_06_08_sessionid_a1b9f3

IP 中的点被替换为 _,因此该 ID 可以安全地用作环境变量前缀(用于 <PREFIX>_PASSWORD 解析)以及文件名。请捕获返回的 connectionId,并在后续的 ssh_execute / ssh_disconnect 调用中复用它。

会话审计(命令 + 输出)

每条执行的命令及其输出都会以单行 JSONL 格式写入按天划分的文件中,与服务器诊断日志(SSH_LOG_FILE)分开:

<SSH_AUDIT_DIR>/audit_YYYY-MM-DD.jsonl

每条记录:

{"timestamp":"2026-06-08T11:07:12.569Z","connectionId":"10_0_0_1_2026_06_08_sessionid_a1b9f3","host":"10.0.0.1","command":"show version","exitCode":0,"output":"..."}

使用 SSH_AUDIT_ENABLED=false 禁用。

大输出卸载

当命令输出超过 SSH_MAX_OUTPUT_LENGTHSSH_ENABLE_LARGE_OUTPUT=true 时,完整输出会被 POST 到 SSH_FILE_UPLOAD_ENDPOINT,调用方会收到一个包含返回的 artifact_urifile_id 以及一小段预览的简短摘要——这样庞大的 show tech-support 输出就不会淹没模型上下文。端点接收 { "content": "...", "filename": "..." },并且必须返回 { "file_id": "...", "artifact_uri": "..." }。如果端点未设置或上传失败,则输出将以内联方式返回作为回退。

分页器处理

交互式分页器(Linux less、Cisco/Juniper ---(more)---)否则会阻塞命令直到超时。服务器通过两种方式处理此问题:

  • 预防——在打开 shell 时发送适合设备的禁用分页器命令(Cisco 为 terminal length 0,Juniper 为 set cli screen-length 0),在 Linux exec 时设置 SYSTEMD_PAGER=PAGER=catGIT_PAGER=cat

  • 检测——如果分页器提示仍然出现,则在网络 shell 上自动前进(发送空格,上限由 SSH_MAX_PAGER_PAGES 控制),或发送 q 退出交互式 Linux 分页器,然后从输出中剥离提示痕迹。

使用 SSH_DISABLE_PAGER=false 全局切换,使用 PROFILE_<NAME>_DISABLE_PAGER=false 按配置文件切换,使用 SSH_DISABLE_PAGER_CMD_<DEVICETYPE> 按设备类型覆盖命令,使用 SSH_PAGER_REGEX / SSH_PAGER_ADVANCE_KEY 覆盖检测。

危险模式

以下模式始终被阻止,无论过滤模式如何:

模式

示例

风险

Fork 炸弹

:(){ :|:& };:

系统崩溃

管道 rm

find . | rm

数据丢失

链式 rm

ls && rm -rf /

数据丢失

设备重定向

> /dev/sda

磁盘损坏

系统配置覆盖

> /etc/passwd

系统受损

远程代码执行

curl | bash

任意代码执行

递归 chmod 777

chmod -R 777 /

安全受损

可用工具

一次性(推荐)

工具

描述

ssh_run

连接、运行一条命令并在成功时关闭连接——一次调用,无需跟踪 connectionId。失败时连接保持打开状态,并返回其 connectionId,以便你可以使用 ssh_execute 重试不同的命令。必需参数:hostcommand

ssh_run_with_jump

ssh_run 相同,但首先进入嵌套 CLI。将 jumpCommands 作为列表接收,并按顺序尝试,直到其中一个到达嵌套提示符。必需参数:hostcommand

连接管理

工具

描述

ssh_connect

打开持久连接并返回 connectionId。密码从 <CONNECTIONID>_PASSWORD 环境变量自动解析;支持 sshOptions 用于旧算法协商。

ssh_connect_with_jump_command

SSH 连接到主机,然后通过单个跳转命令进入嵌套 CLI(telnet、fs_cli 等)。支持预设。当每个候选需要各自的提示符模式时使用此工具。

ssh_load_connections

从 CSV/JSON 文件加载连接(凭据按 connectionId 从环境变量解析)。

ssh_disconnect

断开一个连接。

ssh_disconnect_all

断开所有连接。

执行

工具

描述

ssh_execute

在现有连接上运行命令。必需参数:commandconnectionId

ssh_execute_on_multiple

在选定的连接上运行命令(["*"][] = 全部)。按顺序执行。

状态与内省

工具

描述

ssh_get_command_filter

显示适用于连接的命令过滤器(白名单/黑名单,全局 + 按配置文件,含优先级规则)和主机过滤器(允许/阻止的主机);可选地检查某个特定命令是否会被允许。

ssh_list_connections

列出活动连接及其状态。

ssh_check_connections

对所有连接进行健康检查(死套接字检测、shell 状态)。

ssh_failed_connections

列出最近的失败连接尝试(来自 failed-connections JSONL 日志)。

文件传输(SFTP)

工具

描述

ssh_upload_file

通过 SFTP 上传文件。

ssh_download_file

通过 SFTP 下载文件。

ssh_list_files

通过 SFTP 列出远程目录。

示例工作流

单条命令(一次调用)

→ ssh_run {
    host: "172.168.0.2",
    profile: "ROUTERS",
    command: "show version"
  }
  (connects, runs, closes; returns the output)

嵌套 CLI 中的单条命令(一次调用)

→ ssh_run_with_jump {
    host: "10.0.0.1",
    username: "admin",
    preset: "topex",
    jumpCommands: ["telnet lh", "telnet 127.0.0.1"],
    command: "view portsoncard *"
  }

失败后重试

1. → ssh_run { host: "172.168.0.2", profile: "ROUTERS", command: "show bogus" }
   ← { status: "error", connectionId: "172_168_0_2_..._sessionid_a1b2c3", exitCode: 2, ... }
     (connection left open)

2. → ssh_execute {
       command: "show interfaces terse",
       connectionId: "172_168_0_2_..._sessionid_a1b2c3"
     }

3. → ssh_disconnect { connectionId: "172_168_0_2_..._sessionid_a1b2c3" }

集群操作(持久连接)

1. Load connections from CSV (passwords auto-resolved from env vars)
   → ssh_load_connections { filePath: "devices.csv", connectAll: true }
   (ROUTER1_PASSWORD, ROUTER2_PASSWORD resolved automatically)

2. Execute show commands on all devices
   → ssh_execute_on_multiple {
       command: "show ip interface brief",
       connectionIds: ["*"]
     }

3. Execute a command on one specific router
   → ssh_execute {
       command: "show running-config | include hostname",
       connectionId: "router1"
     }

4. Check connection health
   → ssh_check_connections {}

5. Inspect why a command was blocked
   → ssh_get_command_filter {
       connectionId: "router1",
       command: "configure terminal"
     }

6. Disconnect all
   → ssh_disconnect_all {}

架构说明

Shell 缓冲区管理

每条命令执行前都会清空缓冲区。稳定性检测使用缓冲区连续 3 × 500ms 不变 = 命令完成。密码提示在缓冲区最后 200 个字符中检测。

保活系统

SSH2 每 10 秒发送一次保活(keepaliveInterval: 10000)。连续 3 次保活失败后,连接自动关闭(keepaliveCountMax: 3)。自定义间隔会记录保活计数以用于调试。

连接健康监控

服务器检测死连接(套接字已销毁),跟踪网络设备的 shell 状态,自动清理死连接,并在网络设备的 shell 已关闭时尝试重新打开 shell。

跳转 Shell

当调用 ssh_connect_with_jump_command 时,服务器:(1) 打开 SSH 连接,(2) 打开 PTY shell,(3) 发送跳转命令(例如 telnet lh),(4) 每 300ms 轮询 shell 缓冲区以匹配预期的提示符正则表达式,(5) 将连接标记为 jump_shell,并设置 jumpShellActive: true。断开连接时,在关闭 SSH 会话之前发送嵌套 CLI 退出命令。在 shell 恢复时,自动重新发送跳转命令。

环境变量凭据解析

创建连接时(通过 ssh_connectssh_load_connections),如果未提供密码,服务器会自动从环境变量中查找 <PREFIX>_PASSWORD,其中 <PREFIX> 是 connectionId 的大写形式,非字母数字字符替换为 _。同样的约定适用于 _ENABLE_PASSWORD_USERNAME。显式提供的值始终优先。

与原版的比较

Возможность

zibdie/SSH-MCP-Server

Этот форк

Базовый SSH/SFTP

Белый список команд

Чёрный список команд

Записи чёрного списка из нескольких слов

Обнаружение опасных шаблонов

Журналирование аудита

Инструмент проверки команд

Поддержка файлов конфигурации

Типы сетевых устройств (Cisco, Juniper, MikroTik)

Режим Cisco enable

Jump-оболочка (вложенный CLI через SSH)

Массовые подключения из CSV/JSON

Выполнение на нескольких подключениях

Учётные данные из переменных окружения

Мониторинг состояния подключений

Отслеживание keepalive

Совместимость host/hostname

Разработка

# Clone
git clone https://github.com/marian-craciunescu/ssh-mcp-server-secured.git
cd ssh-mcp-server-secured

# Install dependencies
npm install

# Run in development mode
npm run dev

# Test with MCP Inspector
npx @modelcontextprotocol/inspector node index.js

Вопросы безопасности

  • По умолчанию используется режим чёрного списка — обеспечивает защиту, оставаясь гибким

  • Опасные шаблоны проверяются всегда — даже в отключённом режиме

  • Журналирование аудита включено по умолчанию — отслеживайте заблокированные попытки

  • Sudo можно ограничить — установите SSH_ALLOW_SUDO=false для сред с высокими требованиями безопасности

  • Изоляция учётных данных — пароли извлекаются из переменных окружения по connectionId, никогда не вводятся в чате и не видны в вызовах инструментов

Лицензия

MIT — см. файл LICENSE

Авторы

Поддержка

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

Maintenance

Maintainers
Response time
2wRelease cycle
18Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

  • F
    license
    A
    quality
    F
    maintenance
    A server based on the MCP framework that provides remote server management capabilities through SSH, supporting features like connection pooling, file transfers, and remote command execution.
    7
  • A
    license
    A
    quality
    C
    maintenance
    A secure remote server management tool based on MCP protocol, supporting SSH connections, command execution, and SFTP file transfers.
    20
    41
    6
    MIT
  • A
    license
    A
    quality
    B
    maintenance
    An MCP server for SSH/SCP operations with passwordless authentication, enabling remote command execution, file transfer, and session management.
    19
    23
    MIT

View all related MCP servers

Related MCP Connectors

  • An MCP server for deep research or task groups

  • MCP Server for JFrog, providing tools for development and artifact management.

  • An authenticated remote MCP server for user-owned devices and one-shot capability invocation.

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/marian-craciunescu/ssh-mcp-server-secured'

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