ssh-mcp-server-secured
SSH MCP Server (Secured)
zibdie/SSH-MCP-Server의 보안 강화 포크로, 명령어 화이트리스트/블랙리스트 필터링, 네트워크 장치 지원, 대량 연결 관리를 통해 MCP(Model Context Protocol)로 안전하게 원격 서버를 관리할 수 있습니다.
주요 기능
원샷 실행:
ssh_run은 단일 도구 호출로 연결, 명령 실행, 연결 종료를 모두 처리합니다 — 전달할 connectionId가 없습니다명령어 화이트리스트/블랙리스트: 실행할 수 있는 명령어를 제어합니다
위험 패턴 탐지: 포크 폭탄, 명령어 주입, 파괴적 패턴을 차단합니다
네트워크 장치 지원: Cisco, Juniper, MikroTik, FortiGate, Palo Alto, Sophos — 지속형 셸 세션과 자동 페이저 억제 지원
점프 셸 지원: SSH로 호스트에 접속한 후 중첩 CLI(telnet으로 호스트 접속, FreeSWITCH fs_cli 등)로 진입합니다 — 명령은 중첩 셸 안에서 실행되며, 순서가 지정된 점프 명령 폴백 목록을 사용합니다
대량 연결 관리: CSV/JSON 파일에서 수십 개의 연결을 불러옵니다
환경 변수 자격 증명: 비밀번호는 connectionId를 기준으로 환경 변수에서 자동 확인됩니다 — 채팅에 비밀 노출 없음
다중 연결 실행: 모든 또는 선택된 연결에서 동시에 명령을 실행합니다
연결 상태 모니터링: Keepalive 추적, 끊어진 연결 감지, 자동 정리
구성 가능한 보안 정책: 구성 파일 또는 환경 변수를 통해 설정
감사 로깅: 차단된 모든 명령 시도를 로깅합니다
Related MCP server: SSH MCP Server
설치
빠른 설정 (권장)
# Add to Claude CLI
claude mcp add ssh-mcp-secured npx '@marian-craciunescu/ssh-mcp-server-secured@latest'수동 설치
npm install -g @marian-craciunescu/ssh-mcp-server-secured{
"mcpServers": {
"ssh-mcp-secured": {
"command": "ssh-mcp-server-secured"
}
}
}사용법
1. 단일 연결
ssh_connect를 사용하여 호스트에 연결합니다. host, username, connectionId만 제공하면 됩니다. 비밀번호는 환경 변수에서 자동으로 확인됩니다:
Connect to host 172.168.0.2 with user admin connectionId=router1LLM은 ssh_connect를 다음과 같이 호출합니다:
{
"host": "172.168.0.2",
"username": "admin",
"deviceType": "cisco",
"connectionId": "router1"
}도구 호출에 비밀번호가 없습니다. 서버는 환경 변수에서 ROUTER1_PASSWORD를 자동으로 찾습니다.
자격 증명 확인 규칙
connectionId는 환경 변수 접두사로 변환됩니다. 대문자로 바뀌고, 영숫자가 아닌 문자는 _로 대체됩니다.
connectionId | 비밀번호용 환경 변수 | enable 비밀번호용 환경 변수 |
|
|
|
|
|
|
|
|
|
선택적으로 username이 제공되지 않은 경우 <PREFIX>_USERNAME도 확인됩니다.
MCP 구성에 자격 증명을 설정하세요:
{
"mcpServers": {
"ssh-mcp-secured": {
"command": "ssh-mcp-server-secured",
"env": {
"SSH_FILTER_MODE": "blacklist",
"ROUTER1_PASSWORD": "admin123",
"ROUTER1_ENABLE_PASSWORD": "enable123",
"SERVER1_PASSWORD": "rootpass",
"SERVER1_USERNAME": "root"
}
}
}
}자격 증명은 MCP 구성에 저장되며(또는 CI/CD, 볼트 등을 통해 주입되며) 채팅이나 도구 호출에 절대 나타나지 않습니다. 도구 호출에서 비밀번호가 명시적으로 제공되면 환경 변수보다 우선합니다.
레거시 장치용 SSH 옵션
기본값이 아닌 알고리즘(ssh -o에 해당)이 필요한 구형 장치에 연결할 때는 sshOptions 매개변수를 사용하세요:
자연어로는:
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"
}
}이는 다음 명령과 동일합니다:
ssh -p 2222 admin@10.0.0.1 -o KexAlgorithms=+diffie-hellman-group-exchange-sha1 -o HostKeyAlgorithms=+ssh-rsa값 앞에 +를 붙이면 ssh2 기본값에 추가됩니다. + 없이 값을 지정하면 기본값을 완전히 대체합니다.
Option | SSH2 equivalent | 사용 사례 |
|
| 레거시 키 교환 (예: |
|
| 레거시 호스트 키 (예: |
|
| 레거시 암호화 (예: |
|
| 레거시 MAC (예: |
sshOptions는 ssh_connect, ssh_connect_with_jump_command, 그리고 ssh_load_connections로 불러오는 JSON 파일에서 지원됩니다.
키보드 인터랙티브 인증이 자동으로 활성화됩니다(tryKeyboard: true). 표준 비밀번호 인증을 거부하고 keyboard-interactive를 요구하는 레거시 장치는 추가 구성 없이 작동합니다.
2. 파일에서 대량 연결
ssh_load_connections를 사용하여 CSV 또는 JSON 파일에서 여러 연결을 불러옵니다. 비밀번호는 동일한 connectionId 규칙을 사용하여 환경 변수에서 확인됩니다:
CSV 형식 (connections.csv):
host,username,port,deviceType,connectionId
172.168.0.2,admin,22,cisco,router1
10.1.2.15,noc,22,cisco,router2
192.168.1.1,root,22,linux,server1파일에는 비밀번호가 없습니다. 서버는 환경 변수에서 ROUTER1_PASSWORD, ROUTER2_PASSWORD, SERVER1_PASSWORD를 확인합니다.
참고: CSV는 객체를 담을 수 없으므로 레거시 장치용 SSH 옵션은 개별 환경 변수 또는 JSON 파일로 설정해야 합니다.
JSON 형식 (connections.json):
[
{
"host": "172.168.0.2",
"username": "admin",
"deviceType": "cisco",
"connectionId": "router1"
},
{
"host": "10.1.2.15",
"username": "noc",
"deviceType": "cisco",
"connectionId": "router2",
"sshOptions": {
"KexAlgorithms": "+diffie-hellman-group-exchange-sha1",
"HostKeyAlgorithms": "+ssh-rsa"
}
}
]프로필:
유사한 설정으로 동일한 유형의 장치(예: 모든 Cisco 스위치)에 연결하기 위한 재사용 가능한 연결 프로필을 정의합니다. 프로필에는 레거시 장치용 기본 SSH 옵션을 포함할 수 있으므로 모든 연결에서 반복할 필요가 없습니다.
해석 우선순위: 명시적 인자 > 프로필 환경 변수 > connectionId 환경 변수
export PROFILE_CISCO_USER=admin
export PROFILE_CISCO_PASSWORD=secret123
export PROFILE_CISCO_DEVICE_TYPE=cisco
export PROFILE_CISCO_PORT=2222
export PROFILE_CISCO_SSH_OPTIONS='{"KexAlgorithms":"+diffie-hellman-group-exchange-sha1","HostKeyAlgorithms":"+ssh-rsa"}'아래는 CSV/JSON에서 연결을 불러올 때 프로필 환경 변수가 해석되는 방식의 예입니다. PROFILE_CISCO_SSH_OPTIONS 값은 JSON으로 파싱되어 deviceType이 cisco인 모든 연결에 적용됩니다.
환경 변수 예시 | 필드 | 값 |
PROFILE_CISCO_USER | username | admin |
PROFILE_CISCO_PASSWORD | password | secret123 |
PROFILE_CISCO_DEVICE_TYPE | deviceType | cisco |
PROFILE_CISCO_SSH_OPTIONS | sshOptions (JSON으로 파싱됨) | {"KexAlgorithms":"+diffie-hellman-group-exchange-sha1","HostKeyAlgorithms":"+ssh-rsa"} |
PROFILE_CISCO_JUMP_COMMAND | jumpCommand | telnet lh |
PROFILE_CISCO_PRESET | preset | topex |
PROFILE_CISCO_PORT | port | 2222 |
PROFILE_CISCO_WHITELIST | 프로필별 명령어 화이트리스트 (쉼표로 구분 또는 JSON 배열) | show ospf neigh,show version |
PROFILE_CISCO_BLACKLIST | 프로필별 명령어 블랙리스트 (쉼표로 구분 또는 JSON 배열) | show running config,conf t |
PROFILE_CISCO_DISABLE_PAGER | 프로필별 페이저 토글 ( | false |
ssh_connect host=10.0.0.1 profile=CISCO connectionId=SWITCH1"
프로필별 명령어 필터링
전역 SSH_WHITELIST / SSH_BLACKLIST 외에도 각 프로필은 PROFILE_<NAME>_WHITELIST 및 PROFILE_<NAME>_BLACKLIST를 통해 자체 명령어 필터를 가질 수 있습니다. 이 필터는 해당 프로필로 열린 모든 연결에 대해 실행 시 전역 필터 위에 계층적으로 적용됩니다:
프로필 블랙리스트는 항상 차단합니다 — 전역 필터가 허용하는 명령어도 차단합니다 (예:
show running config차단).프로필 화이트리스트는 특정 명령어를 다시 허용하며, 존재하는 경우 우선적으로 적용됩니다: 목록에 없는 것은 모두 차단됩니다 (예:
show ospf neigh허용, 블랙리스트는 나머지를 계속 차단).직접 충돌 시 블랙리스트가 우선합니다.
export PROFILE_ROUTERS_BLACKLIST="show running config,conf t,configure terminal"
export PROFILE_ROUTERS_WHITELIST="show ospf neigh,show version,show ip interface brief"ssh_connect host=10.0.0.1 profile=ROUTERS connectionId=router1
# show ospf neigh → allowed (profile whitelist)
# show running config → blocked (profile blacklist)PROFILE_<NAME>_DISABLE_PAGER=false는 해당 프로필을 사용하는 연결에서 페이저 억제를 끄며, 전역 SSH_DISABLE_PAGER 기본값을 재정의합니다.
사용법:
Load connections from /path/to/connections.csv and connect to all참고: 원하는 경우 CSV/JSON에 비밀번호를 직접 제공할 수도 있습니다. 환경 변수 해석은 비밀번호 필드가 없거나 비어 있을 때만 적용됩니다.
3. 네트워크 장치 유형
서버는 적절한 연결 처리를 통해 다양한 장치 유형을 지원합니다:
장치 유형 | 동작 | 사용 사례 |
| 표준 SSH exec 모드 (기본값) | Linux/Unix 서버 |
| 지속형 셸, enable 모드 지원 | Cisco IOS/IOS-XE 라우터 및 스위치 |
| 지속형 셸 ( | Cisco IOS-XE |
| 지속형 셸 ( | Cisco IOS-XR |
| 지속형 셸 ( | Cisco ASA 방화벽 |
| 지속형 셸 ( | Cisco Nexus (NX-OS) |
| 지속형 셸 ( | Juniper JunOS 장치 |
| 지속형 셸 | MikroTik RouterOS |
| 지속형 셸 ( | FortiGate / FortiOS 방화벽 |
| 지속형 셸 ( | Palo Alto PAN-OS 방화벽 |
| 지속형 셸 (런타임 시 페이저 자동 처리) | Sophos XG/XGS (SFOS) 방화벽 |
| 일반 지속형 셸 | 기타 네트워크 장치 |
| 지속형 셸 + 중첩 CLI |
|
네트워크 장치는 표준 exec() 대신 PTY가 할당된 지속형 셸 세션을 사용합니다. 많은 네트워크 운영 체제가 각 exec 명령 후 SSH 채널을 닫기 때문입니다.
4. 원샷 명령 (ssh_run)
ssh_connect + ssh_execute + ssh_disconnect는 세 번의 도구 호출이며, 가운데 두 호출은 모델이 생성된 connectionId를 그대로 복사해야 합니다. ssh_run은 이를 한 번의 호출로 줄입니다:
{
"host": "10.1.2.15",
"profile": "ROUTERS",
"command": "show version"
}연결하고, 명령을 실행하고, 연결을 닫습니다. 명령 출력을 반환하므로 추적할 connectionId가 없습니다.
실패 시 연결은 유지된 상태로 열려 있으므로 다른 명령을 재시도할 수 있습니다. 결과는 구조화된 객체입니다(JSON 텍스트와 structuredContent 모두로 반환됨):
{
"status": "error",
"connectionId": "10_1_2_15_2026_08_12_sessionid_a1b2c3",
"command": "show bogus",
"error": "Command exited with code 2",
"exitCode": 2,
"output": "% Invalid input detected",
"retry": "The SSH connection is still open. Call ssh_execute with this connectionId to run a different command, then ssh_disconnect when finished."
}해당 connectionId로 ssh_execute를 사용하여 재시도한 다음 ssh_disconnect를 호출하세요. 유기된 연결은 SSH_IDLE_TIMEOUT(기본값 120초)에 의해 정리됩니다.
프로필, 화이트리스트/블랙리스트, 호스트 필터, 감사 로깅, 페이저 처리, 대용량 출력 오프로드는 모두 ssh_connect + ssh_execute와 동일하게 동작합니다. 필터에 차단된 명령은 SSH 세션이 열리기 전에 거부됩니다.
성공의 정의: 명령이 실행되었고 종료 코드가 0이거나 없음입니다. 영구 셸 경로의 네트워크 장치는 종료 코드를 보고하지 않으므로, 실행 자체가 실패하지 않는 한 해당 명령은 성공으로 간주됩니다. Linux에서 0이 아닌 종료 코드는 실패로 간주되며 연결이 유지됩니다.
중첩 CLI 및 폴백(ssh_run_with_jump)
동일한 단일 호출 흐름이지만 먼저 중첩 CLI에 진입합니다. jumpCommands는 중첩 프롬프트에 도달할 때까지 순서대로 시도되는 목록입니다:
{
"host": "10.0.0.1",
"username": "admin",
"preset": "topex",
"jumpCommands": ["telnet lh", "telnet 127.0.0.1"],
"command": "view portsoncard *"
}telnet lh가 프롬프트에 도달하지 못하면 telnet 127.0.0.1이 시도됩니다. 각 시도는 새로운 연결이므로, 실패한 시도의 반쯤 열린 telnet이 다음 시도를 손상시킬 수 없습니다. 모든 후보가 실패하면 오류에 각 후보가 반환한 내용이 나열됩니다.
모든 후보는 하나의 jumpPromptPattern(직접 제공하거나 preset을 통해 제공)을 공유합니다. 후보마다 다른 프롬프트 패턴이 필요한 경우 ssh_connect_with_jump_command를 대신 사용하세요. PROFILE_<NAME>_JUMP_COMMAND는 jumpCommands가 생략된 경우 단일 후보를 제공합니다.
5. 여러 연결에서 실행
ssh_execute_on_multiple을 사용하여 특정 연결에서 명령을 실행합니다:
{
"command": "show version",
"connectionIds": ["router1", "router2", "switch1"]
}또는 모든 연결에서 실행:
{
"command": "show ip interface brief",
"connectionIds": ["*"]
}6. 점프 셸(SSH를 통한 중첩 CLI)
호스트에 SSH로 접속한 후 명령을 실행하기 전에 중첩 대화형 셸에 진입해야 하는 경우 ssh_connect_with_jump_command를 사용하세요. 이는 다음과 같은 시나리오를 다룹니다:
SSH 점프 호스트에서 Topex VoIP 게이트웨이로 Telnet
원격 서버의 FreeSWITCH
fs_cliSSH 후 대화형 세션이 필요한 모든 CLI
작동 방식:
SSH → open shell → send jump command (e.g. "telnet lh") → wait for nested prompt (e.g. "topexsw>") → ready해당 connectionId에 대한 모든 후속 ssh_execute 명령은 중첩 셸 내에서 실행됩니다.
Topex 게이트웨이 예시(프리셋 사용):
{
"host": "10.0.0.1",
"username": "admin",
"connectionId": "topex1",
"preset": "topex",
"jumpCommand": "telnet lh"
}topex 프리셋은 jumpPromptPattern: "topexsw>\\s*$"와 jumpExitCommand: "quit"를 자동으로 채웁니다. jumpCommand만 제공하면 됩니다.
그런 다음 Topex CLI 내에서 명령을 실행합니다:
{
"command": "view portsoncard *",
"connectionId": "topex1"
}FreeSWITCH 예시(프리셋이 모든 것을 채움):
{
"host": "10.0.0.5",
"username": "root",
"connectionId": "fs1",
"preset": "freeswitch"
}freeswitch 프리셋은 jumpCommand: "fs_cli", jumpPromptPattern: "freeswitch@...>", jumpExitCommand: "/exit"를 자동으로 채웁니다. 그런 다음:
{
"command": "sofia status",
"connectionId": "fs1"
}완전 사용자 지정(프리셋 없음):
{
"host": "10.0.0.1",
"username": "admin",
"connectionId": "custom1",
"jumpCommand": "telnet 192.168.1.100",
"jumpPromptPattern": ">\\s*$",
"jumpExitCommand": "quit",
"jumpReadyTimeout": 8000
}내장 프리셋:
프리셋 | jumpCommand | 프롬프트 패턴 | 종료 명령 |
|
|
|
|
| (사용자 제공) |
|
|
프리셋은 재정의할 수 있습니다 — 명시적으로 제공된 매개변수가 우선합니다.
셸 복구: 셸이 끊기면 ssh_execute가 자동으로 셸을 다시 열고 점프 셸에 다시 진입합니다.
연결 해제: ssh_disconnect는 SSH 연결을 닫기 전에 중첩 CLI에 종료 명령을 정상적으로 보냅니다.
7. 로깅
환경 변수를 통해 로그 수준을 설정합니다:
변수 | 값 | 기본값 |
| DEBUG, INFO, WARN, ERROR | INFO |
| 로그 파일 경로 | (없음) |
로그 형식:
[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구성
환경 변수
변수 | 값 | 기본값 | 설명 |
|
|
| 명령 필터링 모드 |
|
|
| sudo 명령 허용 |
|
|
| 차단된 명령을 stderr에 기록 |
| 파일 경로 | - | 구성 JSON 파일 경로 |
| 쉼표로 구분 또는 JSON | - | 화이트리스트 명령 재정의 |
| 쉼표로 구분 또는 JSON | - | 블랙리스트 명령 재정의 |
| JSON 배열 | - | 위험한 정규식 패턴 재정의 |
|
|
| 로그 상세 수준 |
| 경로 | - | 파일에 로그 기록 |
| whitelist, blacklist, disabled | disabled | 호스트 필터링 모드 |
| 쉼표로 구분된 IP | - | 허용된 호스트 IP 화이트리스트 |
| 쉼표로 구분된 IP | - | 허용된 호스트 IP 블랙리스트 |
| 초 | 120 | 유휴 연결 시간 초과 |
| 파일 경로 | ./ssh-failed-connections.json | /var/log/ssh-failed.jsonl |
|
|
| 명령별 세션 감사(명령 + 전체 출력)를 JSONL에 기록 |
| 경로 |
| 일별 |
|
|
| 과도하게 큰 명령 출력을 업로드 엔드포인트로 오프로드하고 인라인 텍스트 대신 URI를 반환 |
| 정수(문자 수) |
| 출력이 오프로드되는 크기 임계값 |
| URL | - | 대용량 출력용 POST 대상. |
|
|
| 셸 및 exec에서 대화형 페이저( |
| 문자열 | 장치 유형별 기본값 | 장치 유형에 대한 페이저 비활성화 명령 재정의(예: |
| 정규식 문자열 | 내장 | 페이저 프롬프트 감지에 사용되는 패턴 재정의 |
| 문자열 |
| 다음 페이저 페이지로 이동하기 위해 전송되는 키 |
| 정수 | 1000 | 명령당 자동 페이지 전환 페이지 수 안전 상한 |
<CONNECTIONID>_PASSWORD 규칙을 따르는 추가 환경 변수는 자격 증명 확인에 자동으로 사용됩니다(자격 증명 확인 규칙 참조).
MCP 구성 예시
호스트 화이트리스트/블랙리스트:
사용자 지정 차단 명령이 있는 블랙리스트 모드:
{
"ssh_mcp": {
"command": "ssh-mcp-server-secured",
"args": [],
"env": {
"SSH_FILTER_MODE": "blacklist",
"SSH_ALLOW_SUDO": "true",
"SSH_LOG_BLOCKED": "true",
"SSH_BLACKLIST": "rm,rmdir,mkfs,fdisk,shutdown,reboot,halt,poweroff,passwd,useradd,userdel,iptables,crontab,conf t,configure terminal"
}
}
}화이트리스트 모드(엄격 — 특정 명령만 허용):
{
"ssh_mcp": {
"command": "ssh-mcp-server-secured",
"args": [],
"env": {
"SSH_FILTER_MODE": "whitelist",
"SSH_ALLOW_SUDO": "false",
"SSH_LOG_BLOCKED": "true",
"SSH_WHITELIST": "ls,cat,grep,tail,head,df,du,free,uptime,ps,systemctl,journalctl,docker,kubectl,ping,curl,dig,ss,netstat,show,display"
}
}
}자격 증명 환경 변수가 있는 네트워크 작업:
{
"ssh_mcp": {
"command": "ssh-mcp-server-secured",
"args": [],
"env": {
"SSH_FILTER_MODE": "blacklist",
"SSH_ALLOW_SUDO": "true",
"SSH_LOG_LEVEL": "DEBUG",
"SSH_BLACKLIST": "conf t,configure terminal,rm,shutdown,reboot",
"ROUTER1_PASSWORD": "admin123",
"ROUTER1_ENABLE_PASSWORD": "enable123",
"ROUTER2_PASSWORD": "pass123",
"SERVER1_PASSWORD": "pass1234"
}
}
}이제 채팅에서 connect to 172.168.0.2 as admin connectionId=router1이라고 말하기만 하면 됩니다 — 비밀번호가 노출되지 않습니다.
npx 사용(전역 설치 없음):
{
"ssh_mcp": {
"command": "npx",
"args": ["@marian-craciunescu/ssh-mcp-server-secured"],
"env": {
"SSH_FILTER_MODE": "blacklist",
"SSH_ALLOW_SUDO": "true"
}
}
}구성 파일
config.json 또는 ssh-mcp-config.json 생성:
{
"commandFilter": {
"mode": "whitelist",
"allowSudo": false,
"logBlocked": true,
"whitelist": [
"ls", "cat", "grep", "df", "ps", "systemctl", "docker", "show", "ping"
],
"blacklist": [
"rm", "shutdown", "reboot", "passwd", "conf t", "configure terminal"
],
"dangerousPatterns": [
";\\s*rm\\s+-rf",
"curl.*\\|\\s*bash"
]
}
}필터 모드
블랙리스트 모드(기본값)
블랙리스트의 명령은 차단됩니다. 그 외의 모든 것은 허용됩니다. configure terminal 및 conf t와 같은 다중 단어 항목을 지원합니다.
✓ ls -la
✓ docker ps
✓ show ip interface brief
✗ rm -rf /tmp/files → Blocked: 'rm' is in blacklist
✗ configure terminal → Blocked: 'configure terminal' is in blacklist
✗ shutdown now → Blocked: 'shutdown' is in blacklist화이트리스트 모드
화이트리스트의 명령만 허용됩니다. 그 외의 모든 것은 차단됩니다.
✓ ls -la → Allowed: 'ls' is whitelisted
✓ show version → Allowed: 'show' is whitelisted
✗ vim /etc/hosts → Blocked: 'vim' not in whitelist
✗ make install → Blocked: 'make' not in whitelist혼합 모드
두 목록이 동시에 활성화되며 필터는 기본 거부 방식입니다: 명령이 실행되려면 화이트리스트 항목과 일치해야 합니다. 충돌 시 가장 긴 일치 항목이 우선하며, 어느 목록에서 왔든 관계없습니다. 이를 통해 광범위한 접두사를 허용하고, 그 안에서 위험한 하위 집합을 제외한 다음, 더 좁은 예외를 다시 허용할 수 있습니다.
일치는 접두사 기준입니다: 항목은 명령이 항목과 같거나, 공백, 탭 또는 줄바꿈이 뒤따르는 접두사로 시작할 때 일치합니다. 비교는 소문자 변환 및 공백 제거 후 수행됩니다.
SSH_FILTER_MODE=mixed
SSH_WHITELIST=show, show running-config interface, show running-config | include, ping -c , ls -lha, terminal length 0
SSH_BLACKLIST=show running-config, conf t, configure terminal, reload, rm, shutdown, ping결과 결정:
✓ show version → 'show' (4) beats nothing
✓ show interfaces terse → 'show' (4) beats nothing
✗ show running-config → 'show running-config' (19) beats 'show' (4)
✓ show running-config interface Gi0/1 → 'show running-config interface' (29) beats 'show running-config' (19)
✓ show running-config | include hostname → 'show running-config | include' (29) beats 'show running-config' (19)
✓ ping -c 4 8.8.8.8 → 'ping -c' (7) beats 'ping' (4)
✗ ping 8.8.8.8 → only 'ping' (4) matches, and it is blacklisted
✓ terminal length 0 → whitelisted, so the server can disable its own pager
✓ ls -lha → exact whitelist entry
✗ ls -la → matches NEITHER list → blocked by deny-by-default
✗ reload → blacklisted, no whitelist match
✗ rm -rf /tmp/x → 'rm' (2) blacklisted, no whitelist match알아야 할 두 가지 사항:
ls -lha는 허용되지만ls -la는 허용되지 않습니다. 화이트리스트 항목은 리터럴 접두사이지 패턴이 아닙니다. 혼합 모드에서는 명시적으로 허용하지 않은 모든 것이 차단되므로, 실행하려는 정확한 명령 형식을 나열하세요.정확히 동일한 경우 화이트리스트가 우선합니다. 동일한 문자열이 두 목록에 모두 있으면 명령이 허용됩니다.
페이저 비활성화 명령(terminal length 0, set cli screen-length 0)을 화이트리스트에 포함하세요. 서버는 셸을 열 때 자체적으로 이를 실행하며, 혼합 모드에서는 그렇지 않으면 차단됩니다.
블랙리스트 및 화이트리스트 모드와 달리 혼합 모드는 파이프 또는 체인의 개별 세그먼트를 검사하지 않습니다 — 전체 명령 문자열만 일치시킵니다. 혼합 모드의 세그먼트 수준 보호는 먼저 실행되며 재정의할 수 없는 위험 패턴 목록에서 제공됩니다:
✗ show version | rm -rf / → Blocked: dangerous pattern /\|\s*rm/i비활성화 모드
명령 필터링 없음(주의해서 사용).
명령 검증 순서
필터링 비활성화 여부 확인
sudo 권한 확인
위험 패턴(정규식) 확인 — 항상 우선 적용되며, 어떤 화이트리스트도 이를 무시할 수 없음
mixed모드: 전체 명령에 대해 화이트리스트와 블랙리스트 중 더 긴 매치를 선택하고, 둘 다 매치되지 않으면 기본적으로 거부. 여기서 종료.블랙리스트에 대해 전체 명령 확인(다중 단어 지원)
파이프/체인에서 기본 명령 추출
각 기본 명령을 블랙리스트/화이트리스트에 대해 확인
전역 결과 위에 프로필별(연결별) 화이트리스트/블랙리스트 적용
ssh_get_command_filter를 사용하여 활성 규칙을 확인하고 특정 명령이 허용되거나 차단되는 이유를 물어볼 수 있습니다.
안정적인 연결 ID
ssh_connect에 connectionId를 전달하지 않거나(또는 default를 전달) 서버는 안정적이고 구조화된 ID를 생성하여 연결 응답에 반환합니다:
<IP>_YYYY_MM_DD_sessionid_<6 random chars>예: 10_0_0_1_2026_06_08_sessionid_a1b9f3
IP의 점은 _로 대체되어 ID가 환경 변수 접두사(<PREFIX>_PASSWORD 해석용)와 파일 이름으로 사용하기에 안전합니다. 반환된 connectionId를 캡처하여 이후 ssh_execute / ssh_disconnect 호출에 재사용하십시오.
세션 감사(명령 + 출력)
실행된 모든 명령과 그 출력은 서버 진단(SSH_LOG_FILE)과 별도로 일별 파일에 단일 JSONL 라인으로 기록됩니다:
<SSH_AUDIT_DIR>/audit_YYYY-MM-DD.jsonl각 레코드:
{"timestamp":"2026-06-08T11:07:12.569Z","connectionId":"10_0_0_1_2026_06_08_sessionid_a1b9f3","host":"10.0.0.1","command":"show version","exitCode":0,"output":"..."}SSH_AUDIT_ENABLED=false로 비활성화합니다.
대용량 출력 오프로딩
명령 출력이 SSH_MAX_OUTPUT_LENGTH를 초과하고 SSH_ENABLE_LARGE_OUTPUT=true인 경우, 전체 출력이 SSH_FILE_UPLOAD_ENDPOINT로 POST되고 호출자는 반환된 artifact_uri와 file_id 및 작은 미리보기를 포함한 짧은 스텁을 받습니다 — 따라서 대용량 show tech-support가 모델 컨텍스트를 넘치게 하지 않습니다. 엔드포인트는 { "content": "...", "filename": "..." }를 수신하고 { "file_id": "...", "artifact_uri": "..." }를 반환해야 합니다. 엔드포인트가 설정되지 않았거나 업로드가 실패하면 출력이 인라인으로 대체 반환됩니다.
페이저 처리
대화형 페이저(Linux less, Cisco/Juniper ---(more)---)는 그대로 두면 명령이 타임아웃될 때까지 차단합니다. 서버는 두 가지 방식으로 처리합니다:
예방 — 셸을 열 때 장치에 적합한 페이저 비활성화 명령을 전송하고(Cisco는
terminal length 0, Juniper는set cli screen-length 0), Linux exec에서는SYSTEMD_PAGER=,PAGER=cat,GIT_PAGER=cat을 설정합니다.감지 — 페이저 프롬프트가 여전히 나타나면 네트워크 셸에서는 자동으로 진행(스페이스 전송,
SSH_MAX_PAGER_PAGES로 제한)하거나, 대화형 Linux 페이저를 종료하기 위해q를 전송한 다음 출력에서 프롬프트 아티팩트를 제거합니다.
SSH_DISABLE_PAGER=false로 전역 토글, PROFILE_<NAME>_DISABLE_PAGER=false로 프로필별 토글, SSH_DISABLE_PAGER_CMD_<DEVICETYPE>로 장치 유형별 명령 재정의, SSH_PAGER_REGEX / SSH_PAGER_ADVANCE_KEY로 감지 재정의가 가능합니다.
위험 패턴
이 패턴들은 필터 모드와 관계없이 항상 차단됩니다:
패턴 | 예시 | 위험 |
포크 폭탄 |
| 시스템 충돌 |
파이프 rm |
| 데이터 손실 |
체인 rm |
| 데이터 손실 |
장치 리다이렉트 |
| 디스크 손상 |
시스템 설정 덮어쓰기 |
| 시스템 침해 |
원격 코드 실행 |
| 임의 코드 실행 |
재귀적 chmod 777 |
| 보안 침해 |
사용 가능한 도구
일회성(권장)
도구 | 설명 |
| 연결하고, 하나의 명령을 실행하고, 성공 시 연결을 닫습니다 — 한 번의 호출로 connectionId를 추적할 필요가 없습니다. 실패 시 연결은 열린 상태로 유지되고 해당 |
|
|
연결 관리
도구 | 설명 |
| 영구 연결을 열고 |
| 호스트에 SSH로 접속한 후 단일 점프 명령을 통해 중첩 CLI(telnet, fs_cli 등)에 진입합니다. 프리셋을 지원합니다. 각 후보가 자체 프롬프트 패턴이 필요할 때 사용하십시오. |
| CSV/JSON 파일에서 연결을 로드합니다(자격 증명은 connectionId별 환경 변수에서 해석됨). |
| 하나의 연결을 끊습니다. |
| 모든 연결을 끊습니다. |
실행
도구 | 설명 |
| 기존 연결에서 명령을 실행합니다. 필수: |
| 선택한 연결에서 명령을 실행합니다( |
상태 및 내부 조사
도구 | 설명 |
| 연결에 적용되는 명령 필터(화이트리스트/블랙리스트, 전역 + 프로필별, 우선순위 규칙 포함)와 호스트 필터(허용/차단 호스트)를 표시합니다. 선택적으로 특정 명령이 허용될지 확인할 수 있습니다. |
| 활성 연결을 상태와 함께 나열합니다. |
| 모든 연결의 상태를 확인합니다(죽은 소켓 감지, 셸 상태). |
| 최근 실패한 연결 시도를 나열합니다(실패 연결 JSONL 로그에서). |
파일 전송(SFTP)
도구 | 설명 |
| SFTP로 파일을 업로드합니다. |
| SFTP로 파일을 다운로드합니다. |
| SFTP로 원격 디렉터리를 나열합니다. |
예제 워크플로우
단일 명령(한 번의 호출)
→ ssh_run {
host: "172.168.0.2",
profile: "ROUTERS",
command: "show version"
}
(connects, runs, closes; returns the output)중첩 CLI 내 단일 명령(한 번의 호출)
→ ssh_run_with_jump {
host: "10.0.0.1",
username: "admin",
preset: "topex",
jumpCommands: ["telnet lh", "telnet 127.0.0.1"],
command: "view portsoncard *"
}실패 후 재시도
1. → ssh_run { host: "172.168.0.2", profile: "ROUTERS", command: "show bogus" }
← { status: "error", connectionId: "172_168_0_2_..._sessionid_a1b2c3", exitCode: 2, ... }
(connection left open)
2. → ssh_execute {
command: "show interfaces terse",
connectionId: "172_168_0_2_..._sessionid_a1b2c3"
}
3. → ssh_disconnect { connectionId: "172_168_0_2_..._sessionid_a1b2c3" }플릿 작업(영구 연결)
1. Load connections from CSV (passwords auto-resolved from env vars)
→ ssh_load_connections { filePath: "devices.csv", connectAll: true }
(ROUTER1_PASSWORD, ROUTER2_PASSWORD resolved automatically)
2. Execute show commands on all devices
→ ssh_execute_on_multiple {
command: "show ip interface brief",
connectionIds: ["*"]
}
3. Execute a command on one specific router
→ ssh_execute {
command: "show running-config | include hostname",
connectionId: "router1"
}
4. Check connection health
→ ssh_check_connections {}
5. Inspect why a command was blocked
→ ssh_get_command_filter {
connectionId: "router1",
command: "configure terminal"
}
6. Disconnect all
→ ssh_disconnect_all {}아키텍처 참고 사항
셸 버퍼 관리
각 명령 전에 버퍼가 지워집니다. 안정성 감지는 버퍼가 3 × 500ms 동안 변경되지 않음 = 명령 완료로 판단합니다. 비밀번호 프롬프트는 버퍼의 마지막 200자에서 감지됩니다.
Keepalive 시스템
SSH2는 10초마다 keepalive를 전송합니다(keepaliveInterval: 10000). keepalive가 3회 실패하면 연결이 자동으로 닫힙니다(keepaliveCountMax: 3). 사용자 정의 간격은 디버깅을 위해 keepalive 횟수를 기록합니다.
연결 상태 모니터링
서버는 죽은 연결(소켓 파괴)을 감지하고, 네트워크 장치의 셸 상태를 추적하며, 죽은 연결을 자동 정리하고, 네트워크 장치에서 셸이 닫힌 경우 셸 재개를 시도합니다.
점프 셸
ssh_connect_with_jump_command가 호출되면 서버는: (1) SSH 연결을 열고, (2) PTY 셸을 열고, (3) 점프 명령(예: telnet lh)을 전송하고, (4) 300ms마다 셸 버퍼를 폴링하여 예상 프롬프트 정규식을 확인하고, (5) 연결을 jumpShellActive: true인 jump_shell로 표시합니다. 연결 해제 시 SSH 세션을 닫기 전에 중첩 CLI 종료 명령이 전송됩니다. 셸 복구 시 점프 명령이 자동으로 재전송됩니다.
환경 변수 자격 증명 해석
연결이 생성될 때(ssh_connect 또는 ssh_load_connections를 통해) 비밀번호가 제공되지 않으면 서버는 환경 변수에서 <PREFIX>_PASSWORD를 자동으로 조회합니다. 여기서 <PREFIX>는 connectionId를 대문자로 변환하고 영숫자가 아닌 문자를 _로 대체한 값입니다. 동일한 규칙이 _ENABLE_PASSWORD와 _USERNAME에도 적용됩니다. 명시적으로 제공된 값이 항상 우선합니다.
원본과의 비교
기능 | zibdie/SSH-MCP-Server | 이 포크 |
기본 SSH/SFTP | ✓ | ✓ |
명령어 화이트리스트 | ✗ | ✓ |
명령어 블랙리스트 | ✗ | ✓ |
다중 단어 블랙리스트 항목 | ✗ | ✓ |
위험 패턴 감지 | ✗ | ✓ |
감사 로깅 | ✗ | ✓ |
명령어 검증 도구 | ✗ | ✓ |
구성 파일 지원 | ✗ | ✓ |
네트워크 장치 유형 (Cisco, Juniper, MikroTik) | ✗ | ✓ |
Cisco enable 모드 | ✗ | ✓ |
점프 셸 (SSH를 통한 중첩 CLI) | ✗ | ✓ |
CSV/JSON에서 대량 연결 | ✗ | ✓ |
다중 연결 실행 | ✗ | ✓ |
환경 변수 자격 증명 | ✗ | ✓ |
연결 상태 모니터링 | ✗ | ✓ |
Keepalive 추적 | ✗ | ✓ |
| ✗ | ✓ |
개발
# Clone
git clone https://github.com/marian-craciunescu/ssh-mcp-server-secured.git
cd ssh-mcp-server-secured
# Install dependencies
npm install
# Run in development mode
npm run dev
# Test with MCP Inspector
npx @modelcontextprotocol/inspector node index.js보안 고려 사항
기본값은 블랙리스트 모드 — 유연성을 유지하면서 보호 기능을 제공합니다
위험 패턴은 항상 검사됩니다 — 비활성화된 경우에도
감사 로깅이 기본적으로 활성화됨 — 차단된 시도를 추적합니다
Sudo를 제한할 수 있습니다 — 고보안 환경에서는
SSH_ALLOW_SUDO=false로 설정하세요자격 증명 격리 — 비밀번호는 connectionId로 환경 변수에서 확인되며, 채팅에 입력하거나 도구 호출에 노출되지 않습니다
라이선스
MIT — LICENSE 파일 참조
크레딧
원본: Nour Zibdie의 zibdie/SSH-MCP-Server
보안 포크: marian-craciunescu
지원
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