Skip to main content
Glama

ssh-mcp

一个集中式 MCP 网关,通过 Streamable HTTP 让 AI 代理对 SSH 基础设施拥有受控访问权限。

ssh-mcp 以单个 HTTP 服务运行。多个 AI 客户端——代理、CI 流水线、仪表盘——连接到一个网关。SSH 凭证保留在网关上。授权策略、审计日志和速率限制在任何 SSH 命令执行之前集中生效。

License: MIT Docker MCP Security M8ven Live Monitored


目录


Related MCP server: MCP SSH Orchestrator

架构

本地 stdio MCP(常见模式)

AI client
   │
   ▼
local MCP process ──► SSH target

每个代理运行自己的进程。SSH 凭证存在于每台机器上。没有集中控制。

ssh-mcp(集中式 HTTP 网关)

AI clients ───────┐
CI agents ────────┼──► ssh-mcp ──► SSH targets
Dashboards ───────┘      │
                         ├─ API-key authentication
                         ├─ per-client authorization
                         ├─ rate limiting
                         ├─ audit logging
                         └─ connection pooling

单个部署即可服务所有客户端。凭证、策略和日志集中存放在一处。


为什么选择 ssh-mcp?

  • 集中式 HTTP 网关——一个部署通过 Streamable HTTP 服务所有 AI 代理、CI 流水线和仪表盘

  • 按客户端授权——不同 API 密钥在不同服务器上授予不同的命令集

  • 分层命令策略——阻止模式、危险 shell 检测和按目标白名单协同工作

  • 集中式 SSH 访问——SSH 凭证存放在网关上,而不是每个代理的机器上

  • 审计跟踪——每条命令、每个客户端、每个结果——带请求追踪的结构化 JSONL 日志

  • 运维韧性——连接池、熔断器以及带指数退避的重试

  • 可观测性——用于监控的 Prometheus 指标和健康检查端点


多代理访问控制

不同的代理需要不同的权限。ssh-mcp 在网关上强制执行这一点:

monitoring agent  →  API key A  →  read-only commands  →  all servers
deployment agent  →  API key B  →  deploy commands      →  web servers only
database agent    →  API key C  →  db commands           →  database server only
                  ┌─ monitoring agent (read-only, all servers)
                  ├─ deployment agent (deploy commands, web only)
MCP clients ──────┼─ database agent (db commands, db server only)
                  └─ ...
                         │
                         ▼
                      ssh-mcp
                         │
                  centralized policies
                         │
              ┌──────────┼──────────┐
              ▼          ▼          ▼
             web         db      monitoring
           servers    servers     servers

演示此设置的最小配置:

{
  "version": 1,
  "ssh_targets": {
    "web-1": { "host": "10.0.1.10", "username": "deploy" },
    "db-1":  { "host": "10.0.1.20", "username": "dbadmin" }
  },
  "allowed_commands": {
    "default": {
      "web-1": { "allow": ["uptime", "df -h", "free -m"] }
    },
    "api_keys": {
      "deploy-key": {
        "web-1": { "allow": ["systemctl restart app", "deploy *"] }
      },
      "db-key": {
        "db-1": { "allow": ["systemctl restart postgres", "pg_dump *"] }
      }
    }
  }
}

问题

大多数 MCP SSH 服务器以本地 stdio 进程运行——每个客户端一个,没有共享状态、没有集中式授权、也没有审计跟踪。当多个 AI 代理、CI 流水线或仪表盘需要 SSH 访问时,每个都会独立管理自己的 SSH 密钥并运行自己的 MCP 进程。这会导致:

  • 没有集中式访问控制——每个客户端自行决定可以运行什么

  • 没有审计跟踪——运维团队看不到命令

  • SSH 密钥泛滥——密钥散落在每台运行代理的机器上

  • 没有速率限制——失控的代理可能压垮目标

  • 没有连接池——每个客户端独立打开和关闭 SSH 会话

ssh-mcp 通过将单个 MCP 服务器部署为 HTTP 网关来解决这个问题。所有客户端连接到它;它连接到你的 SSH 目标。授权、身份验证、速率限制、连接池和审计日志都在一处完成。


使用场景

多代理服务器管理

运行一组具有不同访问级别的 AI 代理。部署代理可以在 Web 服务器上执行 systemctl restart nginx;监控代理可以在任意位置执行 journalctl;数据库代理只能在数据库服务器上运行 psql。每个代理使用自己的 API 密钥进行身份验证;每个密钥都有各自的权限集。

CI/CD 流水线集成

将你的 CI 流水线指向 ssh-mcp,而无需在每个 runner 上管理 SSH 密钥。每条流水线一个 API 密钥、针对 CI 子网的基于网络的规则以及命令白名单,确保你的部署脚本只运行应运行的内容——不多不少。

集中式日志与配置检索

使用 ssh_download_file 从远程服务器拉取日志、配置文件或数据库转储,而无需离开你的 MCP 客户端。8 层路径验证和沙箱根目录设置确保文件传输保持在安全边界之内。

服务器健康仪表盘

构建一个由 MCP 驱动的仪表盘,在整支服务器集群中查询 uptimefreedfps。连接池复用 SSH 会话,熔断器隔离故障目标,/metrics 处的 Prometheus 指标接入你现有的监控体系。

合规与审计

每条命令都会以结构化 JSONL 记录:谁在哪个服务器上从哪个 IP 运行了什么命令、是否被允许、耗时多久。matched_via 字段精确追踪是哪个授权层做出的决定。配置变更会单独记录变更前后的状态。


安全模型

ssh-mcp 在每一层都应用纵深防御。完整的安全模型记录在 docs/SECURITY.md 中。

安全边界: ssh-mcp 在 SSH 之前增加了一层授权、身份验证和审计。它不会替代底层 SSH 账户的权限。如果某条命令被允许,SSH 用户将以该账户拥有的任何权限来执行它。网关本身应通过 TLS 和网络访问控制加以保护。日志可能包含命令输出,应相应地加以处理。

命令授权链

命令通过有序的分层链进行评估。如果任何一层拒绝,请求就会在该处停止:

检查内容

1. 目标验证

服务器名称是否已知?

2. block_patterns

命令是否匹配被阻止的正则表达式?

3. 危险模式

是否包含 $()、反引号或换行符?

4. 重定向防护

shell 重定向是否指向 /dev//proc//sys/

5. 分段

去除重定向并按 &&`

;、|\ 拆分后,每个分段执行完整链

6. default 规则

所有客户端的允许/拒绝规则

7. api_keys 规则

按密钥的允许/拒绝规则

8. networks 规则

按 CIDR 的允许/拒绝规则

9. 拒绝

隐式回退

身份验证

API 密钥通过 X-API-KeyAuthorization: Bearer 请求头发送。密钥使用 PBKDF2-HMAC-SHA256 哈希(100,000 次迭代、随机 16 字节盐),并通过常数时间比较进行验证。原始密钥永远不会存储。

输入清理

命令、目标名称和日志字符串在处理前会进行清理:去除空字节、移除控制字符、NFKC 规范化,并对 block_patterns 执行 ReDoS 防护

路径遍历防护

SFTP 传输经过 8 层路径验证,包括空字节检查、控制字符去除、点段规范化、符号链接解析和沙箱根目录强制执行。

速率限制

按客户端 IP 的滑动窗口速率限制器(60 次请求 / 60 秒,/health 除外)。违反时返回 HTTP 429 并附带 Retry-After

速率限制可在 settings.rate_limit 下配置:

"settings": {
  "rate_limit": {
    "enabled": true,                        // set false to disable entirely
    "max_requests_per_minute": 60,          // max requests per client IP in the window
    "window_seconds": 60.0,                 // sliding-window duration
    "cleanup_interval_seconds": 300.0       // expired-entry GC interval
  }
}

注意: 速率限制器在容器启动时根据初始配置构建一次,并且不会在配置热重载时重建。要禁用速率限制,必须在启动时存在的配置(例如挂载卷中的 config/ssh-mcp-config.json)中将 settings.rate_limit.enabled 设为 false。这对于高流量客户端或从单个 IP 发出大量请求的测试套件很有用。


快速开始

先决条件

  • 安装了 Docker Compose 的 Docker

  • 用于你要访问的服务器的 SSH 密钥对(或按目标的密码)

1. 创建目录

mkdir -p config logs
ssh-keygen -t ed25519 -f ssh_key -N ""
cp default-config.json config/ssh-mcp-config.json

2. 添加 SSH 目标

打开 config/ssh-mcp-config.json 并添加一个目标:

{
  "version": 1,
  "ssh_targets": {
    "web-server": {
      "host": "192.168.1.10",
      "port": 22,
      "username": "deploy",
      "private_key": "/app/ssh_key"
    }
  },
  "block_patterns": [ "\\brm\\s+-rf\\b", "\\bdd\\s+if=" ],
  "allowed_commands": {
    "default": [
      { "targets": ["*"], "commands": ["hostname", "uptime", "free", "df", "ps", "ls", "cat"] }
    ]
  },
  "settings": {}
}

3. 启动服务器

docker compose up -d --build

4. 验证运行状态

curl http://localhost:9080/health
# {"status": "ok", "connection_pool": {...}}

5. 连接 MCP 客户端

任何支持 Streamable HTTP 的 MCP 客户端都可以连接。将其指向 http://localhost:9080/mcp 并附带 API 密钥请求头。详情请参阅 MCP 客户端配置

6. 列出服务器并运行命令

curl -X POST http://localhost:9080/mcp \
  -H "Content-Type: application/json" \
  -H "X-API-Key: your-api-key" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
      "name": "ssh_list_servers",
      "arguments": {}
    }
  }'

curl -X POST http://localhost:9080/mcp \
  -H "Content-Type: application/json" \
  -H "X-API-Key: your-api-key" \
  -d '{
    "jsonrpc": "2.0",
    "id": 2,
    "method": "tools/call",
    "params": {
      "name": "ssh_execute_command",
      "arguments": {"server_name": "web-server", "command": "uptime"}
    }
  }'

MCP 客户端配置

任何支持 Streamable HTTP 传输的 MCP 客户端都可以连接。配置格式因客户端而异——请使用下面的 URL 和请求头。

设置

传输

Streamable HTTP

URL

https://ssh-mcp.example.com/mcp

身份验证

X-API-Key 请求头或 Authorization: Bearer

通用 Streamable HTTP 配置

{
  "mcpServers": {
    "ssh": {
      "url": "http://localhost:9080/mcp",
      "headers": {
        "Authorization": "Bearer <your-api-key>"
      }
    }
  }
}

Python 客户端

import requests

MCP_URL = "https://ssh-mcp.example.com/mcp"
API_KEY = "your-api-key"


def call_tool(name: str, arguments: dict) -> dict:
    response = requests.post(
        MCP_URL,
        headers={
            "Content-Type": "application/json",
            "X-API-Key": API_KEY,
        },
        json={
            "jsonrpc": "2.0",
            "id": 1,
            "method": "tools/call",
            "params": {"name": name, "arguments": arguments},
        },
    )
    response.raise_for_status()
    return response.json()


print(call_tool("ssh_list_servers", {}))
print(call_tool("ssh_execute_command", {
    "server_name": "web-server",
    "command": "uptime",
}))

原始 JSON-RPC

将工具调用作为 JSON-RPC tools/call 请求发送到 /mcp

curl -X POST http://localhost:9080/mcp \
  -H "Content-Type: application/json" \
  -H "X-API-Key: your-api-key" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
      "name": "ssh_execute_command",
      "arguments": {"server_name": "web-server", "command": "uptime"}
    }
  }'

工具

所有工具调用都是发送到 /mcp 的 JSON-RPC tools/call 请求。所有工具都返回字符串(JSON 或纯文本)。

工具

参数

描述

ssh_list_servers

(无)

列出已配置的 SSH 目标(host、port、username——不含机密信息)

ssh_list_allowed_commands

server_name (str)

列出当前客户端可以在目标上运行的命令(default + api_key + network 规则的并集)

ssh_execute_command

server_name (str), command (str), timeout (int, 默认 30), sudo (bool, 默认 false)

通过 SSH 执行命令;返回 stdout(stderr 以 [STDERR] 追加,退出码以 [EXIT: n] 表示)

ssh_download_file

server_name (str), remote_path (str)

通过 SFTP 下载文件;授权等同于 cat <path>

ssh_upload_file

server_name (str), remote_path (str), content (str), permissions (str, 默认 "0644")

通过 SFTP 上传文件;授权等同于 tee <path>

ssh_check_connection

server_name (str), timeout (int, 默认 10)

通过运行目标的 checkcommand 检查 SSH 连接;返回成功标志、输出和退出码

示例

# List available servers
call_tool("ssh_list_servers", {})
# {"web-server": {"host": "192.168.1.10", "port": 22, "username": "deploy"}}

# List what this client can run on web-server
call_tool("ssh_list_allowed_commands", {"server_name": "web-server"})
# ["cat", "df", "du", "free", "grep", "head", "hostname", ...]

# Execute a command
call_tool("ssh_execute_command", {
    "server_name": "web-server",
    "command": "uptime",
})
# " 07:12:33 up 10 days,  2:15,  1 user,  load average: 0.08, 0.03, 0.01"

# Download a file
call_tool("ssh_download_file", {
    "server_name": "web-server",
    "remote_path": "/etc/hostname",
})
# "web-server\n"

# Upload a file
call_tool("ssh_upload_file", {
    "server_name": "web-server",
    "remote_path": "/tmp/backup.sql",
    "content": "CREATE TABLE ...;\n",
    "permissions": "0640",
})
# "OK: Uploaded 19 bytes to /tmp/backup.sql"

# Check SSH connectivity
call_tool("ssh_check_connection", {"server_name": "web-server"})
# {"success": true, "output": "ping", "error": null, "exit_code": 0, "checkcommand": "echo ping"}

# Check with custom timeout
call_tool("ssh_check_connection", {"server_name": "web-server", "timeout": 5})

关于 sudo 的说明: 没有 sudo_password 参数。如果 sudo 需要密码,则密码来自配置中目标的 password 字段。sudo 标志会使用 sudo -S -p ''(密码来自配置)或 sudo -n(免密码)进行包装。

错误响应

失败时,工具返回:

{
  "error": true,
  "error_type": "AuthorizationError",
  "message": "Command rejected: target 'foo' not found",
  "retryable": false,
  "request_id": "abc-123"
}

常见的 error_type 值:AuthorizationErrorPathValidationErrorFileTransferErrorSSHAuthenticationErrorSSHTimeoutErrorMCPSSHError。对于 SSHTimeoutErrorretryable 标志为 true。违反速率限制则改为返回 HTTP 429。


配置

配置文件位置

服务器读取 <config_dir>/ssh-mcp-config.json。通过 --config CLI 标志或 MCP_SSH_CONFIG_PATH 环境变量设置 config_dir(默认值:/config)。如果文件不存在,服务器会写入随附的 default-config.json

顶层结构

{
  "version": 1,
  "ssh_targets": { ... },
  "block_patterns": [ ... ],
  "allowed_commands": {
    "default": [ ... ],
    "api_keys": [ ... ],
    "networks": [ ... ]
  },
  "settings": { ... }
}

配置在加载时会根据 config.schema.json(JSON Schema Draft 2020-12)进行校验。未知键会导致硬错误。

ssh_targets

一个以服务器标识符为键的对象。每个目标都需要 hostportusername,以及 private_keypassword 中的至少一个。

"ssh_targets": {
  "web-server": {
    "host": "192.168.1.10",
    "port": 22,
    "username": "deploy",
    "private_key": "/app/ssh_key",
    "checkcommand": "echo ping"
  }
}

字段

必填

默认值

描述

host

主机名或 IP 地址

port

22

SSH 端口

username

SSH 用户名

private_key

*

服务器文件系统上 SSH 私钥文件的路径

password

*

SSH 密码(也可以通过 secrets.json 或环境变量设置)

checkcommand

"echo ping"

ssh_check_connection 执行以验证连接的命令

* private_keypassword 中至少需要提供其一。

private_key服务器文件系统上的路径(在 Docker 中,会挂载到容器内),而不是内联密钥。

block_patterns

一个正则表达式模式列表。任何匹配模式的命令都会被拒绝,无论其他允许列表层如何。模式在加载时会针对灾难性回溯结构进行筛查(ReDoS 防护),并在运行时以超时保护进行编译。

allowed_commands

三个子对象控制每个客户端可以运行哪些命令:

  • default — 适用于所有客户端的规则(除非更具体的层先作出决定)

  • api_keys — 按密钥的规则,通过 key_hash 匹配

  • networks — 按 CIDR 的规则,通过客户端源 IP 匹配

每个规则都有一个 targets 列表(服务器 ID,或 "*" 表示全部)和一个 commands 列表(基础命令名,或 "*" 表示任意命令)。

"allowed_commands": {
  "default": [
    { "targets": ["*"], "commands": ["hostname", "uptime", "free", "df", "ps"] }
  ],
  "api_keys": [
    {
      "name": "ci-bot",
      "key_hash": "pbkdf2:sha256:100000$<salt>$<hash>",
      "rules": [
        { "targets": ["web-server"], "commands": ["systemctl", "journalctl"] }
      ]
    }
  ],
  "networks": [
    {
      "name": "home-lan",
      "range": "192.168.1.0/24",
      "rules": [
        { "targets": ["*"], "commands": ["*"] }
      ]
    }
  ]
}

settings

设置

默认值

描述

max_output_length

50000

返回给客户端的命令输出的最大字节数(整数或大小字符串)

command_timeout_max

120

命令超时的硬上限(秒)

retry_max_attempts

3

瞬时 SSH 失败的重试次数

retry_backoff_base_seconds

1.0

指数退避基数(秒)

circuit_breaker_failure_threshold

5

每个目标在熔断器打开前的失败次数

circuit_breaker_timeout_seconds

60.0

熔断器打开后的恢复超时(秒)

log_level

"INFO"

日志级别:DEBUG、INFO、WARNING、ERROR

max_log_output

4096

日志条目中存储的输出最大字符数

compress_rotated

true

对轮转的日志文件进行 Gzip 压缩

pool_max_connections_per_target

5

每个目标的最大连接池 SSH 连接数

pool_idle_timeout_seconds

300.0

空闲连接超时(秒)

pool_cleanup_interval_seconds

60.0

连接池清理间隔(秒)

max_concurrent_ssh_connections

20

所有目标的全局上限;超出时返回 HTTP 503

watcher_debounce_seconds

2.0

配置重新加载之间的最小间隔;0 表示禁用

trusted_proxies

[]

可信反向代理 IP(IPv4/IPv6)

SFTP 设置(settings.sftp

设置

默认值

描述

sftp.sandbox_root

"/"

SFTP 路径校验的根目录

sftp.max_path_length

4096

允许的 SFTP 路径最大长度(字节);0 表示禁用

密钥

SSH 目标密码和 API 密钥哈希可以与主配置分离,放入 <config_dir>/secrets.jsonMCP_SSH_SECRET_* 环境变量中。优先级:

environment variables  >  secrets.json  >  ssh-mcp-config.json

密钥来源

作用

secrets.json

按目标的 password 和按密钥的 key_hash 覆盖(按名称匹配)

MCP_SSH_SECRET_PASSWORD_<TARGET_ID>

覆盖 ssh_targets[<TARGET_ID>].password

MCP_SSH_SECRET_API_KEY_<KEY_NAME>

覆盖 api_keys 条目 <KEY_NAME>key_hash

<TARGET_ID><KEY_NAME> 会转换为大写,- 变为 _。API 密钥值必须是哈希字符串,而不是原始密钥。

环境变量与 CLI 标志

环境变量

CLI 标志

默认值

旧版回退

MCP_SSH_CONFIG_PATH

--config

/config

CONFIG_DIR

MCP_SSH_SSH_KEY

--ssh-key

ssh_key

SSH_KEY_PATH

MCP_SSH_LOG_DIR

--log-dir

/logs

LOG_DIR

MAX_OUTPUT_LENGTH

--max-output

50000

CONFIG_API_ENABLED

false

CONFIG_API_TOKEN

(启用 API 时必填)

--fix-permissions

False

--print-default-config

CLI 标志优先于环境变量。任何 settings 键都可以在运行时通过 MCP_SSH_SETTING_<KEY> 覆盖(转换为大写,- 变为 _)。

热重载

服务器会轮询配置文件以检测更改(间隔 15 秒,去抖 2 秒)。检测到更改后,它会重新加载、校验,并以原子方式替换为新配置。配置更改回调(授权规则重建、连接池刷新)会在替换成功后运行。在可用时使用基于 watchdog 的文件监控。


可观测性

健康检查

GET /health 返回 {"status": "ok"} 以及连接池统计信息。容器的 HEALTHCHECK 使用此端点。

Prometheus 指标

GET /metrics 在专用注册表上暴露指标,所有指标均以 mcpssh_ 为前缀:

指标

类型

标签

mcpssh_requests_total

Counter

tool, status(成功/错误/拒绝)

mcpssh_ssh_connections_total

Counter

target

mcpssh_ssh_connection_duration_seconds

Histogram

target

mcpssh_auth_denials_total

Counter

reason

mcpssh_command_duration_seconds

Histogram

target

mcpssh_pool_active_connections

Gauge

target

mcpssh_pool_idle_connections

Gauge

target

mcpssh_pool_created_total

Counter

target

结构化日志

mcp-ssh 服务器支持通过配置文件中的 settings.logging.log_targets 配置可插拔的日志目标。每个目标都是一个独立的驱动程序,接收所有日志条目。

默认行为

默认情况下,日志条目以人类可读的文本格式写入 stdout。这适用于容器日志由运行时捕获的 Docker 环境。

日志目标类型

目标

配置值

格式

描述

标准输出

"stdout"

Text

写入标准输出。默认目标。

JSON 文件

"jsonfile"

JSONL

将JSON对象逐行写入文件。

文本文件

"file"

Text

将人类可读的文本写入文件。

配置

{
  "settings": {
    "log_level": "INFO",
    "logging": {
      "log_targets": [
        { "target": "stdout" },
        { "target": "jsonfile", "filepath": "logs/ssh-mcp.log" }
      ],
      "max_log_output": 4096,
      "compress_rotated": true
    }
  }
}

日志级别

  • 配置文件: 设置 settings.log_level 来控制默认级别。

  • 环境变量: 设置 MCP_SSH_LOG_LEVEL 来覆盖配置文件中的默认值(例如 MCP_SSH_LOG_LEVEL=DEBUG)。

  • 每个目标: 每个日志目标可以有自己的 log_level 来覆盖默认值。

旧版配置

如果 settings.logging 不存在,服务器将回退到日志目录(默认为 /logs)中的单个 JSONL 文件目标。这保持与现有配置的向后兼容性。

文本格式

标准输出和文本文件目标使用以下格式:

2025-01-15 10:30:00 INFO ssh_execute_command: Command executed on server1

JSON 格式

JSON 文件目标每行写入一个 JSON 对象:

{"timestamp": "2025-01-15T10:30:00+00:00", "event": "ssh_execute_command", "level": "INFO", "message": "Command executed on server1", "request_id": "abc-123", "log_level": "INFO", "log_format_version": 1}

文件轮转

基于文件的目标超过 max_file_size_mb(默认:10 MiB)时会进行轮转,保留 backup_count 个备份(默认:5)。当 compress_rotatedtrue 时,轮转文件会以 gzip 压缩。

配置变更事件

事件

含义

config.load

启动时加载初始配置

config.reload

从磁盘重新读取配置(带有 successchanged_keystargets_addedtargets_removed

config.migrated

已应用模式迁移(from_versionto_version

config.default_created

已复制捆绑的默认配置

config.fallback

回退到内存中的默认值

config.callback_error

配置更改回调引发异常


配置 API 与 Web 仪表盘

统一容器包含可选的配置 API 与 Web 仪表盘——这是针对您的 SSH 策略、目标、命令规则和备份的完整管理平面。无需编辑配置文件。此功能默认禁用。

功能一览

  • Web 仪表盘 — 一个响应式单页应用,包含 5 个页面:SSH 目标、阻止模式、命令规则、设置和备份。使用您的 API 令牌登录,即可在浏览器中管理一切。

  • REST API — 对每个配置部分提供完整的增删改查,外加配置验证、API 密钥哈希、备份管理和内联 SSH 连通性测试。

  • API 密钥哈希工具 — 将明文 API 密钥哈希为可直接用于配置的 PBKDF2 字符串。不再需要猜测哈希格式。

  • 备份与恢复 — 每次写入时自动备份配置;可从仪表盘或 API 列出、恢复或删除备份。

  • 原子化、线程安全的写入 — 所有配置写入都会经过验证、通过线程锁串行化,并原子化地写入磁盘。

  • Swagger UI 与 ReDoc — 自动生成的交互式 API 文档,位于 /api/docs/api/redoc

启用配置 API

在您的 compose.yaml.env 文件中设置以下环境变量:

变量

默认值

描述

CONFIG_API_ENABLED

false

设置为 true 以启用配置 API

CONFIG_API_TOKEN

(启用时必填)

用于对 API 请求进行身份验证的 Bearer 令牌

services:
  mcp-ssh:
    environment:
      CONFIG_API_ENABLED: "true"
      CONFIG_API_TOKEN: "your-secret-token-here"

API 端点

所有端点都挂载在 /api 上,与 MCP 服务器共用同一个 Starlette ASGI 应用。

健康检查与工具

方法

路径

描述

GET

/api/health

配置 API 的健康检查(无需身份验证)

POST

/api/hash-key

将明文 API 密钥哈希为 PBKDF2-HMAC-SHA256 字符串

GET

/api/config/schema

返回配置 JSON Schema(无需身份验证)

POST

/api/config/validate

验证配置字典而不写入磁盘

配置

方法

路径

描述

GET

/api/config

获取完整配置(对机密信息进行脱敏)

PUT

/api/config

替换完整配置

GET

/api/config/{section}

获取单个配置部分(settingsssh_targetsallowed_commandsblock_patterns

PUT

/api/config/{section}

替换单个配置部分

SSH 目标

方法

路径

描述

GET

/api/config/ssh_targets/{name}

获取特定的 SSH 目标(机密信息已剥离)

PUT

/api/config/ssh_targets/{name}

创建或替换 SSH 目标

DELETE

/api/config/ssh_targets/{name}

删除 SSH 目标

POST

/api/config/ssh_targets/{name}/check

通过目标的 checkcommand 测试 SSH 连通性

命令规则

方法

路径

描述

GET

/api/config/allowed_commands

列出允许的命令规则(通过 GET /api/config/{section}

PUT

/api/config/allowed_commands

替换允许的命令规则(通过 PUT /api/config/{section}

阻止模式

方法

路径

描述

GET

/api/config/block_patterns

列出阻止模式(通过 GET /api/config/{section}

PUT

/api/config/block_patterns

替换所有阻止模式

POST

/api/config/block_patterns

追加一个阻止模式

PUT

/api/config/block_patterns/{index}

按索引替换单个阻止模式

DELETE

/api/config/block_patterns/{index}

按索引移除单个阻止模式

备份

方法

路径

描述

GET

/api/backups

列出配置备份(最新的在前)

POST

/api/backups/{name}/restore

从备份恢复配置

DELETE

/api/backups/{name}

删除备份文件

身份验证

所有 API 请求(/api/health/api/config/schema 除外)都要求在 Authorization 头中携带 Bearer 令牌:

curl -H "Authorization: Bearer your-secret-token-here" http://localhost:9080/api/config

Web 仪表盘

启用后,可在 http://localhost:9080/ui/ 访问响应式单页应用——这是一个使用 Tailwind CSS 构建的完整管理界面。无需页面刷新,每次操作都有 toast 通知,并通过模态对话框进行编辑。

页面

功能

SSH 目标

查看、添加、编辑、删除目标;通过 checkcommand 进行内联连通性测试;以表格形式查看主机/端口/用户名

阻止模式

添加、编辑(按索引)、删除单个模式;查看完整模式列表

命令规则

编辑默认、API 密钥和网络规则;包含目标和命令列表的完整规则编辑器

设置

编辑所有服务器设置:SFTP 沙箱、速率限制、日志记录、连接池、断路器等等

备份

列出、恢复和删除配置备份;每个备份都有时间戳和大小

其他功能:

  • 基于令牌的登录,支持会话管理(存储在 sessionStorage 中)

  • 配置验证 — 更改在写入之前会经过验证

  • API 密钥哈希 — 可直接在仪表盘中哈希明文密钥

  • 响应式设计 — 适用于桌面端和移动端

  • Toast 通知 — 每次操作都有成功/错误反馈

Swagger / ReDoc

交互式 API 文档由 FastAPI 自动生成:

  • Swagger UI:http://localhost:9080/api/docs

  • ReDoc:http://localhost:9080/api/redoc


部署

Docker Compose

compose.yaml 定义了一个 mcp-ssh 服务,该服务同时承载 MCP 服务器以及(可选的)配置 API 与 Web 仪表盘。配置 API 通过 CONFIG_API_ENABLED 环境变量启用(默认:false)。

mcp-ssh — MCP SSH 网关 + 配置 API

主机路径

容器路径

模式

./config

/config

rw

./logs

/logs

rw

./ssh_key

/app/ssh_key

ro

./ssh_key.pub

/app/ssh_key.pub

ro

暴露在主机端口 9080 上(映射到容器端口 8080)。运行镜像为 python:3.13-alpine,并带有哈希固定的摘要。进程由非 root 用户 mcpssh 运行。在构建时于 sbom 阶段生成 CycloneDX SBOM。

配置 API 与 Web 仪表盘(可选)

通过在你的 .env 文件或环境中设置 CONFIG_API_ENABLED=true 来启用配置 API:

# Generate an auth token
openssl rand -hex 32
CONFIG_API_ENABLED=true
CONFIG_API_TOKEN=<your-token>

启用后,配置 API 将挂载在 /api 上,与 MCP 网关共用同一个 HTTP 服务器。它提供:

  • REST API,位于 http://localhost:9080/api/... — 对 SSH 目标、阻止模式、命令规则、备份和设置提供完整的增删改查

  • Web 仪表盘(GUI),位于 http://localhost:9080/ui/ — 用于可视化策略管理的单页应用(SSH 目标、阻止模式、命令规则、设置、备份)

  • API 文档,位于 http://localhost:9080/api/docs(Swagger UI)和 http://localhost:9080/api/redoc(ReDoc)

Makefile

命令

描述

make build

构建 Docker 镜像(ghcr.io/gelse/ssh-mcp:latest

make up

docker compose up -d

make down

docker compose down

make test

运行单元测试

make config-test

运行 config-api 单元测试

make integrationtest

构建测试镜像,运行集成测试

make clean-test

清理测试产物和容器

从 GHCR 拉取

Docker 镜像会自动构建并发布到 GitHub Container Registry:

docker pull ghcr.io/gelse/ssh-mcp:latest

局限性与威胁模型

ssh-mcp 不是什么

  • 不是 shell。 你无法获得交互式终端会话。所有执行都是一次性的命令调用。

  • 不是文件管理器。 SFTP 仅限于单文件上传/下载,并带有路径验证和沙箱强制。不支持目录列表,不支持递归操作。

  • 不是网络防火墙。 限流按 IP 进行,采用固定的默认值。它防护的是失控的客户端,而非坚决的攻击者。

威胁模型

威胁

缓解措施

通过命令链进行的命令注入(cmd1 && cmd2

命令分段 — 每个分段都会运行完整的授权链

对敏感路径的 shell 重定向(> /etc/passwd

重定向目标守卫会拒绝重定向到 /dev//proc//sys/

SFTP 中的路径遍历

8 层路径验证:空字节检查、控制字符剥离、点段归一化、符号链接解析、沙箱根目录强制

通过 block_patterns 进行的 ReDoS

加载时静态筛查 + 运行时超时守卫

API 密钥暴力破解

PBKDF2-HMAC-SHA256 配合恒定时间验证;按 IP 限流

日志注入

所有用户可控字段在记录日志前进行换行符清理

配置中的机密信息

secrets.json 分离、MCP_SSH_SECRET_* 环境变量、0600 文件权限

不在范围之内

  • TLS 终止(由你的反向代理处理)

  • API 密钥之外的用户认证(无 OAuth,应用层无 mTLS)

  • SSH 会话多路复用(无 tmux/screen 透传)

  • 审计日志防篡改(日志是本地文件;请使用你自己的日志传输来保证不可变性)


开发

项目结构

  • server.py — FastMCP 应用工厂 + CLI 入口点

  • lib/ — 30 个单一职责模块(认证、配置、SSH 客户端、文件传输、日志等)

  • config-api/ — 配置 API + Web 仪表盘(FastAPI,当 CONFIG_API_ENABLED=true 时挂载于 /api

  • tests/ — 36 个单元测试文件 + 使用真实 Docker 容器的集成测试

技术栈

Python 3.13, FastMCP 3.4.x, paramiko 5.0, Starlette 1.4, FastAPI 0.115+, Pydantic 2.10+, httpx 0.28+, uvicorn 0.34+

运行测试

# Unit tests (fast inner loop)
source .venv/bin/activate
python -m pytest tests/test_<module>.py -x

# Full unit test suite
make test

# Integration tests (requires Docker)
make integrationtest

添加新工具

AGENTS.md 中的完整示例 会端到端地演示如何添加一个新的 @mcp.tool() 处理器:常量、类型、重新导出、处理器、测试、提交。

无 Lint/类型检查工具

项目没有 ruffmypypyrightflake8 配置。格式化遵循 .editorconfig 默认值(Python 使用 4 个空格,每行 88 个字符)。


路线图

  • 用于可视化策略管理的配置 GUI


许可证

MIT 许可证 — 详情请参阅 LICENSE

A
license - permissive license
Not graded
quality - not tested
A
maintenance

Maintenance

Maintainers
<1hResponse time
Release cycle
1Releases (12mo)
Commit activity

Related MCP Servers

  • A
    license
    Not graded
    quality
    D
    maintenance
    Enables secure remote access operations through SSH, SFTP, rsync, VPN, and tunneling with enterprise-grade policy enforcement and audit logging. Provides AI assistants with secure, policy-driven access to remote systems while maintaining comprehensive audit trails and zero-trust security.
    1
    Apache 2.0
  • A
    license
    B
    quality
    A
    maintenance
    Provides policy-driven, auditable SSH access to server fleets for AI assistants with zero-trust security controls, command whitelisting, and comprehensive audit logging to safely manage infrastructure.
    13
    27
    Apache 2.0
  • A
    license
    A
    quality
    C
    maintenance
    Enables AI assistants to securely execute remote SSH commands, perform file transfers, and monitor system status through a standardized interface. It features robust security controls including command whitelisting, blacklisting, and credential isolation to prevent unauthorized operations.
    10
    22
    MIT
  • A
    license
    Not graded
    quality
    A
    maintenance
    Enables AI assistants to securely execute SSH commands on remote servers with connection pooling, session isolation, and a web audit panel.
    3
    MIT

View all related MCP servers

Related MCP Connectors

  • Operate Linux, macOS and Windows from your LLM. Every action runs through an auditable allowlist.

  • Let AI operate servers without SSH. Choose actions, approve risky changes, and audit every step.

  • Agent payments, API key vaulting, and governed mandates. Agents spend within user-defined limits.

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/gelse/ssh-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server