Skip to main content
Glama

SSH MCP Server (Secured)

npm version CI/CD License: MIT

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=router1

LLM은 ssh_connect를 다음과 같이 호출합니다:

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

도구 호출에 비밀번호가 없습니다. 서버는 환경 변수에서 ROUTER1_PASSWORD를 자동으로 찾습니다.

자격 증명 확인 규칙

connectionId는 환경 변수 접두사로 변환됩니다. 대문자로 바뀌고, 영숫자가 아닌 문자는 _로 대체됩니다.

connectionId

비밀번호용 환경 변수

enable 비밀번호용 환경 변수

router1

ROUTER1_PASSWORD

ROUTER1_ENABLE_PASSWORD

my-connection

MY_CONNECTION_PASSWORD

MY_CONNECTION_ENABLE_PASSWORD

dc1.switch.3

DC1_SWITCH_3_PASSWORD

DC1_SWITCH_3_ENABLE_PASSWORD

선택적으로 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

사용 사례

KexAlgorithms

algorithms.kex

레거시 키 교환 (예: diffie-hellman-group1-sha1)

HostKeyAlgorithms

algorithms.serverHostKey

레거시 호스트 키 (예: ssh-rsa, ssh-dss)

Ciphers

algorithms.cipher

레거시 암호화 (예: aes128-cbc)

MACs

algorithms.hmac

레거시 MAC (예: hmac-sha1)

sshOptionsssh_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으로 파싱되어 deviceTypecisco인 모든 연결에 적용됩니다.

환경 변수 예시

필드

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

프로필별 페이저 토글 (true/false)

false

ssh_connect host=10.0.0.1 profile=CISCO connectionId=SWITCH1"

프로필별 명령어 필터링

전역 SSH_WHITELIST / SSH_BLACKLIST 외에도 각 프로필은 PROFILE_<NAME>_WHITELISTPROFILE_<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. 네트워크 장치 유형

서버는 적절한 연결 처리를 통해 다양한 장치 유형을 지원합니다:

장치 유형

동작

사용 사례

linux

표준 SSH exec 모드 (기본값)

Linux/Unix 서버

cisco

지속형 셸, enable 모드 지원

Cisco IOS/IOS-XE 라우터 및 스위치

cisco_xe

지속형 셸 (terminal length 0)

Cisco IOS-XE

cisco_xr

지속형 셸 (terminal length 0)

Cisco IOS-XR

cisco_asa

지속형 셸 (terminal length 0)

Cisco ASA 방화벽

cisco_nexus

지속형 셸 (terminal length 0)

Cisco Nexus (NX-OS)

juniper

지속형 셸 (set cli screen-length 0)

Juniper JunOS 장치

mikrotik

지속형 셸

MikroTik RouterOS

fortinet

지속형 셸 (config system console / set output standard)

FortiGate / FortiOS 방화벽

paloalto

지속형 셸 (set cli pager off)

Palo Alto PAN-OS 방화벽

sophos

지속형 셸 (런타임 시 페이저 자동 처리)

Sophos XG/XGS (SFOS) 방화벽

network

일반 지속형 셸

기타 네트워크 장치

jump_shell

지속형 셸 + 중첩 CLI

ssh_connect_with_jump_command에서 내부적으로 사용

네트워크 장치는 표준 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."
}

해당 connectionIdssh_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_COMMANDjumpCommands가 생략된 경우 단일 후보를 제공합니다.

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_cli

  • SSH 후 대화형 세션이 필요한 모든 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

프롬프트 패턴

종료 명령

freeswitch

fs_cli

freeswitch@...>

/exit

topex

(사용자 제공)

topexsw>

quit

프리셋은 재정의할 수 있습니다 — 명시적으로 제공된 매개변수가 우선합니다.

셸 복구: 셸이 끊기면 ssh_execute가 자동으로 셸을 다시 열고 점프 셸에 다시 진입합니다.

연결 해제: ssh_disconnect는 SSH 연결을 닫기 전에 중첩 CLI에 종료 명령을 정상적으로 보냅니다.

7. 로깅

환경 변수를 통해 로그 수준을 설정합니다:

변수

기본값

SSH_LOG_LEVEL

DEBUG, INFO, WARN, ERROR

INFO

SSH_LOG_FILE

로그 파일 경로

(없음)

로그 형식:

[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

구성

환경 변수

변수

기본값

설명

SSH_FILTER_MODE

whitelist, blacklist, disabled

blacklist

명령 필터링 모드

SSH_ALLOW_SUDO

true, false

true

sudo 명령 허용

SSH_LOG_BLOCKED

true, false

true

차단된 명령을 stderr에 기록

SSH_MCP_CONFIG

파일 경로

-

구성 JSON 파일 경로

SSH_WHITELIST

쉼표로 구분 또는 JSON

-

화이트리스트 명령 재정의

SSH_BLACKLIST

쉼표로 구분 또는 JSON

-

블랙리스트 명령 재정의

SSH_DANGEROUS_PATTERNS

JSON 배열

-

위험한 정규식 패턴 재정의

SSH_LOG_LEVEL

DEBUG, INFO, WARN, ERROR

INFO

로그 상세 수준

SSH_LOG_FILE

경로

-

파일에 로그 기록

SSH_HOST_FILTER_MODE

whitelist, blacklist, disabled

disabled

호스트 필터링 모드

SSH_HOST_WHITELIST

쉼표로 구분된 IP

-

허용된 호스트 IP 화이트리스트

SSH_HOST_BLACKLIST

쉼표로 구분된 IP

-

허용된 호스트 IP 블랙리스트

SSH_IDLE_TIMEOUT

120

유휴 연결 시간 초과

SSH_FAILED_CONNECTIONS_LOG

파일 경로

./ssh-failed-connections.json

/var/log/ssh-failed.jsonl

SSH_AUDIT_ENABLED

true, false

true

명령별 세션 감사(명령 + 전체 출력)를 JSONL에 기록

SSH_AUDIT_DIR

경로

./audit

일별 audit_YYYY-MM-DD.jsonl 파일 디렉터리

SSH_ENABLE_LARGE_OUTPUT

true, false

false

과도하게 큰 명령 출력을 업로드 엔드포인트로 오프로드하고 인라인 텍스트 대신 URI를 반환

SSH_MAX_OUTPUT_LENGTH

정수(문자 수)

10000

출력이 오프로드되는 크기 임계값

SSH_FILE_UPLOAD_ENDPOINT

URL

-

대용량 출력용 POST 대상. {content, filename}을 수신하고 {file_id, artifact_uri}를 반환해야 함

SSH_DISABLE_PAGER

true, false

true

셸 및 exec에서 대화형 페이저(less/---(more)---) 억제

SSH_DISABLE_PAGER_CMD_<DEVICETYPE>

문자열

장치 유형별 기본값

장치 유형에 대한 페이저 비활성화 명령 재정의(예: SSH_DISABLE_PAGER_CMD_CISCO)

SSH_PAGER_REGEX

정규식 문자열

내장

페이저 프롬프트 감지에 사용되는 패턴 재정의

SSH_PAGER_ADVANCE_KEY

문자열

" "(공백)

다음 페이저 페이지로 이동하기 위해 전송되는 키

SSH_MAX_PAGER_PAGES

정수

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 terminalconf 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

비활성화 모드

명령 필터링 없음(주의해서 사용).

명령 검증 순서

  1. 필터링 비활성화 여부 확인

  2. sudo 권한 확인

  3. 위험 패턴(정규식) 확인 — 항상 우선 적용되며, 어떤 화이트리스트도 이를 무시할 수 없음

  4. mixed 모드: 전체 명령에 대해 화이트리스트와 블랙리스트 중 더 긴 매치를 선택하고, 둘 다 매치되지 않으면 기본적으로 거부. 여기서 종료.

  5. 블랙리스트에 대해 전체 명령 확인(다중 단어 지원)

  6. 파이프/체인에서 기본 명령 추출

  7. 각 기본 명령을 블랙리스트/화이트리스트에 대해 확인

  8. 전역 결과 위에 프로필별(연결별) 화이트리스트/블랙리스트 적용

ssh_get_command_filter를 사용하여 활성 규칙을 확인하고 특정 명령이 허용되거나 차단되는 이유를 물어볼 수 있습니다.

안정적인 연결 ID

ssh_connectconnectionId를 전달하지 않거나(또는 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_urifile_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

find . | rm

데이터 손실

체인 rm

ls && rm -rf /

데이터 손실

장치 리다이렉트

> /dev/sda

디스크 손상

시스템 설정 덮어쓰기

> /etc/passwd

시스템 침해

원격 코드 실행

curl | bash

임의 코드 실행

재귀적 chmod 777

chmod -R 777 /

보안 침해

사용 가능한 도구

일회성(권장)

도구

설명

ssh_run

연결하고, 하나의 명령을 실행하고, 성공 시 연결을 닫습니다 — 한 번의 호출로 connectionId를 추적할 필요가 없습니다. 실패 시 연결은 열린 상태로 유지되고 해당 connectionId가 반환되어 ssh_execute로 다른 명령을 재시도할 수 있습니다. 필수: host, command.

ssh_run_with_jump

ssh_run과 동일하지만 먼저 중첩 CLI에 진입합니다. jumpCommands를 목록으로 받아 중첩 프롬프트에 도달할 때까지 순서대로 시도합니다. 필수: host, command.

연결 관리

도구

설명

ssh_connect

영구 연결을 열고 connectionId를 반환합니다. 비밀번호는 <CONNECTIONID>_PASSWORD 환경 변수에서 자동 해석됩니다. 레거시 알고리즘 협상을 위한 sshOptions를 지원합니다.

ssh_connect_with_jump_command

호스트에 SSH로 접속한 후 단일 점프 명령을 통해 중첩 CLI(telnet, fs_cli 등)에 진입합니다. 프리셋을 지원합니다. 각 후보가 자체 프롬프트 패턴이 필요할 때 사용하십시오.

ssh_load_connections

CSV/JSON 파일에서 연결을 로드합니다(자격 증명은 connectionId별 환경 변수에서 해석됨).

ssh_disconnect

하나의 연결을 끊습니다.

ssh_disconnect_all

모든 연결을 끊습니다.

실행

도구

설명

ssh_execute

기존 연결에서 명령을 실행합니다. 필수: command, connectionId.

ssh_execute_on_multiple

선택한 연결에서 명령을 실행합니다(["*"] 또는 [] = 전체). 순차적으로 실행됩니다.

상태 및 내부 조사

도구

설명

ssh_get_command_filter

연결에 적용되는 명령 필터(화이트리스트/블랙리스트, 전역 + 프로필별, 우선순위 규칙 포함)와 호스트 필터(허용/차단 호스트)를 표시합니다. 선택적으로 특정 명령이 허용될지 확인할 수 있습니다.

ssh_list_connections

활성 연결을 상태와 함께 나열합니다.

ssh_check_connections

모든 연결의 상태를 확인합니다(죽은 소켓 감지, 셸 상태).

ssh_failed_connections

최근 실패한 연결 시도를 나열합니다(실패 연결 JSONL 로그에서).

파일 전송(SFTP)

도구

설명

ssh_upload_file

SFTP로 파일을 업로드합니다.

ssh_download_file

SFTP로 파일을 다운로드합니다.

ssh_list_files

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: truejump_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 추적

host/hostname 호환성

개발

# 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 파일 참조

크레딧

지원

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