Skip to main content
Glama

SSH MCP Server (Secured)

npm version CI/CD License: MIT

Un fork asegurado de zibdie/SSH-MCP-Server con filtrado de lista blanca/lista negra de comandos, soporte para dispositivos de red y gestión de conexiones masivas para la administración remota segura de servidores mediante MCP (Model Context Protocol).

Características principales

  • Ejecución de un solo disparo: ssh_run se conecta, ejecuta un comando y se desconecta en una sola llamada de herramienta — sin connectionId que gestionar

  • Lista blanca/lista negra de comandos: Controla qué comandos se pueden ejecutar

  • Detección de patrones peligrosos: Bloquea fork bombs, inyección de comandos y patrones destructivos

  • Soporte para dispositivos de red: Cisco, Juniper, MikroTik, FortiGate, Palo Alto, Sophos con sesiones de shell persistentes y supresión automática del paginador

  • Soporte de shell de salto: Conéctate por SSH a un host y luego entra en una CLI anidada (telnet a un host, FreeSWITCH fs_cli, etc.) — los comandos se ejecutan dentro del shell anidado, con una lista de respaldo ordenada de comandos de salto

  • Gestión de conexiones masivas: Carga docenas de conexiones desde archivos CSV/JSON

  • Credenciales mediante variables de entorno: Las contraseñas se resuelven automáticamente desde variables de entorno por connectionId — sin secretos en el chat

  • Ejecución en múltiples conexiones: Ejecuta comandos en todas o en conexiones seleccionadas simultáneamente

  • Monitorización de salud de conexiones: Seguimiento de keepalive, detección de conexiones muertas, limpieza automática

  • Políticas de seguridad configurables: Mediante archivo de configuración o variables de entorno

  • Registro de auditoría: Registra todos los intentos de comandos bloqueados

Related MCP server: SSH MCP Server

Instalación

Configuración rápida (Recomendada)

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

Instalación manual

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

Uso

1. Conexión única

Conéctate a un host usando ssh_connect. Solo necesitas proporcionar host, username y connectionId — la contraseña se resuelve automáticamente desde las variables de entorno:

Connect to host 172.168.0.2 with user admin connectionId=router1

El LLM llama a ssh_connect con:

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

Sin contraseña en la llamada a la herramienta. El servidor busca automáticamente ROUTER1_PASSWORD en las variables de entorno.

Convención de resolución de credenciales

El connectionId se convierte en un prefijo de variable de entorno: en mayúsculas, con los caracteres no alfanuméricos reemplazados por _.

connectionId

Variable de entorno para la contraseña

Variable de entorno para la contraseña de 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

Opcionalmente, también se resuelve <PREFIX>_USERNAME si no se proporciona el username.

Configura las credenciales en tu configuración de 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"
      }
    }
  }
}

Las credenciales residen en la configuración de MCP (o se inyectan mediante CI/CD, vault, etc.) y nunca aparecen en el chat ni en las llamadas a herramientas. Si se proporciona una contraseña explícitamente en la llamada a la herramienta, tiene prioridad sobre la variable de entorno.

Opciones SSH para dispositivos heredados

Al conectarte a dispositivos antiguos que requieren algoritmos no predeterminados (el equivalente a ssh -o), usa el parámetro sshOptions:

En lenguaje natural:

Conéctate a 10.0.0.1 puerto 2222 como usuario, connectionId old-switch, con KexAlgorithms +diffie-hellman-group-exchange-sha1 y 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"
  }
}

Esto es equivalente a:

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

Prefija un valor con + para añadirlo a los valores predeterminados de ssh2. Sin +, el valor reemplaza los valores predeterminados por completo.

Opción

Equivalente en SSH2

Caso de uso

KexAlgorithms

algorithms.kex

Intercambio de claves heredado (p. ej. diffie-hellman-group1-sha1)

HostKeyAlgorithms

algorithms.serverHostKey

Claves de host heredadas (p. ej. ssh-rsa, ssh-dss)

Ciphers

algorithms.cipher

Cifrados heredados (p. ej. aes128-cbc)

MACs

algorithms.hmac

MAC heredados (p. ej. hmac-sha1)

sshOptions es compatible con ssh_connect, ssh_connect_with_jump_command y archivos JSON cargados mediante ssh_load_connections.

La autenticación keyboard-interactive está habilitada automáticamente (tryKeyboard: true). Los dispositivos heredados que rechazan la autenticación estándar por contraseña y requieren keyboard-interactive funcionarán sin configuración adicional.

2. Conexiones masivas desde archivo

Carga múltiples conexiones desde un archivo CSV o JSON usando ssh_load_connections. Las contraseñas se resuelven desde variables de entorno usando la misma convención de connectionId:

Formato 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

Sin contraseñas en el archivo. El servidor resuelve ROUTER1_PASSWORD, ROUTER2_PASSWORD, SERVER1_PASSWORD desde las variables de entorno.

NOTA: CSV no puede transportar objetos, por lo que las opciones SSH para dispositivos heredados deben configurarse mediante variables de entorno individuales o en un archivo JSON.

Formato 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"
    }
  }
]

Perfiles: Define perfiles de conexión reutilizables para conectarte al mismo tipo de dispositivo con configuraciones similares (p. ej. todos los switches Cisco). Los perfiles pueden incluir opciones SSH predeterminadas para dispositivos heredados, para que no tengas que repetirlas en cada conexión.

Prioridad de resolución: argumentos explícitos > variables de entorno del perfil > variables de entorno del 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"}'

A CONTINUACIÓN se muestra un ejemplo de cómo se resuelven las variables de entorno del perfil al cargar conexiones desde CSV/JSON. El valor de PROFILE_CISCO_SSH_OPTIONS se analiza como JSON y se aplica a todas las conexiones con deviceType de cisco.

Ejemplo de variable de entorno

Campo

Valor

PROFILE_CISCO_USER

username

admin

PROFILE_CISCO_PASSWORD

password

secret123

PROFILE_CISCO_DEVICE_TYPE

deviceType

cisco

PROFILE_CISCO_SSH_OPTIONS

sshOptions (analizado como 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

lista blanca de comandos por perfil (separada por comas o array JSON)

show ospf neigh,show version

PROFILE_CISCO_BLACKLIST

lista negra de comandos por perfil (separada por comas o array JSON)

show running config,conf t

PROFILE_CISCO_DISABLE_PAGER

alternancia de paginador por perfil (true/false)

false

ssh_connect host=10.0.0.1 profile=CISCO connectionId=SWITCH1"

Filtrado de comandos por perfil

Además de la SSH_WHITELIST / SSH_BLACKLIST global, cada perfil puede llevar su propio filtro de comandos mediante PROFILE_<NAME>_WHITELIST y PROFILE_<NAME>_BLACKLIST. Estos se superponen al filtro global en el momento de la ejecución para cualquier conexión abierta con ese perfil:

  • La lista negra del perfil siempre bloquea — incluso comandos que el filtro global permitiría (p. ej. bloquear show running config).

  • La lista blanca del perfil vuelve a permitir comandos específicos y, cuando está presente, se vuelve autoritativa: cualquier cosa no listada se bloquea (p. ej. permitir show ospf neigh mientras la lista negra sigue bloqueando el resto).

  • En caso de conflicto directo, gana la lista negra.

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 desactiva la supresión del paginador para las conexiones que usan ese perfil, anulando el valor predeterminado global SSH_DISABLE_PAGER.

Uso:

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

Nota: Aún puedes proporcionar contraseñas directamente en CSV/JSON si lo prefieres — la resolución de variables de entorno solo se activa cuando el campo de contraseña falta o está vacío.

3. Tipos de dispositivos de red

El servidor admite diferentes tipos de dispositivos con el manejo de conexión adecuado:

Tipo de dispositivo

Comportamiento

Caso de uso

linux

Modo exec SSH estándar (predeterminado)

Servidores Linux/Unix

cisco

Shell persistente, soporte de modo enable

Routers y switches Cisco IOS/IOS-XE

cisco_xe

Shell persistente (terminal length 0)

Cisco IOS-XE

cisco_xr

Shell persistente (terminal length 0)

Cisco IOS-XR

cisco_asa

Shell persistente (terminal length 0)

Firewalls Cisco ASA

cisco_nexus

Shell persistente (terminal length 0)

Cisco Nexus (NX-OS)

juniper

Shell persistente (set cli screen-length 0)

Dispositivos Juniper JunOS

mikrotik

Shell persistente

MikroTik RouterOS

fortinet

Shell persistente (config system console / set output standard)

Firewalls FortiGate / FortiOS

paloalto

Shell persistente (set cli pager off)

Firewalls Palo Alto PAN-OS

sophos

Shell persistente (paginador gestionado automáticamente en tiempo de ejecución)

Firewalls Sophos XG/XGS (SFOS)

network

Shell persistente genérico

Otros dispositivos de red

jump_shell

Shell persistente + CLI anidada

Usado internamente por ssh_connect_with_jump_command

Los dispositivos de red usan sesiones de shell persistentes con PTY en lugar del exec() estándar porque muchos sistemas operativos de red cierran el canal SSH después de cada comando exec.

4. Comando de un solo disparo (ssh_run)

ssh_connect + ssh_execute + ssh_disconnect son tres llamadas a herramientas, y las dos intermedias requieren que el modelo copie un connectionId generado textualmente. ssh_run lo reduce a una sola llamada:

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

Se conecta, ejecuta el comando y cierra la conexión. Devuelve la salida del comando — sin connectionId que rastrear.

En caso de fallo, la conexión se mantiene abierta para que puedas reintentar con un comando diferente. El resultado es un objeto estructurado (devuelto tanto como texto JSON como en 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."
}

Reintenta con ssh_execute usando ese connectionId y luego ssh_disconnect. Las conexiones abandonadas se eliminan mediante SSH_IDLE_TIMEOUT (120 s por defecto).

Los perfiles, la lista blanca/negra, el filtro de hosts, el registro de auditoría, el manejo de paginadores y la descarga de salidas grandes se comportan exactamente igual que con ssh_connect + ssh_execute. Un comando bloqueado por el filtro se rechaza antes de abrir cualquier sesión SSH.

El éxito se define como: el comando se ejecutó y su código de salida es 0 o está ausente. Los dispositivos de red en la ruta de shell persistente no informan códigos de salida, por lo que esos comandos se consideran exitosos a menos que la ejecución en sí falle. En Linux, un código de salida distinto de cero cuenta como fallo y mantiene la conexión abierta.

CLI anidado con respaldos (ssh_run_with_jump)

Mismo flujo de una sola llamada, pero entra primero en un CLI anidado. jumpCommands es una lista que se prueba en orden hasta que uno alcanza el prompt anidado:

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

Si telnet lh no logra alcanzar el prompt, se prueba telnet 127.0.0.1. Cada intento es una conexión nueva, por lo que un telnet a medio abrir de un intento fallido no puede corromper el siguiente. Si todos los candidatos fallan, el error enumera lo que devolvió cada uno.

Todos los candidatos comparten un único jumpPromptPattern (proporcionado directamente o mediante preset). Cuando los candidatos necesitan patrones de prompt diferentes, usa ssh_connect_with_jump_command en su lugar. PROFILE_<NAME>_JUMP_COMMAND proporciona un único candidato cuando se omite jumpCommands.

5. Ejecutar en Múltiples Conexiones

Ejecuta un comando en conexiones específicas usando ssh_execute_on_multiple:

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

O ejecuta en TODAS las conexiones:

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

6. Shell de Salto (CLI Anidado vía SSH)

Usa ssh_connect_with_jump_command cuando necesites conectarte por SSH a un host y luego entrar en un shell interactivo anidado antes de ejecutar comandos. Esto cubre escenarios como:

  • Telnet a una pasarela VoIP Topex desde un host de salto SSH

  • FreeSWITCH fs_cli en un servidor remoto

  • Cualquier CLI que requiera una sesión interactiva después de SSH

Cómo funciona:

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

Todos los comandos ssh_execute posteriores en ese connectionId se ejecutan dentro del shell anidado.

Ejemplo de pasarela Topex (con preset):

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

El preset topex rellena automáticamente jumpPromptPattern: "topexsw>\\s*$" y jumpExitCommand: "quit". Solo necesitas proporcionar jumpCommand.

Luego ejecuta comandos dentro del CLI de Topex:

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

Ejemplo de FreeSWITCH (el preset lo rellena todo):

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

El preset freeswitch rellena automáticamente jumpCommand: "fs_cli", jumpPromptPattern: "freeswitch@...>" y jumpExitCommand: "/exit". Luego:

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

Totalmente personalizado (sin preset):

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

Presets integrados:

Preset

jumpCommand

Patrón de prompt

Comando de salida

freeswitch

fs_cli

freeswitch@...>

/exit

topex

(lo proporciona el usuario)

topexsw>

quit

Los presets se pueden sobrescribir: cualquier parámetro proporcionado explícitamente tiene prioridad.

Recuperación del shell: Si el shell se cae, ssh_execute reabre automáticamente el shell y vuelve a entrar en el shell de salto.

Desconexión: ssh_disconnect envía correctamente el comando de salida al CLI anidado antes de cerrar la conexión SSH.

7. Registro (Logging)

Establece el nivel de registro mediante la variable de entorno:

Variable

Valores

Por defecto

SSH_LOG_LEVEL

DEBUG, INFO, WARN, ERROR

INFO

SSH_LOG_FILE

Ruta al archivo de registro

(ninguno)

Formato de registro:

[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

Configuración

Variables de Entorno

Variable

Valores

Por defecto

Descripción

SSH_FILTER_MODE

whitelist, blacklist, disabled

blacklist

Modo de filtrado de comandos

SSH_ALLOW_SUDO

true, false

true

Permitir comandos sudo

SSH_LOG_BLOCKED

true, false

true

Registrar comandos bloqueados en stderr

SSH_MCP_CONFIG

ruta de archivo

-

Ruta al archivo JSON de configuración

SSH_WHITELIST

separados por comas o JSON

-

Sobrescribir comandos de la lista blanca

SSH_BLACKLIST

separados por comas o JSON

-

Sobrescribir comandos de la lista negra

SSH_DANGEROUS_PATTERNS

matriz JSON

-

Sobrescribir patrones regex peligrosos

SSH_LOG_LEVEL

DEBUG, INFO, WARN, ERROR

INFO

Verbosidad del registro

SSH_LOG_FILE

ruta

-

Registrar en archivo

SSH_HOST_FILTER_MODE

whitelist, blacklist, disabled

disabled

Modo de filtrado de hosts

SSH_HOST_WHITELIST

IPs separadas por comas

-

Lista blanca de IPs de host permitidas

SSH_HOST_BLACKLIST

IPs separadas por comas

-

Lista negra de IPs de host permitidas

SSH_IDLE_TIMEOUT

segundos

120

Tiempo de espera de conexión inactiva

SSH_FAILED_CONNECTIONS_LOG

ruta de archivo

./ssh-failed-connections.json

/var/log/ssh-failed.jsonl

SSH_AUDIT_ENABLED

true, false

true

Escribir una auditoría de sesión por comando (comando + salida completa) en JSONL

SSH_AUDIT_DIR

ruta

./audit

Directorio para archivos diarios audit_YYYY-MM-DD.jsonl

SSH_ENABLE_LARGE_OUTPUT

true, false

false

Descargar la salida de comandos sobredimensionada a un endpoint de carga y devolver una URI en lugar de texto en línea

SSH_MAX_OUTPUT_LENGTH

entero (caracteres)

10000

Umbral de tamaño de salida por encima del cual se descarga la salida

SSH_FILE_UPLOAD_ENDPOINT

URL

-

Destino POST para salidas grandes. Recibe {content, filename}, debe devolver {file_id, artifact_uri}

SSH_DISABLE_PAGER

true, false

true

Suprimir paginadores interactivos (less/---(more)---) en shell y exec

SSH_DISABLE_PAGER_CMD_<DEVICETYPE>

cadena

por defecto por dispositivo

Sobrescribir el comando de desactivación de paginador para un tipo de dispositivo (p. ej. SSH_DISABLE_PAGER_CMD_CISCO)

SSH_PAGER_REGEX

cadena regex

integrado

Sobrescribir el patrón usado para detectar un prompt de paginador

SSH_PAGER_ADVANCE_KEY

cadena

" " (espacio)

Tecla enviada para avanzar a la siguiente página del paginador

SSH_MAX_PAGER_PAGES

entero

1000

Límite de seguridad en páginas auto-paginadas por comando

Cualquier variable de entorno adicional que siga la convención <CONNECTIONID>_PASSWORD se usa automáticamente para la resolución de credenciales (consulta Convención de resolución de credenciales).

Ejemplos de Configuración MCP

Lista blanca/negra de hosts:

Modo lista negra con comandos bloqueados personalizados:

{
  "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"
    }
  }
}

Modo lista blanca (estricto: solo permite comandos específicos):

{
  "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"
    }
  }
}

Operaciones de red con variables de entorno de credenciales:

{
  "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"
    }
  }
}

Ahora en el chat simplemente dices connect to 172.168.0.2 as admin connectionId=router1 — sin contraseñas expuestas.

Vía npx (sin instalación global):

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

Archivo de Configuración

Crea config.json o 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"
    ]
  }
}

Modos de Filtro

Modo Lista Negra (Por Defecto)

Los comandos de la lista negra se bloquean. Todo lo demás está permitido. Admite entradas de varias palabras como configure terminal y 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

Modo Lista Blanca

Solo se permiten los comandos de la lista blanca. Todo lo demás se bloquea.

✓ 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

Modo Mixto

Ambas listas están activas a la vez y el filtro es denegar por defecto: un comando debe coincidir con una entrada de la lista blanca para ejecutarse. En caso de conflicto, gana la entrada coincidente más larga, independientemente de la lista de la que provenga. Esto es lo que te permite permitir un prefijo amplio, recortar un subconjunto peligroso de él y luego volver a permitir una excepción más estrecha.

La coincidencia es por prefijo: una entrada coincide cuando el comando es igual a ella, o comienza con ella seguida de un espacio, tabulador o nueva línea. La comparación se hace en minúsculas y recortada.

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

Decisiones resultantes:

✓ 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

Dos cosas que debes saber:

  • ls -lha está permitido pero ls -la no. Las entradas de la lista blanca son prefijos literales, no patrones. En modo mixto, cualquier cosa que no hayas permitido explícitamente se bloquea, así que enumera las formas exactas de comando que pretendes ejecutar.

  • Los empates exactos van a la lista blanca. Si la misma cadena está en ambas listas, el comando se permite.

Incluye tus comandos de desactivación de paginador (terminal length 0, set cli screen-length 0) en la lista blanca. El servidor los emite por sí mismo al abrir un shell, y el modo mixto los bloquearía de otro modo.

A diferencia del modo lista negra y lista blanca, el modo mixto no inspecciona los segmentos individuales de una tubería o cadena: solo coincide con la cadena de comando completa. La protección a nivel de segmento en modo mixto proviene de la lista de patrones peligrosos, que se ejecuta primero y no se puede sobrescribir:

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

Modo Deshabilitado

Sin filtrado de comandos (usar con precaución).

Orden de Validación de Comandos

  1. Verificar si el filtrado está deshabilitado

  2. Verificar el permiso de sudo

  3. Verificar patrones peligrosos (regex) — siempre gana, ninguna lista blanca puede anularlo

  4. En modo mixed: coincidencia más larga entre lista blanca y lista negra sobre el comando completo; denegar por defecto si ninguno coincide. Se detiene aquí.

  5. Verificar el comando completo contra la lista negra (soporte multi-palabra)

  6. Extraer comandos base de tuberías/cadenas

  7. Verificar cada comando base contra la lista negra/lista blanca

  8. Aplicar lista blanca/lista negra por perfil (por conexión) además del resultado global

Usa ssh_get_command_filter para ver las reglas activas y para preguntar por qué un comando específico sería permitido o bloqueado.

IDs de Conexión Estables

Si no pasas un connectionId a ssh_connect (o pasas default), el servidor genera un id estructurado y estable y lo devuelve en la respuesta de conexión:

<IP>_YYYY_MM_DD_sessionid_<6 random chars>

Ejemplo: 10_0_0_1_2026_06_08_sessionid_a1b9f3

Los puntos de la IP se reemplazan con _ para que el id sea seguro de usar como prefijo de variable de entorno (para la resolución de <PREFIX>_PASSWORD) y como nombre de archivo. Captura el connectionId devuelto y reutilízalo para llamadas posteriores de ssh_execute / ssh_disconnect.

Auditoría de Sesión (comando + salida)

Cada comando ejecutado y su salida se escriben como una línea JSONL en un archivo diario, separado de los diagnósticos del servidor (SSH_LOG_FILE):

<SSH_AUDIT_DIR>/audit_YYYY-MM-DD.jsonl

Cada registro:

{"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":"..."}

Deshabilitar con SSH_AUDIT_ENABLED=false.

Descarga de Salidas Grandes

Cuando la salida de un comando excede SSH_MAX_OUTPUT_LENGTH y SSH_ENABLE_LARGE_OUTPUT=true, la salida completa se envía mediante POST a SSH_FILE_UPLOAD_ENDPOINT y el llamador recibe un stub corto que contiene el artifact_uri y file_id devueltos, más una pequeña vista previa — para que un show tech enorme nunca inunde el contexto del modelo. El endpoint recibe { "content": "...", "filename": "..." } y debe devolver { "file_id": "...", "artifact_uri": "..." }. Si el endpoint no está configurado o la subida falla, la salida se devuelve en línea como respaldo.

Manejo de Paginadores

Los paginadores interactivos (Linux less, Cisco/Juniper ---(more)---) de otro modo bloquean un comando hasta que se agota el tiempo de espera. El servidor maneja esto de dos maneras:

  • Prevención — al abrir el shell envía un comando apropiado para deshabilitar el paginador (terminal length 0 para Cisco, set cli screen-length 0 para Juniper), y en la ejecución de Linux establece SYSTEMD_PAGER=, PAGER=cat, GIT_PAGER=cat.

  • Detección — si un indicador de paginador aún aparece, avanza automáticamente (envía un espacio, limitado por SSH_MAX_PAGER_PAGES) en shells de red, o envía q para salir de un paginador interactivo de Linux, luego elimina los artefactos del paginador de la salida.

Alternar globalmente con SSH_DISABLE_PAGER=false, por perfil con PROFILE_<NAME>_DISABLE_PAGER=false, anular el comando por tipo de dispositivo con SSH_DISABLE_PAGER_CMD_<DEVICETYPE>, y anular la detección con SSH_PAGER_REGEX / SSH_PAGER_ADVANCE_KEY.

Patrones Peligrosos

Estos patrones siempre están bloqueados independientemente del modo de filtrado:

Patrón

Ejemplo

Riesgo

Bomba fork

:(){ :|:& };:

Bloqueo del sistema

rm con tubería

find . | rm

Pérdida de datos

rm encadenado

ls && rm -rf /

Pérdida de datos

Redirección a dispositivo

> /dev/sda

Corrupción de disco

Sobrescritura de configuración del sistema

> /etc/passwd

Compromiso del sistema

Ejecución remota de código

curl | bash

Ejecución arbitraria de código

chmod 777 recursivo

chmod -R 777 /

Compromiso de seguridad

Herramientas Disponibles

Disparo único (recomendado)

Herramienta

Descripción

ssh_run

Conecta, ejecuta un comando y cierra la conexión al tener éxito — una sola llamada, sin connectionId que rastrear. En caso de fallo, la conexión se deja abierta y se devuelve su connectionId para que puedas reintentar con un comando diferente usando ssh_execute. Requerido: host, command.

ssh_run_with_jump

Igual que ssh_run, pero primero entra a una CLI anidada. Toma jumpCommands como una lista y los prueba en orden hasta que uno alcanza el prompt anidado. Requerido: host, command.

Gestión de conexiones

Herramienta

Descripción

ssh_connect

Abre una conexión persistente y devuelve un connectionId. Contraseña resuelta automáticamente desde la variable de entorno <CONNECTIONID>_PASSWORD; admite sshOptions para negociación de algoritmos heredados.

ssh_connect_with_jump_command

Conecta por SSH a un host y luego entra a una CLI anidada (telnet, fs_cli, etc.) mediante un único comando de salto. Admite ajustes preestablecidos. Úsalo cuando cada candidato necesite su propio patrón de prompt.

ssh_load_connections

Carga conexiones desde un archivo CSV/JSON (credenciales resueltas desde variables de entorno por connectionId).

ssh_disconnect

Cierra una conexión.

ssh_disconnect_all

Cierra todas las conexiones.

Ejecución

Herramienta

Descripción

ssh_execute

Ejecuta un comando en una conexión existente. Requerido: command, connectionId.

ssh_execute_on_multiple

Ejecuta un comando en conexiones seleccionadas (["*"] o [] = todas). Se ejecuta secuencialmente.

Estado e introspección

Herramienta

Descripción

ssh_get_command_filter

Muestra el filtro de comandos (lista blanca/lista negra, global + por perfil, con reglas de precedencia) y el filtro de hosts (hosts permitidos/bloqueados) que aplican a una conexión; opcionalmente verifica si un comando específico sería permitido.

ssh_list_connections

Lista conexiones activas con su estado.

ssh_check_connections

Verificación de salud de todas las conexiones (detección de socket muerto, estado del shell).

ssh_failed_connections

Lista intentos de conexión fallidos recientes (del registro JSONL de fallos).

Transferencia de archivos (SFTP)

Herramienta

Descripción

ssh_upload_file

Sube un archivo vía SFTP.

ssh_download_file

Descarga un archivo vía SFTP.

ssh_list_files

Lista un directorio remoto vía SFTP.

Ejemplo de Flujo de Trabajo

Comando único (una llamada)

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

Comando único dentro de una CLI anidada (una llamada)

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

Reintento después de un fallo

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" }

Operaciones en flota (conexiones persistentes)

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 {}

Notas de Arquitectura

Gestión del Buffer del Shell

El buffer se limpia antes de cada comando. La detección de estabilidad usa el buffer sin cambios durante 3 × 500 ms = comando completado. Los indicadores de contraseña se detectan en los últimos 200 caracteres del buffer.

Sistema de Keepalive

SSH2 envía keepalives cada 10 segundos (keepaliveInterval: 10000). Después de 3 keepalives fallidos, la conexión se cierra automáticamente (keepaliveCountMax: 3). Un intervalo personalizado registra el conteo de keepalives para depuración.

Monitoreo de Salud de Conexiones

El servidor detecta conexiones muertas (socket destruido), rastrea el estado del shell para dispositivos de red, limpia automáticamente conexiones muertas e intenta reabrir el shell en dispositivos de red si el shell se ha cerrado.

Shell de Salto

Cuando se llama a ssh_connect_with_jump_command, el servidor: (1) abre una conexión SSH, (2) abre un shell PTY, (3) envía el comando de salto (ej. telnet lh), (4) sondea el buffer del shell cada 300 ms buscando la expresión regular del prompt esperado, (5) marca la conexión como jump_shell con jumpShellActive: true. Al desconectar, el comando de salida de la CLI anidada se envía antes de cerrar la sesión SSH. En la recuperación del shell, el comando de salto se reenvía automáticamente.

Resolución de Credenciales por Variables de Entorno

Cuando se crea una conexión (mediante ssh_connect o ssh_load_connections), si no se proporciona la contraseña, el servidor busca automáticamente <PREFIX>_PASSWORD en las variables de entorno, donde <PREFIX> es el connectionId en mayúsculas con los caracteres no alfanuméricos reemplazados por _. La misma convención aplica para _ENABLE_PASSWORD y _USERNAME. Los valores proporcionados explícitamente siempre tienen prioridad.

Comparación con el Original

Característica

zibdie/SSH-MCP-Server

Este fork

SSH/SFTP básico

Lista blanca de comandos

Lista negra de comandos

Entradas de lista negra de varias palabras

Detección de patrones peligrosos

Registro de auditoría

Herramienta de validación de comandos

Soporte de archivo de configuración

Tipos de dispositivos de red (Cisco, Juniper, MikroTik)

Modo enable de Cisco

Shell de salto (CLI anidada vía SSH)

Conexiones masivas desde CSV/JSON

Ejecución de múltiples conexiones

Credenciales de variables de entorno

Monitoreo de salud de conexión

Seguimiento de keepalive

Compatibilidad con host/hostname

Desarrollo

# 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

Consideraciones de seguridad

  • El modo predeterminado es lista negra — proporciona protección mientras sigue siendo flexible

  • Los patrones peligrosos siempre se verifican — incluso en modo deshabilitado

  • Registro de auditoría habilitado por defecto — rastree intentos bloqueados

  • Sudo puede restringirse — establezca SSH_ALLOW_SUDO=false para entornos de alta seguridad

  • Aislamiento de credenciales — las contraseñas se resuelven desde variables de entorno por connectionId, nunca se escriben en el chat ni son visibles en llamadas de herramientas

Licencia

MIT — consulte el archivo LICENSE

Créditos

Soporte

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