ssh-licco 2.3.1__tar.gz → 2.5.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {ssh_licco-2.3.1/ssh_licco.egg-info → ssh_licco-2.5.0}/PKG-INFO +4 -33
- {ssh_licco-2.3.1 → ssh_licco-2.5.0}/README.md +3 -32
- {ssh_licco-2.3.1 → ssh_licco-2.5.0}/pyproject.toml +1 -1
- {ssh_licco-2.3.1 → ssh_licco-2.5.0/ssh_licco.egg-info}/PKG-INFO +4 -33
- {ssh_licco-2.3.1 → ssh_licco-2.5.0}/ssh_licco.egg-info/SOURCES.txt +0 -2
- {ssh_licco-2.3.1 → ssh_licco-2.5.0}/ssh_mcp/__init__.py +2 -2
- {ssh_licco-2.3.1 → ssh_licco-2.5.0}/ssh_mcp/connection_config.py +1 -1
- {ssh_licco-2.3.1 → ssh_licco-2.5.0}/ssh_mcp/handlers/connect.py +2 -34
- {ssh_licco-2.3.1 → ssh_licco-2.5.0}/ssh_mcp/handlers/execute.py +592 -594
- {ssh_licco-2.3.1 → ssh_licco-2.5.0}/ssh_mcp/handlers/schemas.py +494 -495
- {ssh_licco-2.3.1 → ssh_licco-2.5.0}/ssh_mcp/handlers/utils.py +0 -47
- {ssh_licco-2.3.1 → ssh_licco-2.5.0}/ssh_mcp/key_manager.py +0 -12
- {ssh_licco-2.3.1 → ssh_licco-2.5.0}/ssh_mcp/security.py +112 -0
- {ssh_licco-2.3.1 → ssh_licco-2.5.0}/ssh_mcp/server.py +1720 -1917
- {ssh_licco-2.3.1 → ssh_licco-2.5.0}/tests/test_security.py +61 -0
- {ssh_licco-2.3.1 → ssh_licco-2.5.0}/tests/test_server.py +716 -599
- ssh_licco-2.3.1/ssh_mcp/runtime_guard.py +0 -178
- ssh_licco-2.3.1/ssh_mcp/secret_provider.py +0 -226
- {ssh_licco-2.3.1 → ssh_licco-2.5.0}/LICENSE +0 -0
- {ssh_licco-2.3.1 → ssh_licco-2.5.0}/setup.cfg +0 -0
- {ssh_licco-2.3.1 → ssh_licco-2.5.0}/ssh_licco.egg-info/dependency_links.txt +0 -0
- {ssh_licco-2.3.1 → ssh_licco-2.5.0}/ssh_licco.egg-info/entry_points.txt +0 -0
- {ssh_licco-2.3.1 → ssh_licco-2.5.0}/ssh_licco.egg-info/requires.txt +0 -0
- {ssh_licco-2.3.1 → ssh_licco-2.5.0}/ssh_licco.egg-info/top_level.txt +0 -0
- {ssh_licco-2.3.1 → ssh_licco-2.5.0}/ssh_mcp/__main__.py +0 -0
- {ssh_licco-2.3.1 → ssh_licco-2.5.0}/ssh_mcp/audit_logger.py +0 -0
- {ssh_licco-2.3.1 → ssh_licco-2.5.0}/ssh_mcp/batch_executor.py +0 -0
- {ssh_licco-2.3.1 → ssh_licco-2.5.0}/ssh_mcp/cli.py +0 -0
- {ssh_licco-2.3.1 → ssh_licco-2.5.0}/ssh_mcp/clients/__init__.py +0 -0
- {ssh_licco-2.3.1 → ssh_licco-2.5.0}/ssh_mcp/clients/additional_clients.py +0 -0
- {ssh_licco-2.3.1 → ssh_licco-2.5.0}/ssh_mcp/clients/factory.py +0 -0
- {ssh_licco-2.3.1 → ssh_licco-2.5.0}/ssh_mcp/clients/interface.py +0 -0
- {ssh_licco-2.3.1 → ssh_licco-2.5.0}/ssh_mcp/clients/paramiko_client.py +0 -0
- {ssh_licco-2.3.1 → ssh_licco-2.5.0}/ssh_mcp/config_manager.py +0 -0
- {ssh_licco-2.3.1 → ssh_licco-2.5.0}/ssh_mcp/connection_pool.py +0 -0
- {ssh_licco-2.3.1 → ssh_licco-2.5.0}/ssh_mcp/exceptions.py +0 -0
- {ssh_licco-2.3.1 → ssh_licco-2.5.0}/ssh_mcp/executor.py +0 -0
- {ssh_licco-2.3.1 → ssh_licco-2.5.0}/ssh_mcp/handlers/__init__.py +0 -0
- {ssh_licco-2.3.1 → ssh_licco-2.5.0}/ssh_mcp/handlers/context.py +0 -0
- {ssh_licco-2.3.1 → ssh_licco-2.5.0}/ssh_mcp/handlers/docker.py +0 -0
- {ssh_licco-2.3.1 → ssh_licco-2.5.0}/ssh_mcp/handlers/file_transfer.py +0 -0
- {ssh_licco-2.3.1 → ssh_licco-2.5.0}/ssh_mcp/handlers/host.py +0 -0
- {ssh_licco-2.3.1 → ssh_licco-2.5.0}/ssh_mcp/handlers/key.py +0 -0
- {ssh_licco-2.3.1 → ssh_licco-2.5.0}/ssh_mcp/handlers/process.py +0 -0
- {ssh_licco-2.3.1 → ssh_licco-2.5.0}/ssh_mcp/handlers/session.py +0 -0
- {ssh_licco-2.3.1 → ssh_licco-2.5.0}/ssh_mcp/logging_config.py +0 -0
- {ssh_licco-2.3.1 → ssh_licco-2.5.0}/ssh_mcp/service.py +0 -0
- {ssh_licco-2.3.1 → ssh_licco-2.5.0}/ssh_mcp/session_manager.py +0 -0
- {ssh_licco-2.3.1 → ssh_licco-2.5.0}/ssh_mcp/tunnel.py +0 -0
- {ssh_licco-2.3.1 → ssh_licco-2.5.0}/ssh_mcp/watchdog.py +0 -0
- {ssh_licco-2.3.1 → ssh_licco-2.5.0}/tests/test_config_manager.py +0 -0
- {ssh_licco-2.3.1 → ssh_licco-2.5.0}/tests/test_key_manager.py +0 -0
- {ssh_licco-2.3.1 → ssh_licco-2.5.0}/tests/test_session_manager.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ssh-licco
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.5.0
|
|
4
4
|
Summary: SSH Model Context Protocol Server - Enable SSH functionality for AI models
|
|
5
5
|
Author-email: SSH LICCO Team <contact@ssh-licco.dev>
|
|
6
6
|
License: MIT
|
|
@@ -233,7 +233,6 @@ export SSH_EXTRA_ALLOWED_COMMANDS="git,pip,npm"
|
|
|
233
233
|
|
|
234
234
|
- **[MCP_CONFIG_GUIDE.md](MCP_CONFIG_GUIDE.md)** - 完整配置指南,包含 5 种使用场景示例
|
|
235
235
|
- **[SECURITY_CONFIG_GUIDE.md](SECURITY_CONFIG_GUIDE.md)** - 安全配置详解
|
|
236
|
-
- **[docs/SECURITY_HARDENING.md](docs/SECURITY_HARDENING.md)** - 生产加固四项方案(v2.1.0 新增)
|
|
237
236
|
|
|
238
237
|
### 🛡️ 硬拦截灾难性命令(v2.2.0 新增)
|
|
239
238
|
|
|
@@ -256,21 +255,6 @@ mv /path/to/junk /tmp/.trash_$(date +%s)/
|
|
|
256
255
|
|
|
257
256
|
命中硬拦截时会输出 `WARNING` 审计日志(含 category 与命令),便于 SOC 监控。
|
|
258
257
|
|
|
259
|
-
### 🏛️ 生产加固四项(v2.1.0 + v2.2.0 演进)
|
|
260
|
-
|
|
261
|
-
SSH 跳板模式部署的生产必做加固,补齐跳板机单点风险:
|
|
262
|
-
|
|
263
|
-
| 加固点 | 开关 | 说明 |
|
|
264
|
-
|--------|------|------|
|
|
265
|
-
| 1. 运行账号最小权限 | `SSH_RUNTIME_GUARD=true` | 专用普通账号运行,拒绝 root/sudo 启动 |
|
|
266
|
-
| 2. 密钥不落地磁盘 | `SSH_SECRET_PROVIDER_ENABLED=true` | 私钥从 KMS 临时拉取到内存,用完清零,不落盘 |
|
|
267
|
-
| 3. 双层命令拦截 | `SSH_REMOTE_GUARD=true` + 远端 ForceCommand | 跳板机侧禁元字符 + 远端二次白名单校验 |
|
|
268
|
-
| 4. 灾难性命令硬拦截(v2.2.0 新增) | 默认开启,不可关闭 | rm -rf 绝对路径、mkfs、raw-disk dd、fork-bomb、chmod -R 777/000 /、`> /dev/sd*` 等在 MCP 网关层被**无条件**拒绝;任何安全级别、`confirm_dangerous=true` 等均无法绕过 |
|
|
269
|
-
|
|
270
|
-
> **关于 v2.1.0 引入的"高危操作审批"项**:v2.1.0 曾提供 `SSH_APPROVAL_GATE=true` + 3 个审批工具作为第 4 项加固。v2.2.0 起该项被**硬拦截取代**——审批流程依赖 AI 自报命令,存在闭环风险;硬拦截更直接,灾难性命令在 MCP 网关层就被拒绝,运维侧不需要再走"先申请再审批"流程。`SSH_APPROVAL_GATE` 环境变量与 `ssh_mcp/approval.py` / `ssh_mcp/handlers/approval.py` 代码已在 v2.2.0 删除。
|
|
271
|
-
|
|
272
|
-
完整方案见 [docs/SECURITY_HARDENING.md](docs/SECURITY_HARDENING.md)。
|
|
273
|
-
|
|
274
258
|
---
|
|
275
259
|
|
|
276
260
|
## 🛠️ 可用工具(v2.2.0 维持 9 个;v2.1.0 曾增加的 3 个审批工具因流程闭环风险已下线)
|
|
@@ -278,7 +262,7 @@ SSH 跳板模式部署的生产必做加固,补齐跳板机单点风险:
|
|
|
278
262
|
| 工具 | 描述 | 核心能力 |
|
|
279
263
|
|------|------|---------|
|
|
280
264
|
| `ssh_connect` | 连接管理 | 自动读取环境变量/配置,支持密码+密钥认证,可保存配置,登录后自动执行命令 |
|
|
281
|
-
| `ssh_execute` | 命令执行 | 自动连接、智能后台检测、长任务等待、超时控制,支持 nohup/screen/tmux 三种后台模式;v2.
|
|
265
|
+
| `ssh_execute` | 命令执行 | 自动连接、智能后台检测、长任务等待、超时控制,支持 nohup/screen/tmux 三种后台模式;v2.2.0 起对灾难性命令(rm -rf 绝对路径、mkfs、raw-disk dd、fork-bomb 等)做**无条件硬拦截** |
|
|
282
266
|
| `ssh_disconnect` | 会话管理 | 断开指定会话 OR 列出所有活跃会话 |
|
|
283
267
|
| `ssh_file_transfer` | 文件传输 | 上传/下载/列表/写入/追加/删除/创建目录/查看元信息(8 种操作);v2.1.3+ delete 操作新增 Windows/Unix 敏感路径拦截与路径遍历防护 |
|
|
284
268
|
| `ssh_host` | 主机管理 | `action=list/add/remove` 增删查主机配置 |
|
|
@@ -458,7 +442,6 @@ localhost:5432 - accepting connections
|
|
|
458
442
|
> 下表所有变量均被代码读取。**注意**:
|
|
459
443
|
> - `SSH_RATE_LIMIT` 是 **bool** 总开关(true/false),`SSH_RATE_LIMIT_MAX` 才是次数上限,两者分开配置
|
|
460
444
|
> - 主机密钥检查(strict_host_key_checking)**不通过 env 配置**,请用 `ssh_connect` 工具参数或 `hosts.json`
|
|
461
|
-
> - 详细分组说明见 [docs/SECURITY_HARDENING.md#环境变量速查](docs/SECURITY_HARDENING.md#环境变量速查v230-完整列表)
|
|
462
445
|
|
|
463
446
|
| 分类 | 变量 | 默认 | 说明 |
|
|
464
447
|
|------|------|------|------|
|
|
@@ -470,19 +453,7 @@ localhost:5432 - accepting connections
|
|
|
470
453
|
| **限流** | `SSH_RATE_LIMIT` | `true` | 限流总开关(bool) |
|
|
471
454
|
| | `SSH_RATE_LIMIT_MAX` | `30` | 限流次数上限 |
|
|
472
455
|
| | `SSH_RATE_LIMIT_WINDOW` | `60` | 限流窗口(秒) |
|
|
473
|
-
|
|
|
474
|
-
| | `SSH_RUNTIME_ALLOW_ROOT` | `false` | 是否允许 root 启动 |
|
|
475
|
-
| | `SSH_RUNTIME_ALLOWED_USERS` | (空) | 允许启动的账号白名单 |
|
|
476
|
-
| | `SSH_RUNTIME_BLOCK_SUDO` | `true` | 是否拦截 sudo 上下文 |
|
|
477
|
-
| **加固点 2** | `SSH_SECRET_PROVIDER_ENABLED` | `false` | secret provider 总开关 |
|
|
478
|
-
| | `SSH_SECRET_PROVIDER` | `env` | 凭证 provider:`env` / `command` / `http` |
|
|
479
|
-
| | `SSH_SECRET_ENV_KEY_<NAME>` | — | env provider:私钥环境变量 |
|
|
480
|
-
| | `SSH_SECRET_COMMAND_<NAME>` | — | command provider:拉取私钥的命令 |
|
|
481
|
-
| | `SSH_SECRET_HTTP_URL_<NAME>` | — | http provider:拉取私钥的 URL |
|
|
482
|
-
| | `SSH_SECRET_HTTP_TOKEN` | — | http provider:Bearer token |
|
|
483
|
-
| | `SSH_SECRET_HTTP_TIMEOUT` | `15` | http provider:HTTP 超时(秒) |
|
|
484
|
-
| **加固点 3** | `SSH_REMOTE_GUARD` | `false` | 远端元字符拦截总开关 |
|
|
485
|
-
| **加固点 4** | *(无 env)* | — | 灾难性命令硬拦截,零配置零绕过 |
|
|
456
|
+
| **硬拦截** | *(无 env)* | — | 灾难性命令硬拦截,零配置零绕过 |
|
|
486
457
|
| **连接默认** | `SSH_HOST` / `SSH_PORT` / `SSH_USER` / `SSH_PASSWORD` | `127.0.0.1` / `22` / `root` / (空) | 单 host 模式默认连接参数 |
|
|
487
458
|
| | `SSH_TIMEOUT` | `60` | 连接超时(秒) |
|
|
488
459
|
| | `SSH_KEEPALIVE_INTERVAL` | `30` | keepalive 间隔(秒) |
|
|
@@ -651,7 +622,7 @@ ssh-licco 的核心依赖是:
|
|
|
651
622
|
|
|
652
623
|
| 指标 | 状态 |
|
|
653
624
|
|------|------|
|
|
654
|
-
| **测试用例** |
|
|
625
|
+
| **测试用例** | 418 passed, 0 skipped |
|
|
655
626
|
| **覆盖率** | 16 个源模块全覆盖 |
|
|
656
627
|
| **测试框架** | pytest + pytest-asyncio |
|
|
657
628
|
|
|
@@ -200,7 +200,6 @@ export SSH_EXTRA_ALLOWED_COMMANDS="git,pip,npm"
|
|
|
200
200
|
|
|
201
201
|
- **[MCP_CONFIG_GUIDE.md](MCP_CONFIG_GUIDE.md)** - 完整配置指南,包含 5 种使用场景示例
|
|
202
202
|
- **[SECURITY_CONFIG_GUIDE.md](SECURITY_CONFIG_GUIDE.md)** - 安全配置详解
|
|
203
|
-
- **[docs/SECURITY_HARDENING.md](docs/SECURITY_HARDENING.md)** - 生产加固四项方案(v2.1.0 新增)
|
|
204
203
|
|
|
205
204
|
### 🛡️ 硬拦截灾难性命令(v2.2.0 新增)
|
|
206
205
|
|
|
@@ -223,21 +222,6 @@ mv /path/to/junk /tmp/.trash_$(date +%s)/
|
|
|
223
222
|
|
|
224
223
|
命中硬拦截时会输出 `WARNING` 审计日志(含 category 与命令),便于 SOC 监控。
|
|
225
224
|
|
|
226
|
-
### 🏛️ 生产加固四项(v2.1.0 + v2.2.0 演进)
|
|
227
|
-
|
|
228
|
-
SSH 跳板模式部署的生产必做加固,补齐跳板机单点风险:
|
|
229
|
-
|
|
230
|
-
| 加固点 | 开关 | 说明 |
|
|
231
|
-
|--------|------|------|
|
|
232
|
-
| 1. 运行账号最小权限 | `SSH_RUNTIME_GUARD=true` | 专用普通账号运行,拒绝 root/sudo 启动 |
|
|
233
|
-
| 2. 密钥不落地磁盘 | `SSH_SECRET_PROVIDER_ENABLED=true` | 私钥从 KMS 临时拉取到内存,用完清零,不落盘 |
|
|
234
|
-
| 3. 双层命令拦截 | `SSH_REMOTE_GUARD=true` + 远端 ForceCommand | 跳板机侧禁元字符 + 远端二次白名单校验 |
|
|
235
|
-
| 4. 灾难性命令硬拦截(v2.2.0 新增) | 默认开启,不可关闭 | rm -rf 绝对路径、mkfs、raw-disk dd、fork-bomb、chmod -R 777/000 /、`> /dev/sd*` 等在 MCP 网关层被**无条件**拒绝;任何安全级别、`confirm_dangerous=true` 等均无法绕过 |
|
|
236
|
-
|
|
237
|
-
> **关于 v2.1.0 引入的"高危操作审批"项**:v2.1.0 曾提供 `SSH_APPROVAL_GATE=true` + 3 个审批工具作为第 4 项加固。v2.2.0 起该项被**硬拦截取代**——审批流程依赖 AI 自报命令,存在闭环风险;硬拦截更直接,灾难性命令在 MCP 网关层就被拒绝,运维侧不需要再走"先申请再审批"流程。`SSH_APPROVAL_GATE` 环境变量与 `ssh_mcp/approval.py` / `ssh_mcp/handlers/approval.py` 代码已在 v2.2.0 删除。
|
|
238
|
-
|
|
239
|
-
完整方案见 [docs/SECURITY_HARDENING.md](docs/SECURITY_HARDENING.md)。
|
|
240
|
-
|
|
241
225
|
---
|
|
242
226
|
|
|
243
227
|
## 🛠️ 可用工具(v2.2.0 维持 9 个;v2.1.0 曾增加的 3 个审批工具因流程闭环风险已下线)
|
|
@@ -245,7 +229,7 @@ SSH 跳板模式部署的生产必做加固,补齐跳板机单点风险:
|
|
|
245
229
|
| 工具 | 描述 | 核心能力 |
|
|
246
230
|
|------|------|---------|
|
|
247
231
|
| `ssh_connect` | 连接管理 | 自动读取环境变量/配置,支持密码+密钥认证,可保存配置,登录后自动执行命令 |
|
|
248
|
-
| `ssh_execute` | 命令执行 | 自动连接、智能后台检测、长任务等待、超时控制,支持 nohup/screen/tmux 三种后台模式;v2.
|
|
232
|
+
| `ssh_execute` | 命令执行 | 自动连接、智能后台检测、长任务等待、超时控制,支持 nohup/screen/tmux 三种后台模式;v2.2.0 起对灾难性命令(rm -rf 绝对路径、mkfs、raw-disk dd、fork-bomb 等)做**无条件硬拦截** |
|
|
249
233
|
| `ssh_disconnect` | 会话管理 | 断开指定会话 OR 列出所有活跃会话 |
|
|
250
234
|
| `ssh_file_transfer` | 文件传输 | 上传/下载/列表/写入/追加/删除/创建目录/查看元信息(8 种操作);v2.1.3+ delete 操作新增 Windows/Unix 敏感路径拦截与路径遍历防护 |
|
|
251
235
|
| `ssh_host` | 主机管理 | `action=list/add/remove` 增删查主机配置 |
|
|
@@ -425,7 +409,6 @@ localhost:5432 - accepting connections
|
|
|
425
409
|
> 下表所有变量均被代码读取。**注意**:
|
|
426
410
|
> - `SSH_RATE_LIMIT` 是 **bool** 总开关(true/false),`SSH_RATE_LIMIT_MAX` 才是次数上限,两者分开配置
|
|
427
411
|
> - 主机密钥检查(strict_host_key_checking)**不通过 env 配置**,请用 `ssh_connect` 工具参数或 `hosts.json`
|
|
428
|
-
> - 详细分组说明见 [docs/SECURITY_HARDENING.md#环境变量速查](docs/SECURITY_HARDENING.md#环境变量速查v230-完整列表)
|
|
429
412
|
|
|
430
413
|
| 分类 | 变量 | 默认 | 说明 |
|
|
431
414
|
|------|------|------|------|
|
|
@@ -437,19 +420,7 @@ localhost:5432 - accepting connections
|
|
|
437
420
|
| **限流** | `SSH_RATE_LIMIT` | `true` | 限流总开关(bool) |
|
|
438
421
|
| | `SSH_RATE_LIMIT_MAX` | `30` | 限流次数上限 |
|
|
439
422
|
| | `SSH_RATE_LIMIT_WINDOW` | `60` | 限流窗口(秒) |
|
|
440
|
-
|
|
|
441
|
-
| | `SSH_RUNTIME_ALLOW_ROOT` | `false` | 是否允许 root 启动 |
|
|
442
|
-
| | `SSH_RUNTIME_ALLOWED_USERS` | (空) | 允许启动的账号白名单 |
|
|
443
|
-
| | `SSH_RUNTIME_BLOCK_SUDO` | `true` | 是否拦截 sudo 上下文 |
|
|
444
|
-
| **加固点 2** | `SSH_SECRET_PROVIDER_ENABLED` | `false` | secret provider 总开关 |
|
|
445
|
-
| | `SSH_SECRET_PROVIDER` | `env` | 凭证 provider:`env` / `command` / `http` |
|
|
446
|
-
| | `SSH_SECRET_ENV_KEY_<NAME>` | — | env provider:私钥环境变量 |
|
|
447
|
-
| | `SSH_SECRET_COMMAND_<NAME>` | — | command provider:拉取私钥的命令 |
|
|
448
|
-
| | `SSH_SECRET_HTTP_URL_<NAME>` | — | http provider:拉取私钥的 URL |
|
|
449
|
-
| | `SSH_SECRET_HTTP_TOKEN` | — | http provider:Bearer token |
|
|
450
|
-
| | `SSH_SECRET_HTTP_TIMEOUT` | `15` | http provider:HTTP 超时(秒) |
|
|
451
|
-
| **加固点 3** | `SSH_REMOTE_GUARD` | `false` | 远端元字符拦截总开关 |
|
|
452
|
-
| **加固点 4** | *(无 env)* | — | 灾难性命令硬拦截,零配置零绕过 |
|
|
423
|
+
| **硬拦截** | *(无 env)* | — | 灾难性命令硬拦截,零配置零绕过 |
|
|
453
424
|
| **连接默认** | `SSH_HOST` / `SSH_PORT` / `SSH_USER` / `SSH_PASSWORD` | `127.0.0.1` / `22` / `root` / (空) | 单 host 模式默认连接参数 |
|
|
454
425
|
| | `SSH_TIMEOUT` | `60` | 连接超时(秒) |
|
|
455
426
|
| | `SSH_KEEPALIVE_INTERVAL` | `30` | keepalive 间隔(秒) |
|
|
@@ -618,7 +589,7 @@ ssh-licco 的核心依赖是:
|
|
|
618
589
|
|
|
619
590
|
| 指标 | 状态 |
|
|
620
591
|
|------|------|
|
|
621
|
-
| **测试用例** |
|
|
592
|
+
| **测试用例** | 418 passed, 0 skipped |
|
|
622
593
|
| **覆盖率** | 16 个源模块全覆盖 |
|
|
623
594
|
| **测试框架** | pytest + pytest-asyncio |
|
|
624
595
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ssh-licco
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.5.0
|
|
4
4
|
Summary: SSH Model Context Protocol Server - Enable SSH functionality for AI models
|
|
5
5
|
Author-email: SSH LICCO Team <contact@ssh-licco.dev>
|
|
6
6
|
License: MIT
|
|
@@ -233,7 +233,6 @@ export SSH_EXTRA_ALLOWED_COMMANDS="git,pip,npm"
|
|
|
233
233
|
|
|
234
234
|
- **[MCP_CONFIG_GUIDE.md](MCP_CONFIG_GUIDE.md)** - 完整配置指南,包含 5 种使用场景示例
|
|
235
235
|
- **[SECURITY_CONFIG_GUIDE.md](SECURITY_CONFIG_GUIDE.md)** - 安全配置详解
|
|
236
|
-
- **[docs/SECURITY_HARDENING.md](docs/SECURITY_HARDENING.md)** - 生产加固四项方案(v2.1.0 新增)
|
|
237
236
|
|
|
238
237
|
### 🛡️ 硬拦截灾难性命令(v2.2.0 新增)
|
|
239
238
|
|
|
@@ -256,21 +255,6 @@ mv /path/to/junk /tmp/.trash_$(date +%s)/
|
|
|
256
255
|
|
|
257
256
|
命中硬拦截时会输出 `WARNING` 审计日志(含 category 与命令),便于 SOC 监控。
|
|
258
257
|
|
|
259
|
-
### 🏛️ 生产加固四项(v2.1.0 + v2.2.0 演进)
|
|
260
|
-
|
|
261
|
-
SSH 跳板模式部署的生产必做加固,补齐跳板机单点风险:
|
|
262
|
-
|
|
263
|
-
| 加固点 | 开关 | 说明 |
|
|
264
|
-
|--------|------|------|
|
|
265
|
-
| 1. 运行账号最小权限 | `SSH_RUNTIME_GUARD=true` | 专用普通账号运行,拒绝 root/sudo 启动 |
|
|
266
|
-
| 2. 密钥不落地磁盘 | `SSH_SECRET_PROVIDER_ENABLED=true` | 私钥从 KMS 临时拉取到内存,用完清零,不落盘 |
|
|
267
|
-
| 3. 双层命令拦截 | `SSH_REMOTE_GUARD=true` + 远端 ForceCommand | 跳板机侧禁元字符 + 远端二次白名单校验 |
|
|
268
|
-
| 4. 灾难性命令硬拦截(v2.2.0 新增) | 默认开启,不可关闭 | rm -rf 绝对路径、mkfs、raw-disk dd、fork-bomb、chmod -R 777/000 /、`> /dev/sd*` 等在 MCP 网关层被**无条件**拒绝;任何安全级别、`confirm_dangerous=true` 等均无法绕过 |
|
|
269
|
-
|
|
270
|
-
> **关于 v2.1.0 引入的"高危操作审批"项**:v2.1.0 曾提供 `SSH_APPROVAL_GATE=true` + 3 个审批工具作为第 4 项加固。v2.2.0 起该项被**硬拦截取代**——审批流程依赖 AI 自报命令,存在闭环风险;硬拦截更直接,灾难性命令在 MCP 网关层就被拒绝,运维侧不需要再走"先申请再审批"流程。`SSH_APPROVAL_GATE` 环境变量与 `ssh_mcp/approval.py` / `ssh_mcp/handlers/approval.py` 代码已在 v2.2.0 删除。
|
|
271
|
-
|
|
272
|
-
完整方案见 [docs/SECURITY_HARDENING.md](docs/SECURITY_HARDENING.md)。
|
|
273
|
-
|
|
274
258
|
---
|
|
275
259
|
|
|
276
260
|
## 🛠️ 可用工具(v2.2.0 维持 9 个;v2.1.0 曾增加的 3 个审批工具因流程闭环风险已下线)
|
|
@@ -278,7 +262,7 @@ SSH 跳板模式部署的生产必做加固,补齐跳板机单点风险:
|
|
|
278
262
|
| 工具 | 描述 | 核心能力 |
|
|
279
263
|
|------|------|---------|
|
|
280
264
|
| `ssh_connect` | 连接管理 | 自动读取环境变量/配置,支持密码+密钥认证,可保存配置,登录后自动执行命令 |
|
|
281
|
-
| `ssh_execute` | 命令执行 | 自动连接、智能后台检测、长任务等待、超时控制,支持 nohup/screen/tmux 三种后台模式;v2.
|
|
265
|
+
| `ssh_execute` | 命令执行 | 自动连接、智能后台检测、长任务等待、超时控制,支持 nohup/screen/tmux 三种后台模式;v2.2.0 起对灾难性命令(rm -rf 绝对路径、mkfs、raw-disk dd、fork-bomb 等)做**无条件硬拦截** |
|
|
282
266
|
| `ssh_disconnect` | 会话管理 | 断开指定会话 OR 列出所有活跃会话 |
|
|
283
267
|
| `ssh_file_transfer` | 文件传输 | 上传/下载/列表/写入/追加/删除/创建目录/查看元信息(8 种操作);v2.1.3+ delete 操作新增 Windows/Unix 敏感路径拦截与路径遍历防护 |
|
|
284
268
|
| `ssh_host` | 主机管理 | `action=list/add/remove` 增删查主机配置 |
|
|
@@ -458,7 +442,6 @@ localhost:5432 - accepting connections
|
|
|
458
442
|
> 下表所有变量均被代码读取。**注意**:
|
|
459
443
|
> - `SSH_RATE_LIMIT` 是 **bool** 总开关(true/false),`SSH_RATE_LIMIT_MAX` 才是次数上限,两者分开配置
|
|
460
444
|
> - 主机密钥检查(strict_host_key_checking)**不通过 env 配置**,请用 `ssh_connect` 工具参数或 `hosts.json`
|
|
461
|
-
> - 详细分组说明见 [docs/SECURITY_HARDENING.md#环境变量速查](docs/SECURITY_HARDENING.md#环境变量速查v230-完整列表)
|
|
462
445
|
|
|
463
446
|
| 分类 | 变量 | 默认 | 说明 |
|
|
464
447
|
|------|------|------|------|
|
|
@@ -470,19 +453,7 @@ localhost:5432 - accepting connections
|
|
|
470
453
|
| **限流** | `SSH_RATE_LIMIT` | `true` | 限流总开关(bool) |
|
|
471
454
|
| | `SSH_RATE_LIMIT_MAX` | `30` | 限流次数上限 |
|
|
472
455
|
| | `SSH_RATE_LIMIT_WINDOW` | `60` | 限流窗口(秒) |
|
|
473
|
-
|
|
|
474
|
-
| | `SSH_RUNTIME_ALLOW_ROOT` | `false` | 是否允许 root 启动 |
|
|
475
|
-
| | `SSH_RUNTIME_ALLOWED_USERS` | (空) | 允许启动的账号白名单 |
|
|
476
|
-
| | `SSH_RUNTIME_BLOCK_SUDO` | `true` | 是否拦截 sudo 上下文 |
|
|
477
|
-
| **加固点 2** | `SSH_SECRET_PROVIDER_ENABLED` | `false` | secret provider 总开关 |
|
|
478
|
-
| | `SSH_SECRET_PROVIDER` | `env` | 凭证 provider:`env` / `command` / `http` |
|
|
479
|
-
| | `SSH_SECRET_ENV_KEY_<NAME>` | — | env provider:私钥环境变量 |
|
|
480
|
-
| | `SSH_SECRET_COMMAND_<NAME>` | — | command provider:拉取私钥的命令 |
|
|
481
|
-
| | `SSH_SECRET_HTTP_URL_<NAME>` | — | http provider:拉取私钥的 URL |
|
|
482
|
-
| | `SSH_SECRET_HTTP_TOKEN` | — | http provider:Bearer token |
|
|
483
|
-
| | `SSH_SECRET_HTTP_TIMEOUT` | `15` | http provider:HTTP 超时(秒) |
|
|
484
|
-
| **加固点 3** | `SSH_REMOTE_GUARD` | `false` | 远端元字符拦截总开关 |
|
|
485
|
-
| **加固点 4** | *(无 env)* | — | 灾难性命令硬拦截,零配置零绕过 |
|
|
456
|
+
| **硬拦截** | *(无 env)* | — | 灾难性命令硬拦截,零配置零绕过 |
|
|
486
457
|
| **连接默认** | `SSH_HOST` / `SSH_PORT` / `SSH_USER` / `SSH_PASSWORD` | `127.0.0.1` / `22` / `root` / (空) | 单 host 模式默认连接参数 |
|
|
487
458
|
| | `SSH_TIMEOUT` | `60` | 连接超时(秒) |
|
|
488
459
|
| | `SSH_KEEPALIVE_INTERVAL` | `30` | keepalive 间隔(秒) |
|
|
@@ -651,7 +622,7 @@ ssh-licco 的核心依赖是:
|
|
|
651
622
|
|
|
652
623
|
| 指标 | 状态 |
|
|
653
624
|
|------|------|
|
|
654
|
-
| **测试用例** |
|
|
625
|
+
| **测试用例** | 418 passed, 0 skipped |
|
|
655
626
|
| **覆盖率** | 16 个源模块全覆盖 |
|
|
656
627
|
| **测试框架** | pytest + pytest-asyncio |
|
|
657
628
|
|
|
@@ -31,7 +31,7 @@ class ConnectionConfig(BaseModel):
|
|
|
31
31
|
private_key_path: Path | None = Field(default=None, description="Path to private key file")
|
|
32
32
|
private_key_material: str | None = Field(
|
|
33
33
|
default=None,
|
|
34
|
-
description="
|
|
34
|
+
description="In-memory private key PEM (used instead of private_key_path when key is provided as a string)",
|
|
35
35
|
)
|
|
36
36
|
passphrase: str | None = Field(default=None, description="Passphrase for private key")
|
|
37
37
|
timeout: int = Field(default=30, description="Connection timeout in seconds")
|
|
@@ -9,7 +9,6 @@ from mcp.types import TextContent
|
|
|
9
9
|
|
|
10
10
|
from ..config_manager import SSHConfig, SSHHost
|
|
11
11
|
from ..connection_config import ConnectionConfig
|
|
12
|
-
from ..secret_provider import SecretManager, SecretProviderError
|
|
13
12
|
from ..security import SecurityError, command_validator
|
|
14
13
|
from .context import HandlerContext
|
|
15
14
|
|
|
@@ -94,35 +93,7 @@ async def handle_connect(ctx: HandlerContext, args: dict) -> list[TextContent]:
|
|
|
94
93
|
or None
|
|
95
94
|
)
|
|
96
95
|
|
|
97
|
-
# 加固点 2:密钥不落地磁盘
|
|
98
|
-
# 当 SecretManager 启用时,自动从 KMS 临时拉取私钥到内存,不读磁盘路径。
|
|
99
|
-
private_key_material: str | None = None
|
|
100
96
|
private_key_path = Path(args["private_key_path"]) if args.get("private_key_path") else None
|
|
101
|
-
sm = SecretManager.instance()
|
|
102
|
-
secret_material = None
|
|
103
|
-
if sm.enabled and not private_key_path and not host_config.password:
|
|
104
|
-
# 仅在「无磁盘私钥 + 无密码」时尝试拉取(避免覆盖显式凭证)
|
|
105
|
-
secret_name = args.get("name") or getattr(host_config, "name", None) or host_config.host
|
|
106
|
-
try:
|
|
107
|
-
secret_material = sm.fetch(secret_name)
|
|
108
|
-
private_key_material = secret_material.as_str()
|
|
109
|
-
ctx.logger.info(
|
|
110
|
-
"[secret] 私钥从 KMS 临时拉取到内存"
|
|
111
|
-
f"(source={secret_material.source}, name={secret_name}),不落盘"
|
|
112
|
-
)
|
|
113
|
-
except SecretProviderError as e:
|
|
114
|
-
ctx.logger.error(f"[secret] 拉取私钥失败: {e}")
|
|
115
|
-
return [TextContent(type="text", text=f"密钥不落地模式:拉取私钥失败:{e}")]
|
|
116
|
-
elif sm.enabled and private_key_path:
|
|
117
|
-
return [
|
|
118
|
-
TextContent(
|
|
119
|
-
type="text",
|
|
120
|
-
text=(
|
|
121
|
-
"密钥不落地模式已启用(SSH_SECRET_PROVIDER_ENABLED=true),"
|
|
122
|
-
"禁止使用 private_key_path 指定磁盘私钥文件。请通过 SecretManager 配置凭证。"
|
|
123
|
-
),
|
|
124
|
-
)
|
|
125
|
-
]
|
|
126
97
|
|
|
127
98
|
try:
|
|
128
99
|
config = ConnectionConfig(
|
|
@@ -139,7 +110,7 @@ async def handle_connect(ctx: HandlerContext, args: dict) -> list[TextContent]:
|
|
|
139
110
|
known_hosts_path=args.get("known_hosts_path"),
|
|
140
111
|
accept_new_host_key=args.get("accept_new_host_key", True),
|
|
141
112
|
private_key_path=private_key_path,
|
|
142
|
-
private_key_material=
|
|
113
|
+
private_key_material=None,
|
|
143
114
|
passphrase=args.get("passphrase"),
|
|
144
115
|
sudo_password=sudo_password,
|
|
145
116
|
)
|
|
@@ -221,10 +192,7 @@ async def handle_connect(ctx: HandlerContext, args: dict) -> list[TextContent]:
|
|
|
221
192
|
)
|
|
222
193
|
]
|
|
223
194
|
finally:
|
|
224
|
-
|
|
225
|
-
# (paramiko 已在内部把私钥加载为 PKey 对象,原始 PEM 字符串不再需要)
|
|
226
|
-
if secret_material is not None:
|
|
227
|
-
sm.release(secret_material)
|
|
195
|
+
pass
|
|
228
196
|
|
|
229
197
|
|
|
230
198
|
async def handle_disconnect(ctx: HandlerContext, args: dict) -> list[TextContent]:
|