ssh-mcp-server-secured
SSH MCP Server (Secured)
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_runverbindet, 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=router1Der 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 |
|
|
|
|
|
|
|
|
|
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-rsaPräfix mit + fügt einen Wert zu den ssh2-Standardwerten hinzu. Ohne + ersetzt der Wert die Standardwerte vollständig.
Option | SSH2-Äquivalent | Anwendungsfall |
|
| Legacy-Schlüsselaustausch (z. B. |
|
| Legacy-Hostschlüssel (z. B. |
|
| Legacy-Chiffren (z. B. |
|
| Legacy-MACs (z. B. |
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,server1Keine 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 ( | 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 configblockieren).Profil-Whitelist erlaubt bestimmte Befehle wieder und wird, wenn vorhanden, maßgeblich: Alles, was nicht aufgeführt ist, wird blockiert (z. B.
show ospf neigherlauben, 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 allHinweis: 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 |
| Standard-SSH-Exec-Modus (Standard) | Linux/Unix-Server |
| Persistente Shell, Enable-Modus-Unterstützung | Cisco IOS/IOS-XE-Router und -Switches |
| Persistente Shell ( | Cisco IOS-XE |
| Persistente Shell ( | Cisco IOS-XR |
| Persistente Shell ( | Cisco ASA-Firewalls |
| Persistente Shell ( | Cisco Nexus (NX-OS) |
| Persistente Shell ( | Juniper JunOS-Geräte |
| Persistente Shell | MikroTik RouterOS |
| Persistente Shell ( | FortiGate / FortiOS-Firewalls |
| Persistente Shell ( | Palo Alto PAN-OS-Firewalls |
| Persistente Shell (Pager zur Laufzeit automatisch behandelt) | Sophos XG/XGS (SFOS)-Firewalls |
| Generische persistente Shell | Andere Netzwerkgeräte |
| Persistente Shell + verschachtelte CLI | Wird intern von |
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_cliauf einem entfernten ServerJede 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>") → readyAlle 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 |
|
|
|
|
| (vom Benutzer) |
|
|
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 |
| DEBUG, INFO, WARN, ERROR | INFO |
| 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: router1Konfiguration
Umgebungsvariablen
Variable | Werte | Standard | Beschreibung |
|
|
| Befehlsfiltermodus |
|
|
| Sudo-Befehle erlauben |
|
|
| Blockierte Befehle an stderr protokollieren |
| Dateipfad | - | Pfad zur Konfigurations-JSON-Datei |
| kommagetrennt oder JSON | - | Whitelist-Befehle überschreiben |
| kommagetrennt oder JSON | - | Blacklist-Befehle überschreiben |
| JSON-Array | - | Gefährliche Regex-Muster überschreiben |
|
|
| Protokollausführlichkeit |
| Pfad | - | In Datei protokollieren |
| whitelist, blacklist, disabled | disabled | Host-Filtermodus |
| kommagetrennte IPs | - | Whitelist der erlaubten Host-IPs |
| kommagetrennte IPs | - | Blacklist der erlaubten Host-IPs |
| Sekunden | 120 | Leerlauf-Timeout der Verbindung |
| Dateipfad | ./ssh-failed-connections.json | /var/log/ssh-failed.jsonl |
|
|
| Schreibt ein Pro-Befehl-Sitzungsaudit (Befehl + vollständige Ausgabe) in JSONL |
| Pfad |
| Verzeichnis für tägliche |
|
|
| Lagert übermäßig große Befehlsausgaben an einen Upload-Endpunkt aus und gibt eine URI statt Inline-Text zurück |
| Ganzzahl (Zeichen) |
| Ausgabegrößen-Schwellenwert, ab dem die Ausgabe ausgelagert wird |
| URL | - | POST-Ziel für große Ausgaben. Empfängt |
|
|
| Interaktive Pager ( |
| Zeichenfolge | gerätespezifischer Standard | Befehl zum Deaktivieren des Pagers für einen Gerätetyp überschreiben (z. B. |
| Regex-Zeichenfolge | eingebaut | Muster zum Erkennen einer Pager-Eingabeaufforderung überschreiben |
| Zeichenfolge |
| Taste zum Weiterblättern zur nächsten Pager-Seite |
| 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 blacklistWhitelist-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 whitelistGemischter 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, pingResultierende 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 matchZwei Dinge sollten Sie wissen:
ls -lhaist erlaubt, aberls -lanicht. 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/iDeaktivierter Modus
Keine Befehlsfilterung (mit Vorsicht verwenden).
Reihenfolge der Befehlsvalidierung
Prüfen, ob Filterung deaktiviert ist
sudo-Berechtigung prüfen
Gefährliche Muster prüfen (Regex) — hat immer Vorrang, keine Whitelist kann dies überschreiben
Im
mixed-Modus: längste Übereinstimmung zwischen Whitelist und Blacklist über den gesamten Befehl; standardmäßig verweigern, wenn keines zutrifft. Endet hier.Gesamten Befehl gegen Blacklist prüfen (Mehrwort-Unterstützung)
Basisbefehle aus Pipes/Ketten extrahieren
Jeden Basisbefehl gegen Blacklist/Whitelist prüfen
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.jsonlJeder 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 0für Cisco,set cli screen-length 0für Juniper), und bei Linux-Exec setzt erSYSTEMD_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 sendetqzum 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 |
| Datenverlust |
Verkettetes rm |
| Datenverlust |
Geräteumleitung |
| Festplattenbeschädigung |
Systemkonfiguration überschreiben |
| Systemkompromittierung |
Remote-Codeausführung |
| Beliebige Codeausführung |
Rekursives chmod 777 |
| Sicherheitskompromittierung |
Verfügbare Werkzeuge
Einmaliger Befehl (empfohlen)
Werkzeug | Beschreibung |
| Verbinden, einen Befehl ausführen und die Verbindung bei Erfolg schließen — ein Aufruf, keine |
| Wie |
Verbindungsverwaltung
Werkzeug | Beschreibung |
| Öffnet eine dauerhafte Verbindung und gibt eine |
| 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. |
| Lädt Verbindungen aus einer CSV-/JSON-Datei (Anmeldeinformationen werden pro |
| Trennt eine Verbindung. |
| Trennt alle Verbindungen. |
Ausführung
Werkzeug | Beschreibung |
| Führt einen Befehl auf einer bestehenden Verbindung aus. Erforderlich: |
| Führt einen Befehl auf ausgewählten Verbindungen aus ( |
Status und Introspection
Werkzeug | Beschreibung |
| 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. |
| Listet aktive Verbindungen mit Status auf. |
| Gesundheitsprüfung aller Verbindungen (Erkennung toter Sockets, Shell-Status). |
| Listet kürzlich fehlgeschlagene Verbindungsversuche auf (aus dem JSONL-Protokoll fehlgeschlagener Verbindungen). |
Dateiübertragung (SFTP)
Werkzeug | Beschreibung |
| Lädt eine Datei per SFTP hoch. |
| Lädt eine Datei per SFTP herunter. |
| 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 | ✗ | ✓ |
| ✗ | ✓ |
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.jsSicherheitshinweise
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=falsefür HochsicherheitsumgebungenIsolation 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
Original: zibdie/SSH-MCP-Server von Nour Zibdie
Sicherheits-Fork: marian-craciunescu
Support
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