ssh-mcp-server-secured
SSH MCP Server (Secured)
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_runse conecta, ejecuta un comando y se desconecta en una sola llamada de herramienta — sin connectionId que gestionarLista 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=router1El 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 |
|
|
|
|
|
|
|
|
|
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-rsaPrefija 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 |
|
| Intercambio de claves heredado (p. ej. |
|
| Claves de host heredadas (p. ej. |
|
| Cifrados heredados (p. ej. |
|
| MAC heredados (p. ej. |
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,server1Sin 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 ( | 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 neighmientras 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 allNota: 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 |
| Modo exec SSH estándar (predeterminado) | Servidores Linux/Unix |
| Shell persistente, soporte de modo enable | Routers y switches Cisco IOS/IOS-XE |
| Shell persistente ( | Cisco IOS-XE |
| Shell persistente ( | Cisco IOS-XR |
| Shell persistente ( | Firewalls Cisco ASA |
| Shell persistente ( | Cisco Nexus (NX-OS) |
| Shell persistente ( | Dispositivos Juniper JunOS |
| Shell persistente | MikroTik RouterOS |
| Shell persistente ( | Firewalls FortiGate / FortiOS |
| Shell persistente ( | Firewalls Palo Alto PAN-OS |
| Shell persistente (paginador gestionado automáticamente en tiempo de ejecución) | Firewalls Sophos XG/XGS (SFOS) |
| Shell persistente genérico | Otros dispositivos de red |
| Shell persistente + CLI anidada | Usado internamente por |
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_clien un servidor remotoCualquier 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>") → readyTodos 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 |
|
|
|
|
| (lo proporciona el usuario) |
|
|
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 |
| DEBUG, INFO, WARN, ERROR | INFO |
| 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: router1Configuración
Variables de Entorno
Variable | Valores | Por defecto | Descripción |
|
|
| Modo de filtrado de comandos |
|
|
| Permitir comandos sudo |
|
|
| Registrar comandos bloqueados en stderr |
| ruta de archivo | - | Ruta al archivo JSON de configuración |
| separados por comas o JSON | - | Sobrescribir comandos de la lista blanca |
| separados por comas o JSON | - | Sobrescribir comandos de la lista negra |
| matriz JSON | - | Sobrescribir patrones regex peligrosos |
|
|
| Verbosidad del registro |
| ruta | - | Registrar en archivo |
| whitelist, blacklist, disabled | disabled | Modo de filtrado de hosts |
| IPs separadas por comas | - | Lista blanca de IPs de host permitidas |
| IPs separadas por comas | - | Lista negra de IPs de host permitidas |
| segundos | 120 | Tiempo de espera de conexión inactiva |
| ruta de archivo | ./ssh-failed-connections.json | /var/log/ssh-failed.jsonl |
|
|
| Escribir una auditoría de sesión por comando (comando + salida completa) en JSONL |
| ruta |
| Directorio para archivos diarios |
|
|
| Descargar la salida de comandos sobredimensionada a un endpoint de carga y devolver una URI en lugar de texto en línea |
| entero (caracteres) |
| Umbral de tamaño de salida por encima del cual se descarga la salida |
| URL | - | Destino POST para salidas grandes. Recibe |
|
|
| Suprimir paginadores interactivos ( |
| cadena | por defecto por dispositivo | Sobrescribir el comando de desactivación de paginador para un tipo de dispositivo (p. ej. |
| cadena regex | integrado | Sobrescribir el patrón usado para detectar un prompt de paginador |
| cadena |
| Tecla enviada para avanzar a la siguiente página del paginador |
| 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 blacklistModo 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 whitelistModo 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, pingDecisiones 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 matchDos cosas que debes saber:
ls -lhaestá permitido perols -lano. 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/iModo Deshabilitado
Sin filtrado de comandos (usar con precaución).
Orden de Validación de Comandos
Verificar si el filtrado está deshabilitado
Verificar el permiso de sudo
Verificar patrones peligrosos (regex) — siempre gana, ninguna lista blanca puede anularlo
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í.Verificar el comando completo contra la lista negra (soporte multi-palabra)
Extraer comandos base de tuberías/cadenas
Verificar cada comando base contra la lista negra/lista blanca
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.jsonlCada 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 0para Cisco,set cli screen-length 0para Juniper), y en la ejecución de Linux estableceSYSTEMD_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íaqpara 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 |
| Pérdida de datos |
rm encadenado |
| Pérdida de datos |
Redirección a dispositivo |
| Corrupción de disco |
Sobrescritura de configuración del sistema |
| Compromiso del sistema |
Ejecución remota de código |
| Ejecución arbitraria de código |
chmod 777 recursivo |
| Compromiso de seguridad |
Herramientas Disponibles
Disparo único (recomendado)
Herramienta | Descripción |
| 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 |
| Igual que |
Gestión de conexiones
Herramienta | Descripción |
| Abre una conexión persistente y devuelve un |
| 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. |
| Carga conexiones desde un archivo CSV/JSON (credenciales resueltas desde variables de entorno por connectionId). |
| Cierra una conexión. |
| Cierra todas las conexiones. |
Ejecución
Herramienta | Descripción |
| Ejecuta un comando en una conexión existente. Requerido: |
| Ejecuta un comando en conexiones seleccionadas ( |
Estado e introspección
Herramienta | Descripción |
| 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. |
| Lista conexiones activas con su estado. |
| Verificación de salud de todas las conexiones (detección de socket muerto, estado del shell). |
| Lista intentos de conexión fallidos recientes (del registro JSONL de fallos). |
Transferencia de archivos (SFTP)
Herramienta | Descripción |
| Sube un archivo vía SFTP. |
| Descarga un archivo vía SFTP. |
| 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 | ✗ | ✓ |
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.jsConsideraciones 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=falsepara entornos de alta seguridadAislamiento 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
Original: zibdie/SSH-MCP-Server por Nour Zibdie
Fork de seguridad: marian-craciunescu
Soporte
This server cannot be installed
Maintenance
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
- FlicenseAqualityFmaintenanceA 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
- AlicenseAqualityCmaintenanceA secure remote server management tool based on MCP protocol, supporting SSH connections, command execution, and SFTP file transfers.20416MIT
- AlicenseBqualityAmaintenanceMCP server for managing SSH tunnels and remote hosts, offering tools for host inventory, file operations, security scanning, and SSH configuration.82MIT
- AlicenseAqualityBmaintenanceAn MCP server for SSH/SCP operations with passwordless authentication, enabling remote command execution, file transfer, and session management.1923MIT
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.
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
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