Skip to main content
Glama

SSH MCP Server (Secured)

npm version CI/CD License: MIT

Ein gesicherter Fork von zibdie/SSH-MCP-Server mit Befehls-Whitelist/Blacklist-Filterung, Unterstützung für Netzwerkgeräte und Bulk-Verbindungsverwaltung für sichere Remote-Serververwaltung über MCP (Model Context Protocol).

Hauptfunktionen

  • Einmalige Ausführung: ssh_run verbindet, führt einen Befehl aus und trennt die Verbindung in einem einzigen Tool-Aufruf – keine connectionId, die weitergegeben werden muss.

  • Befehls-Whitelist/Blacklist: Steuern Sie, welche Befehle ausgeführt werden können.

  • Erkennung gefährlicher Muster: Blockiert Fork-Bomben, Befehlsinjektion und destruktive Muster.

  • Unterstützung für Netzwerkgeräte: Cisco, Juniper, MikroTik, FortiGate, Palo Alto, Sophos mit persistenten Shell-Sitzungen und automatischer Pager-Unterdrückung.

  • Jump-Shell-Unterstützung: SSH zu einem Host und dann in eine verschachtelte CLI wechseln (Telnet zu einem Host, FreeSWITCH fs_cli usw.) – Befehle werden innerhalb der verschachtelten Shell ausgeführt, mit einer geordneten Fallback-Liste von Jump-Befehlen.

  • Bulk-Verbindungsverwaltung: Laden Sie Dutzende von Verbindungen aus CSV/JSON-Dateien.

  • Umgebungsvariablen-Anmeldedaten: Passwörter werden automatisch aus Umgebungsvariablen anhand der connectionId aufgelöst – keine Geheimnisse im Chat.

  • Multi-Verbindungs-Ausführung: Befehle gleichzeitig auf allen oder ausgewählten Verbindungen ausführen.

  • Verbindungs-Health-Überwachung: Keepalive-Verfolgung, Erkennung toter Verbindungen, automatische Bereinigung.

  • Konfigurierbare Sicherheitsrichtlinien: Über Konfigurationsdatei oder Umgebungsvariablen.

  • Audit-Protokollierung: Protokollieren Sie alle blockierten Befehlsversuche.

Related MCP server: SSH MCP Server

Installation

Schnelle Einrichtung (Empfohlen)

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

Manuelle Installation

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

Verwendung

1. Einzelne Verbindung

Verbinden Sie sich mit einem Host über ssh_connect. Sie müssen nur Host, Benutzername und connectionId angeben – das Passwort wird automatisch aus Umgebungsvariablen aufgelöst:

Connect to host 172.168.0.2 with user admin connectionId=router1

Der LLM ruft ssh_connect auf mit:

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

Kein Passwort im Tool-Aufruf. Der Server sucht automatisch ROUTER1_PASSWORD aus Umgebungsvariablen.

Konvention zur Auflösung von Anmeldedaten

Die connectionId wird in ein Umgebungsvariablen-Präfix umgewandelt: in Großbuchstaben, nicht-alphanumerische Zeichen werden durch _ ersetzt.

connectionId

Umgebungsvariable für Passwort

Umgebungsvariable für Enable-Passwort

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

Optional wird auch <PREFIX>_USERNAME aufgelöst, wenn kein Benutzername angegeben ist.

Legen Sie Anmeldedaten in Ihrer MCP-Konfiguration fest:

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

Anmeldedaten leben in der MCP-Konfiguration (oder werden über CI/CD, Vault usw. injiziert) und erscheinen nie im Chat oder in Tool-Aufrufen. Wenn ein Passwort explizit im Tool-Aufruf angegeben wird, hat es Vorrang vor der Umgebungsvariable.

SSH-Optionen für ältere Geräte

Wenn Sie sich mit älteren Geräten verbinden, die nicht standardmäßige Algorithmen erfordern (das Äquivalent zu ssh -o), verwenden Sie den Parameter sshOptions:

In natürlicher Sprache:

Connect to 10.0.0.1 port 2222 as user, connectionId old-switch, with KexAlgorithms +diffie-hellman-group-exchange-sha1 and 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"
  }
}

Dies entspricht:

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

Präfix mit + fügt einen Wert zu den ssh2-Standardwerten hinzu. Ohne + ersetzt der Wert die Standardwerte vollständig.

Option

SSH2-Äquivalent

Anwendungsfall

KexAlgorithms

algorithms.kex

Legacy-Schlüsselaustausch (z. B. diffie-hellman-group1-sha1)

HostKeyAlgorithms

algorithms.serverHostKey

Legacy-Hostschlüssel (z. B. ssh-rsa, ssh-dss)

Ciphers

algorithms.cipher

Legacy-Chiffren (z. B. aes128-cbc)

MACs

algorithms.hmac

Legacy-MACs (z. B. hmac-sha1)

sshOptions wird bei ssh_connect, ssh_connect_with_jump_command und über ssh_load_connections geladenen JSON-Dateien unterstützt.

Keyboard-interactive-Authentifizierung ist automatisch aktiviert (tryKeyboard: true). Ältere Geräte, die die Standard-Passwort-Authentifizierung ablehnen und keyboard-interactive erfordern, funktionieren ohne zusätzliche Konfiguration.

2. Bulk-Verbindungen aus Datei

Laden Sie mehrere Verbindungen aus einer CSV- oder JSON-Datei mit ssh_load_connections. Passwörter werden aus Umgebungsvariablen unter Verwendung derselben connectionId-Konvention aufgelöst:

CSV-Format (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

Keine Passwörter in der Datei. Der Server löst ROUTER1_PASSWORD, ROUTER2_PASSWORD, SERVER1_PASSWORD aus Umgebungsvariablen auf.

HINWEIS: CSV kann keine Objekte enthalten, daher müssen SSH-Optionen für ältere Geräte über einzelne Umgebungsvariablen oder in der JSON-Datei festgelegt werden.

JSON-Format (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"
    }
  }
]

Profile:

Definieren Sie wiederverwendbare Verbindungsprofile für die Verbindung mit demselben Gerätetyp mit ähnlichen Einstellungen (z. B. alle Cisco-Switches). Profile können Standard-SSH-Optionen für ältere Geräte enthalten, sodass Sie sie nicht in jeder Verbindung wiederholen müssen.

Auflösungspriorität: explizite Argumente > Profil-Umgebungsvariablen > connectionId-Umgebungsvariablen

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

UNTEN ist ein Beispiel, wie Profil-Umgebungsvariablen beim Laden von Verbindungen aus CSV/JSON aufgelöst werden. Der Wert PROFILE_CISCO_SSH_OPTIONS wird als JSON geparst und auf alle Verbindungen mit deviceType cisco angewendet.

Umgebungsvariablen-Beispiel

Feld

Wert

PROFILE_CISCO_USER

username

admin

PROFILE_CISCO_PASSWORD

password

secret123

PROFILE_CISCO_DEVICE_TYPE

deviceType

cisco

PROFILE_CISCO_SSH_OPTIONS

sshOptions (als JSON geparst)

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

Profil-spezifische Befehls-Whitelist (kommagetrennt oder JSON-Array)

show ospf neigh,show version

PROFILE_CISCO_BLACKLIST

Profil-spezifische Befehls-Blacklist (kommagetrennt oder JSON-Array)

show running config,conf t

PROFILE_CISCO_DISABLE_PAGER

Profil-spezifischer Pager-Umschalter (true/false)

false

ssh_connect host=10.0.0.1 profile=CISCO connectionId=SWITCH1

Befehlsfilterung pro Profil

Zusätzlich zur globalen SSH_WHITELIST / SSH_BLACKLIST kann jedes Profil seinen eigenen Befehlsfilter über PROFILE_<NAME>_WHITELIST und PROFILE_<NAME>_BLACKLIST tragen. Diese werden zur Ausführungszeit über den globalen Filter gelegt, für jede Verbindung, die mit diesem Profil geöffnet wird:

  • Profil-Blacklist blockiert immer – auch Befehle, die der globale Filter erlauben würde (z. B. show running config blockieren).

  • Profil-Whitelist erlaubt bestimmte Befehle wieder und wird, wenn vorhanden, maßgeblich: Alles, was nicht aufgeführt ist, wird blockiert (z. B. show ospf neigh erlauben, während die Blacklist den Rest weiterhin blockiert).

  • Bei direktem Konflikt gewinnt die Blacklist.

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 deaktiviert die Pager-Unterdrückung für Verbindungen, die dieses Profil verwenden, und überschreibt den globalen Standard SSH_DISABLE_PAGER.

Verwendung:

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

Hinweis: Sie können Passwörter weiterhin direkt in CSV/JSON angeben, wenn Sie es bevorzugen – die Umgebungsvariablen-Auflösung greift nur, wenn das Passwortfeld fehlt oder leer ist.

3. Netzwerkgerätetypen

Der Server unterstützt verschiedene Gerätetypen mit entsprechender Verbindungsbehandlung:

Gerätetyp

Verhalten

Anwendungsfall

linux

Standard-SSH-Exec-Modus (Standard)

Linux/Unix-Server

cisco

Persistente Shell, Enable-Modus-Unterstützung

Cisco IOS/IOS-XE-Router und -Switches

cisco_xe

Persistente Shell (terminal length 0)

Cisco IOS-XE

cisco_xr

Persistente Shell (terminal length 0)

Cisco IOS-XR

cisco_asa

Persistente Shell (terminal length 0)

Cisco ASA-Firewalls

cisco_nexus

Persistente Shell (terminal length 0)

Cisco Nexus (NX-OS)

juniper

Persistente Shell (set cli screen-length 0)

Juniper JunOS-Geräte

mikrotik

Persistente Shell

MikroTik RouterOS

fortinet

Persistente Shell (config system console / set output standard)

FortiGate / FortiOS-Firewalls

paloalto

Persistente Shell (set cli pager off)

Palo Alto PAN-OS-Firewalls

sophos

Persistente Shell (Pager zur Laufzeit automatisch behandelt)

Sophos XG/XGS (SFOS)-Firewalls

network

Generische persistente Shell

Andere Netzwerkgeräte

jump_shell

Persistente Shell + verschachtelte CLI

Wird intern von ssh_connect_with_jump_command verwendet

Netzwerkgeräte verwenden PTY-zugewiesene persistente Shell-Sitzungen anstelle von standardmäßigem exec(), da viele Netzwerkbetriebssysteme den SSH-Kanal nach jedem exec-Befehl schließen.

4. Einmaliger Befehl (ssh_run)

ssh_connect + ssh_execute + ssh_disconnect sind drei Tool-Aufrufe, und die mittleren beiden erfordern, dass das Modell eine generierte connectionId wörtlich kopiert. ssh_run fasst das in einen Aufruf zusammen:

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

Verbindet, führt den Befehl aus und schließt die Verbindung. Gibt die Befehlsausgabe zurück – keine connectionId, die verfolgt werden muss.

Bei einem Fehler bleibt die Verbindung geöffnet, sodass Sie einen anderen Befehl erneut versuchen können. Das Ergebnis ist ein strukturiertes Objekt (sowohl als JSON-Text als auch in structuredContent zurückgegeben):

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

Wiederholen Sie mit ssh_execute unter Verwendung dieser connectionId, dann ssh_disconnect. Verwaiste Verbindungen werden von SSH_IDLE_TIMEOUT (Standard: 120 s) bereinigt.

Profile, Whitelist/Blacklist, Host-Filter, Audit-Logging, Pager-Behandlung und Auslagerung großer Ausgaben verhalten sich genau wie bei ssh_connect + ssh_execute. Ein durch den Filter blockierter Befehl wird bevor eine SSH-Sitzung geöffnet wird, abgelehnt.

Erfolg ist definiert als: Der Befehl wurde ausgeführt und sein Exit-Code ist 0 oder nicht vorhanden. Netzwerkgeräte auf dem Pfad der persistenten Shell melden keine Exit-Codes, daher sind diese Befehle erfolgreich, sofern die Ausführung selbst nicht fehlschlägt. Unter Linux zählt ein Exit-Code ungleich Null als Fehler und hält die Verbindung offen.

Verschachtelte CLI mit Fallbacks (ssh_run_with_jump)

Gleicher Ein-Aufruf-Ablauf, aber es wird zuerst eine verschachtelte CLI betreten. jumpCommands ist eine Liste, die der Reihe nach versucht wird, bis eine die verschachtelte Eingabeaufforderung erreicht:

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

Wenn telnet lh die Eingabeaufforderung nicht erreicht, wird telnet 127.0.0.1 versucht. Jeder Versuch ist eine neue Verbindung, sodass ein halb offenes Telnet aus einem fehlgeschlagenen Versuch die nächste Verbindung nicht beschädigen kann. Wenn alle Kandidaten fehlschlagen, listet der Fehler auf, was jeder zurückgegeben hat.

Alle Kandidaten teilen sich ein jumpPromptPattern (direkt oder über preset bereitgestellt). Wenn Kandidaten unterschiedliche Prompt-Muster benötigen, verwenden Sie stattdessen ssh_connect_with_jump_command. PROFILE_<NAME>_JUMP_COMMAND liefert einen einzelnen Kandidaten, wenn jumpCommands weggelassen wird.

5. Ausführung auf mehreren Verbindungen

Führen Sie einen Befehl auf bestimmten Verbindungen mit ssh_execute_on_multiple aus:

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

Oder auf ALLEN Verbindungen:

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

6. Jump-Shell (verschachtelte CLI über SSH)

Verwenden Sie ssh_connect_with_jump_command, wenn Sie per SSH auf einen Host zugreifen und dann eine verschachtelte interaktive Shell betreten müssen, bevor Sie Befehle ausführen. Dies deckt Szenarien ab wie:

  • Telnet zu einem Topex-VoIP-Gateway von einem SSH-Jump-Host aus

  • FreeSWITCH fs_cli auf einem entfernten Server

  • Jede CLI, die eine interaktive Sitzung nach SSH erfordert

So funktioniert es:

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

Alle nachfolgenden ssh_execute-Befehle auf dieser connectionId werden innerhalb der verschachtelten Shell ausgeführt.

Topex-Gateway-Beispiel (mit Preset):

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

Das topex-Preset füllt automatisch jumpPromptPattern: "topexsw>\\s*$" und jumpExitCommand: "quit" aus. Sie müssen nur jumpCommand angeben.

Führen Sie dann Befehle innerhalb der Topex-CLI aus:

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

FreeSWITCH-Beispiel (Preset füllt alles aus):

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

Das freeswitch-Preset füllt automatisch jumpCommand: "fs_cli", jumpPromptPattern: "freeswitch@...>" und jumpExitCommand: "/exit" aus. Dann:

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

Vollständig benutzerdefiniert (kein Preset):

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

Integrierte Presets:

Preset

jumpCommand

Prompt-Muster

Exit-Befehl

freeswitch

fs_cli

freeswitch@...>

/exit

topex

(vom Benutzer)

topexsw>

quit

Presets können überschrieben werden – jeder explizit angegebene Parameter hat Vorrang.

Shell-Wiederherstellung: Wenn die Shell abbricht, öffnet ssh_execute die Shell automatisch neu und tritt erneut in die Jump-Shell ein.

Trennen: ssh_disconnect sendet den Exit-Befehl an die verschachtelte CLI, bevor die SSH-Verbindung geschlossen wird.

7. Protokollierung

Legen Sie die Protokollebene über eine Umgebungsvariable fest:

Variable

Werte

Standard

SSH_LOG_LEVEL

DEBUG, INFO, WARN, ERROR

INFO

SSH_LOG_FILE

Pfad zur Protokolldatei

(keine)

Protokollformat:

[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

Konfiguration

Umgebungsvariablen

Variable

Werte

Standard

Beschreibung

SSH_FILTER_MODE

whitelist, blacklist, disabled

blacklist

Befehlsfiltermodus

SSH_ALLOW_SUDO

true, false

true

Sudo-Befehle erlauben

SSH_LOG_BLOCKED

true, false

true

Blockierte Befehle an stderr protokollieren

SSH_MCP_CONFIG

Dateipfad

-

Pfad zur Konfigurations-JSON-Datei

SSH_WHITELIST

kommagetrennt oder JSON

-

Whitelist-Befehle überschreiben

SSH_BLACKLIST

kommagetrennt oder JSON

-

Blacklist-Befehle überschreiben

SSH_DANGEROUS_PATTERNS

JSON-Array

-

Gefährliche Regex-Muster überschreiben

SSH_LOG_LEVEL

DEBUG, INFO, WARN, ERROR

INFO

Protokollausführlichkeit

SSH_LOG_FILE

Pfad

-

In Datei protokollieren

SSH_HOST_FILTER_MODE

whitelist, blacklist, disabled

disabled

Host-Filtermodus

SSH_HOST_WHITELIST

kommagetrennte IPs

-

Whitelist der erlaubten Host-IPs

SSH_HOST_BLACKLIST

kommagetrennte IPs

-

Blacklist der erlaubten Host-IPs

SSH_IDLE_TIMEOUT

Sekunden

120

Leerlauf-Timeout der Verbindung

SSH_FAILED_CONNECTIONS_LOG

Dateipfad

./ssh-failed-connections.json

/var/log/ssh-failed.jsonl

SSH_AUDIT_ENABLED

true, false

true

Schreibt ein Pro-Befehl-Sitzungsaudit (Befehl + vollständige Ausgabe) in JSONL

SSH_AUDIT_DIR

Pfad

./audit

Verzeichnis für tägliche audit_YYYY-MM-DD.jsonl-Dateien

SSH_ENABLE_LARGE_OUTPUT

true, false

false

Lagert übermäßig große Befehlsausgaben an einen Upload-Endpunkt aus und gibt eine URI statt Inline-Text zurück

SSH_MAX_OUTPUT_LENGTH

Ganzzahl (Zeichen)

10000

Ausgabegrößen-Schwellenwert, ab dem die Ausgabe ausgelagert wird

SSH_FILE_UPLOAD_ENDPOINT

URL

-

POST-Ziel für große Ausgaben. Empfängt {content, filename}, muss {file_id, artifact_uri} zurückgeben

SSH_DISABLE_PAGER

true, false

true

Interaktive Pager (less/---(more)---) bei Shell und Exec unterdrücken

SSH_DISABLE_PAGER_CMD_<DEVICETYPE>

Zeichenfolge

gerätespezifischer Standard

Befehl zum Deaktivieren des Pagers für einen Gerätetyp überschreiben (z. B. SSH_DISABLE_PAGER_CMD_CISCO)

SSH_PAGER_REGEX

Regex-Zeichenfolge

eingebaut

Muster zum Erkennen einer Pager-Eingabeaufforderung überschreiben

SSH_PAGER_ADVANCE_KEY

Zeichenfolge

" " (Leerzeichen)

Taste zum Weiterblättern zur nächsten Pager-Seite

SSH_MAX_PAGER_PAGES

Ganzzahl

1000

Sicherheitsgrenze für automatisch umgeblätterte Seiten pro Befehl

Alle zusätzlichen Umgebungsvariablen, die der Konvention <CONNECTIONID>_PASSWORD folgen, werden automatisch zur Anmeldeinformationsauflösung verwendet (siehe Konvention zur Anmeldeinformationsauflösung).

MCP-Konfigurationsbeispiele

Host-Whitelist/Blacklist:

Blacklist-Modus mit benutzerdefinierten blockierten Befehlen:

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

Whitelist-Modus (streng – nur bestimmte Befehle erlauben):

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

Netzwerkoperationen mit Anmeldeinformations-Umgebungsvariablen:

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

Im Chat sagen Sie nun einfach connect to 172.168.0.2 as admin connectionId=router1 – keine Passwörter werden offengelegt.

Über npx (ohne globale Installation):

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

Konfigurationsdatei

Erstellen Sie config.json oder 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"
    ]
  }
}

Filtermodi

Blacklist-Modus (Standard)

Befehle in der Blacklist werden blockiert. Alles andere ist erlaubt. Unterstützt mehrwortige Einträge wie configure terminal und 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

Whitelist-Modus

Nur Befehle in der Whitelist sind erlaubt. Alles andere wird blockiert.

✓ 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

Gemischter Modus

Beide Listen sind gleichzeitig aktiv und der Filter ist standardmäßig verweigernd: Ein Befehl muss mit einem Whitelist-Eintrag übereinstimmen, um ausgeführt zu werden. Bei einem Konflikt gewinnt der längste übereinstimmende Eintrag, unabhängig davon, aus welcher Liste er stammt. Dadurch können Sie ein breites Präfix erlauben, eine gefährliche Teilmenge daraus herausschneiden und dann eine engere Ausnahme wieder zulassen.

Die Übereinstimmung erfolgt per Präfix: Ein Eintrag stimmt überein, wenn der Befehl ihm entspricht oder mit ihm beginnt, gefolgt von einem Leerzeichen, Tab oder Zeilenumbruch. Der Vergleich erfolgt kleingeschrieben und getrimmt.

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

Resultierende Entscheidungen:

✓ 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

Zwei Dinge sollten Sie wissen:

  • ls -lha ist erlaubt, aber ls -la nicht. Whitelist-Einträge sind wörtliche Präfixe, keine Muster. Im gemischten Modus ist alles blockiert, was Sie nicht explizit erlaubt haben. Listen Sie also die genauen Befehlsformen auf, die Sie ausführen möchten.

  • Exakte Gleichstände gehen an die Whitelist. Wenn dieselbe Zeichenfolge in beiden Listen steht, ist der Befehl erlaubt.

Nehmen Sie Ihre Pager-Deaktivierungsbefehle (terminal length 0, set cli screen-length 0) in die Whitelist auf. Der Server gibt sie selbst aus, wenn er eine Shell öffnet, und der gemischte Modus würde sie sonst blockieren.

Im Gegensatz zum Blacklist- und Whitelist-Modus untersucht der gemischte Modus nicht die einzelnen Segmente einer Pipe oder Kette – er gleicht nur die vollständige Befehlszeichenfolge ab. Der Schutz auf Segmentebene im gemischten Modus erfolgt über die Liste der gefährlichen Muster, die zuerst ausgeführt wird und nicht überschrieben werden kann:

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

Deaktivierter Modus

Keine Befehlsfilterung (mit Vorsicht verwenden).

Reihenfolge der Befehlsvalidierung

  1. Prüfen, ob Filterung deaktiviert ist

  2. sudo-Berechtigung prüfen

  3. Gefährliche Muster prüfen (Regex) — hat immer Vorrang, keine Whitelist kann dies überschreiben

  4. Im mixed-Modus: längste Übereinstimmung zwischen Whitelist und Blacklist über den gesamten Befehl; standardmäßig verweigern, wenn keines zutrifft. Endet hier.

  5. Gesamten Befehl gegen Blacklist prüfen (Mehrwort-Unterstützung)

  6. Basisbefehle aus Pipes/Ketten extrahieren

  7. Jeden Basisbefehl gegen Blacklist/Whitelist prüfen

  8. Profilspezifische (verbindungsspezifische) Whitelist/Blacklist zusätzlich zum globalen Ergebnis anwenden

Verwenden Sie ssh_get_command_filter, um die aktiven Regeln einzusehen und zu erfahren, warum ein bestimmter Befehl erlaubt oder blockiert würde.

Stabile Verbindungs-IDs

Wenn Sie keine connectionId an ssh_connect übergeben (oder default übergeben), generiert der Server eine stabile, strukturierte ID und gibt sie in der Verbindungsantwort zurück:

<IP>_YYYY_MM_DD_sessionid_<6 random chars>

Beispiel: 10_0_0_1_2026_06_08_sessionid_a1b9f3

Die Punkte der IP-Adresse werden durch _ ersetzt, sodass die ID sicher als Umgebungsvariablen-Präfix verwendet werden kann (für die <PREFIX>_PASSWORD-Auflösung) und als Dateiname. Erfassen Sie die zurückgegebene connectionId und verwenden Sie sie für nachfolgende ssh_execute- / ssh_disconnect-Aufrufe erneut.

Sitzungsprotokoll (Befehl + Ausgabe)

Jeder ausgeführte Befehl und seine Ausgabe werden als einzelne JSONL-Zeile in eine tagesbasierte Datei geschrieben, getrennt von Serverdiagnosen (SSH_LOG_FILE):

<SSH_AUDIT_DIR>/audit_YYYY-MM-DD.jsonl

Jeder Datensatz:

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

Deaktivieren mit SSH_AUDIT_ENABLED=false.

Auslagerung großer Ausgaben

Wenn die Ausgabe eines Befehls SSH_MAX_OUTPUT_LENGTH überschreitet und SSH_ENABLE_LARGE_OUTPUT=true gesetzt ist, wird die vollständige Ausgabe an SSH_FILE_UPLOAD_ENDPOINT per POST gesendet und der Aufrufer erhält einen kurzen Stub mit der zurückgegebenen artifact_uri und file_id sowie einer kleinen Vorschau — sodass ein riesiges show tech-support den Modellkontext nicht überflutet. Der Endpunkt empfängt { "content": "...", "filename": "..." } und muss { "file_id": "...", "artifact_uri": "..." } zurückgeben. Wenn der Endpunkt nicht gesetzt ist oder der Upload fehlschlägt, wird die Ausgabe inline als Fallback zurückgegeben.

Pager-Behandlung

Interaktive Pager (Linux less, Cisco/Juniper ---(more)---) würden sonst einen Befehl blockieren, bis er zeitlich ausläuft. Der Server behandelt dies auf zwei Arten:

  • Vorbeugung — beim Öffnen der Shell sendet er einen gerätegeeigneten Befehl zum Deaktivieren des Pagers (terminal length 0 für Cisco, set cli screen-length 0 für Juniper), und bei Linux-Exec setzt er SYSTEMD_PAGER=, PAGER=cat, GIT_PAGER=cat.

  • Erkennung — wenn weiterhin eine Pager-Eingabeaufforderung erscheint, blättert er automatisch weiter (sendet ein Leerzeichen, begrenzt durch SSH_MAX_PAGER_PAGES) bei Netzwerkgeräten oder sendet q zum Beenden eines interaktiven Linux-Pagers und entfernt dann die Pager-Artefakte aus der Ausgabe.

Global umschalten mit SSH_DISABLE_PAGER=false, pro Profil mit PROFILE_<NAME>_DISABLE_PAGER=false, Befehl pro Gerätetyp überschreiben mit SSH_DISABLE_PAGER_CMD_<DEVICETYPE> und Erkennung überschreiben mit SSH_PAGER_REGEX / SSH_PAGER_ADVANCE_KEY.

Gefährliche Muster

Diese Muster sind immer blockiert, unabhängig vom Filtermodus:

Muster

Beispiel

Risiko

Fork-Bombe

:(){ :|:& };:

Systemabsturz

Piped rm

find . | rm

Datenverlust

Verkettetes rm

ls && rm -rf /

Datenverlust

Geräteumleitung

> /dev/sda

Festplattenbeschädigung

Systemkonfiguration überschreiben

> /etc/passwd

Systemkompromittierung

Remote-Codeausführung

curl | bash

Beliebige Codeausführung

Rekursives chmod 777

chmod -R 777 /

Sicherheitskompromittierung

Verfügbare Werkzeuge

Einmaliger Befehl (empfohlen)

Werkzeug

Beschreibung

ssh_run

Verbinden, einen Befehl ausführen und die Verbindung bei Erfolg schließen — ein Aufruf, keine connectionId zum Verfolgen. Bei einem Fehler bleibt die Verbindung geöffnet und ihre connectionId wird zurückgegeben, sodass Sie einen anderen Befehl mit ssh_execute erneut versuchen können. Erforderlich: host, command.

ssh_run_with_jump

Wie ssh_run, wechselt jedoch zuerst in eine verschachtelte CLI. Nimmt jumpCommands als Liste entgegen und versucht sie der Reihe nach, bis die verschachtelte Eingabeaufforderung erreicht wird. Erforderlich: host, command.

Verbindungsverwaltung

Werkzeug

Beschreibung

ssh_connect

Öffnet eine dauerhafte Verbindung und gibt eine connectionId zurück. Passwort wird automatisch aus der Umgebungsvariablen <CONNECTIONID>_PASSWORD aufgelöst; unterstützt sshOptions für die Aushandlung von Legacy-Algorithmen.

ssh_connect_with_jump_command

Stellt eine SSH-Verbindung zu einem Host her und wechselt dann über einen einzelnen Sprungbefehl in eine verschachtelte CLI (telnet, fs_cli, usw.). Unterstützt Voreinstellungen. Verwenden Sie dies, wenn jeder Kandidat sein eigenes Eingabeaufforderungsmuster benötigt.

ssh_load_connections

Lädt Verbindungen aus einer CSV-/JSON-Datei (Anmeldeinformationen werden pro connectionId aus Umgebungsvariablen aufgelöst).

ssh_disconnect

Trennt eine Verbindung.

ssh_disconnect_all

Trennt alle Verbindungen.

Ausführung

Werkzeug

Beschreibung

ssh_execute

Führt einen Befehl auf einer bestehenden Verbindung aus. Erforderlich: command, connectionId.

ssh_execute_on_multiple

Führt einen Befehl auf ausgewählten Verbindungen aus (["*"] oder [] = alle). Läuft sequenziell.

Status und Introspection

Werkzeug

Beschreibung

ssh_get_command_filter

Zeigt den Befehlsfilter (Whitelist/Blacklist, global + pro Profil, mit Vorrangregeln) und den Hostfilter (erlaubte/blockierte Hosts), die für eine Verbindung gelten; optional kann geprüft werden, ob ein bestimmter Befehl erlaubt wäre.

ssh_list_connections

Listet aktive Verbindungen mit Status auf.

ssh_check_connections

Gesundheitsprüfung aller Verbindungen (Erkennung toter Sockets, Shell-Status).

ssh_failed_connections

Listet kürzlich fehlgeschlagene Verbindungsversuche auf (aus dem JSONL-Protokoll fehlgeschlagener Verbindungen).

Dateiübertragung (SFTP)

Werkzeug

Beschreibung

ssh_upload_file

Lädt eine Datei per SFTP hoch.

ssh_download_file

Lädt eine Datei per SFTP herunter.

ssh_list_files

Listet ein entferntes Verzeichnis per SFTP auf.

Beispiel-Workflow

Einzelner Befehl (ein Aufruf)

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

Einzelner Befehl in einer verschachtelten CLI (ein Aufruf)

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

Wiederholung nach einem Fehler

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

Flottenoperationen (dauerhafte Verbindungen)

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

Architekturhinweise

Shell-Puffer-Verwaltung

Der Puffer wird vor jedem Befehl geleert. Stabilitätserkennung verwendet einen Puffer, der 3 × 500 ms unverändert bleibt = Befehl abgeschlossen. Passwort-Eingabeaufforderungen werden in den letzten 200 Zeichen des Puffers erkannt.

Keepalive-System

SSH2 sendet Keepalives alle 10 Sekunden (keepaliveInterval: 10000). Nach 3 fehlgeschlagenen Keepalives schließt sich die Verbindung automatisch (keepaliveCountMax: 3). Ein benutzerdefiniertes Intervall protokolliert die Keepalive-Anzahl zum Debuggen.

Verbindungs-Gesundheitsüberwachung

Der Server erkennt tote Verbindungen (Socket zerstört), verfolgt den Shell-Status für Netzwerkgeräte, bereinigt tote Verbindungen automatisch und versucht bei Netzwerkgeräten, die Shell erneut zu öffnen, falls diese geschlossen wurde.

Sprung-Shell

Wenn ssh_connect_with_jump_command aufgerufen wird, führt der Server folgende Schritte aus: (1) Öffnen einer SSH-Verbindung, (2) Öffnen einer PTY-Shell, (3) Senden des Sprungbefehls (z. B. telnet lh), (4) Abfragen des Shell-Puffers alle 300 ms auf das erwartete Eingabeaufforderungs-Regex, (5) Markieren der Verbindung als jump_shell mit jumpShellActive: true. Beim Trennen wird der Beendigungsbefehl der verschachtelten CLI gesendet, bevor die SSH-Sitzung geschlossen wird. Bei der Shell-Wiederherstellung wird der Sprungbefehl automatisch erneut gesendet.

Auflösung von Anmeldeinformationen über Umgebungsvariablen

Wenn eine Verbindung erstellt wird (über ssh_connect oder ssh_load_connections) und das Passwort nicht angegeben ist, sucht der Server automatisch nach <PREFIX>_PASSWORD in den Umgebungsvariablen, wobei <PREFIX> die connectionId in Großbuchstaben ist, wobei nicht-alphanumerische Zeichen durch _ ersetzt werden. Dieselbe Konvention gilt für _ENABLE_PASSWORD und _USERNAME. Explizit angegebene Werte haben immer Vorrang.

Vergleich mit dem Original

Funktion

zibdie/SSH-MCP-Server

Dieser Fork

Basis-SSH/SFTP

Befehls-Whitelist

Befehls-Blacklist

Mehrwort-Blacklist-Einträge

Erkennung gefährlicher Muster

Audit-Protokollierung

Werkzeug zur Befehlsvalidierung

Unterstützung von Konfigurationsdateien

Netzwerkgerätetypen (Cisco, Juniper, MikroTik)

Cisco-Enable-Modus

Jump-Shell (verschachtelte CLI über SSH)

Massenverbindungen aus CSV/JSON

Ausführung über mehrere Verbindungen

Anmeldedaten aus Umgebungsvariablen

Überwachung der Verbindungsintegrität

Keepalive-Verfolgung

host/hostname-Kompatibilität

Entwicklung

# 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

Sicherheitshinweise

  • Standard ist der Blacklist-Modus – bietet Schutz und bleibt dennoch flexibel

  • Gefährliche Muster werden immer geprüft – auch im deaktivierten Modus

  • Audit-Protokollierung ist standardmäßig aktiviert – blockierte Versuche nachverfolgen

  • Sudo kann eingeschränkt werden – setzen Sie SSH_ALLOW_SUDO=false für Hochsicherheitsumgebungen

  • Isolation von Anmeldedaten – Passwörter werden über connectionId aus Umgebungsvariablen aufgelöst, nie im Chat eingegeben oder in Tool-Aufrufen sichtbar

Lizenz

MIT – siehe Datei LICENSE

Danksagungen

Support

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