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.
Files changed (53) hide show
  1. {ssh_licco-2.3.1/ssh_licco.egg-info → ssh_licco-2.5.0}/PKG-INFO +4 -33
  2. {ssh_licco-2.3.1 → ssh_licco-2.5.0}/README.md +3 -32
  3. {ssh_licco-2.3.1 → ssh_licco-2.5.0}/pyproject.toml +1 -1
  4. {ssh_licco-2.3.1 → ssh_licco-2.5.0/ssh_licco.egg-info}/PKG-INFO +4 -33
  5. {ssh_licco-2.3.1 → ssh_licco-2.5.0}/ssh_licco.egg-info/SOURCES.txt +0 -2
  6. {ssh_licco-2.3.1 → ssh_licco-2.5.0}/ssh_mcp/__init__.py +2 -2
  7. {ssh_licco-2.3.1 → ssh_licco-2.5.0}/ssh_mcp/connection_config.py +1 -1
  8. {ssh_licco-2.3.1 → ssh_licco-2.5.0}/ssh_mcp/handlers/connect.py +2 -34
  9. {ssh_licco-2.3.1 → ssh_licco-2.5.0}/ssh_mcp/handlers/execute.py +592 -594
  10. {ssh_licco-2.3.1 → ssh_licco-2.5.0}/ssh_mcp/handlers/schemas.py +494 -495
  11. {ssh_licco-2.3.1 → ssh_licco-2.5.0}/ssh_mcp/handlers/utils.py +0 -47
  12. {ssh_licco-2.3.1 → ssh_licco-2.5.0}/ssh_mcp/key_manager.py +0 -12
  13. {ssh_licco-2.3.1 → ssh_licco-2.5.0}/ssh_mcp/security.py +112 -0
  14. {ssh_licco-2.3.1 → ssh_licco-2.5.0}/ssh_mcp/server.py +1720 -1917
  15. {ssh_licco-2.3.1 → ssh_licco-2.5.0}/tests/test_security.py +61 -0
  16. {ssh_licco-2.3.1 → ssh_licco-2.5.0}/tests/test_server.py +716 -599
  17. ssh_licco-2.3.1/ssh_mcp/runtime_guard.py +0 -178
  18. ssh_licco-2.3.1/ssh_mcp/secret_provider.py +0 -226
  19. {ssh_licco-2.3.1 → ssh_licco-2.5.0}/LICENSE +0 -0
  20. {ssh_licco-2.3.1 → ssh_licco-2.5.0}/setup.cfg +0 -0
  21. {ssh_licco-2.3.1 → ssh_licco-2.5.0}/ssh_licco.egg-info/dependency_links.txt +0 -0
  22. {ssh_licco-2.3.1 → ssh_licco-2.5.0}/ssh_licco.egg-info/entry_points.txt +0 -0
  23. {ssh_licco-2.3.1 → ssh_licco-2.5.0}/ssh_licco.egg-info/requires.txt +0 -0
  24. {ssh_licco-2.3.1 → ssh_licco-2.5.0}/ssh_licco.egg-info/top_level.txt +0 -0
  25. {ssh_licco-2.3.1 → ssh_licco-2.5.0}/ssh_mcp/__main__.py +0 -0
  26. {ssh_licco-2.3.1 → ssh_licco-2.5.0}/ssh_mcp/audit_logger.py +0 -0
  27. {ssh_licco-2.3.1 → ssh_licco-2.5.0}/ssh_mcp/batch_executor.py +0 -0
  28. {ssh_licco-2.3.1 → ssh_licco-2.5.0}/ssh_mcp/cli.py +0 -0
  29. {ssh_licco-2.3.1 → ssh_licco-2.5.0}/ssh_mcp/clients/__init__.py +0 -0
  30. {ssh_licco-2.3.1 → ssh_licco-2.5.0}/ssh_mcp/clients/additional_clients.py +0 -0
  31. {ssh_licco-2.3.1 → ssh_licco-2.5.0}/ssh_mcp/clients/factory.py +0 -0
  32. {ssh_licco-2.3.1 → ssh_licco-2.5.0}/ssh_mcp/clients/interface.py +0 -0
  33. {ssh_licco-2.3.1 → ssh_licco-2.5.0}/ssh_mcp/clients/paramiko_client.py +0 -0
  34. {ssh_licco-2.3.1 → ssh_licco-2.5.0}/ssh_mcp/config_manager.py +0 -0
  35. {ssh_licco-2.3.1 → ssh_licco-2.5.0}/ssh_mcp/connection_pool.py +0 -0
  36. {ssh_licco-2.3.1 → ssh_licco-2.5.0}/ssh_mcp/exceptions.py +0 -0
  37. {ssh_licco-2.3.1 → ssh_licco-2.5.0}/ssh_mcp/executor.py +0 -0
  38. {ssh_licco-2.3.1 → ssh_licco-2.5.0}/ssh_mcp/handlers/__init__.py +0 -0
  39. {ssh_licco-2.3.1 → ssh_licco-2.5.0}/ssh_mcp/handlers/context.py +0 -0
  40. {ssh_licco-2.3.1 → ssh_licco-2.5.0}/ssh_mcp/handlers/docker.py +0 -0
  41. {ssh_licco-2.3.1 → ssh_licco-2.5.0}/ssh_mcp/handlers/file_transfer.py +0 -0
  42. {ssh_licco-2.3.1 → ssh_licco-2.5.0}/ssh_mcp/handlers/host.py +0 -0
  43. {ssh_licco-2.3.1 → ssh_licco-2.5.0}/ssh_mcp/handlers/key.py +0 -0
  44. {ssh_licco-2.3.1 → ssh_licco-2.5.0}/ssh_mcp/handlers/process.py +0 -0
  45. {ssh_licco-2.3.1 → ssh_licco-2.5.0}/ssh_mcp/handlers/session.py +0 -0
  46. {ssh_licco-2.3.1 → ssh_licco-2.5.0}/ssh_mcp/logging_config.py +0 -0
  47. {ssh_licco-2.3.1 → ssh_licco-2.5.0}/ssh_mcp/service.py +0 -0
  48. {ssh_licco-2.3.1 → ssh_licco-2.5.0}/ssh_mcp/session_manager.py +0 -0
  49. {ssh_licco-2.3.1 → ssh_licco-2.5.0}/ssh_mcp/tunnel.py +0 -0
  50. {ssh_licco-2.3.1 → ssh_licco-2.5.0}/ssh_mcp/watchdog.py +0 -0
  51. {ssh_licco-2.3.1 → ssh_licco-2.5.0}/tests/test_config_manager.py +0 -0
  52. {ssh_licco-2.3.1 → ssh_licco-2.5.0}/tests/test_key_manager.py +0 -0
  53. {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.1
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.1.0 新增 `remote_guard`(双层拦截)参数;v2.2.0 起对灾难性命令(rm -rf 绝对路径、mkfs、raw-disk dd、fork-bomb 等)做**无条件硬拦截** |
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
- | **加固点 1** | `SSH_RUNTIME_GUARD` | `false` | runtime guard 总开关 |
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
- | **测试用例** | 400 passed, 0 skipped |
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.1.0 新增 `remote_guard`(双层拦截)参数;v2.2.0 起对灾难性命令(rm -rf 绝对路径、mkfs、raw-disk dd、fork-bomb 等)做**无条件硬拦截** |
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
- | **加固点 1** | `SSH_RUNTIME_GUARD` | `false` | runtime guard 总开关 |
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
- | **测试用例** | 400 passed, 0 skipped |
592
+ | **测试用例** | 418 passed, 0 skipped |
622
593
  | **覆盖率** | 16 个源模块全覆盖 |
623
594
  | **测试框架** | pytest + pytest-asyncio |
624
595
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "ssh-licco"
3
- version = "2.3.1"
3
+ version = "2.5.0"
4
4
  description = "SSH Model Context Protocol Server - Enable SSH functionality for AI models"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.10"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ssh-licco
3
- Version: 2.3.1
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.1.0 新增 `remote_guard`(双层拦截)参数;v2.2.0 起对灾难性命令(rm -rf 绝对路径、mkfs、raw-disk dd、fork-bomb 等)做**无条件硬拦截** |
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
- | **加固点 1** | `SSH_RUNTIME_GUARD` | `false` | runtime guard 总开关 |
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
- | **测试用例** | 400 passed, 0 skipped |
625
+ | **测试用例** | 418 passed, 0 skipped |
655
626
  | **覆盖率** | 16 个源模块全覆盖 |
656
627
  | **测试框架** | pytest + pytest-asyncio |
657
628
 
@@ -19,8 +19,6 @@ ssh_mcp/exceptions.py
19
19
  ssh_mcp/executor.py
20
20
  ssh_mcp/key_manager.py
21
21
  ssh_mcp/logging_config.py
22
- ssh_mcp/runtime_guard.py
23
- ssh_mcp/secret_provider.py
24
22
  ssh_mcp/security.py
25
23
  ssh_mcp/server.py
26
24
  ssh_mcp/service.py
@@ -1,8 +1,8 @@
1
1
  # SSH-LICCO 版本信息
2
2
  # 所有代码中的版本信息都从这里读取
3
- # 版本号:2.3.1
3
+ # 版本号:2.5.0
4
4
 
5
- __version__ = "2.3.1"
5
+ __version__ = "2.5.0"
6
6
  __author__ = "Li Qi"
7
7
  __email__ = "1985694657@qq.com"
8
8
  __license__ = "MIT"
@@ -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="内存中的私钥 PEM(密钥不落地模式:由 SecretProvider 临时拉取,不写入磁盘)",
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=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
- # 加固点 2:连接建立/失败后立即清零内存中的私钥
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]: