Docker 交付的 MCP Server + Web 管理端。Agent 通过 2 个 MCP 工具在局域网 Linux 主机上远程执行命令;管理员通过 Web 控制台管理主机、实时查看执行流、为敏感命令输入密码。
设计基调:面向个人局域网服务器运维,克制复杂度、不做迁移、安全适当放宽。
| 编号 | v0.5 全量版 | v2.0 极简版 | 理由 |
|---|---|---|---|
| R-01 | 高风险命令拦截 + confirm_command 一次性令牌 | 移除,命令全部直接执行 | 个人局域网无需双层审批 |
| R-02 | linux.host_info 结构化系统采集(4 组解析器) | 移除,用 df -h / free -m 等普通命令替代 | 类 Unix 命令不兼容风险高 |
| R-03 | query_command / query_audits 审计查询工具 | 移除,执行记录由 Web 执行流承载 | Agent 无二次查询需求 |
| R-04 | linux.list_hosts 主机列表工具 | 移除,主机清单并入 help_info 动态生成 | 减少工具数量 |
| R-05 | SSH host_key 指纹策略 + 缓存管理接口 | 移除,统一 AutoAddPolicy | 安全放宽 |
| R-06 | 凭据 Fernet 加密 + 密钥轮换(previous key) | 保留加密,移除轮换(单密钥) | 轮换无场景 |
| R-07 | 审计分页查询页 + 输出分段下载 | Web 执行流:SSE 实时推最近 30 条 | 监控无需分页 |
| R-08 | Web 登录密码与 MCP API Key 分离 | 统一为一个 API_KEY(Web 用户名 admin) | 单管理员无需双凭据 |
| R-09 | sudo 三模式标记(预置密码/跳过/弹窗) | 统一 Web 弹窗实时输入;root 用户免弹窗直执行 | 体验参照 linux-handler-python-mcp |
| 编号 | 功能 | 实现要点 |
|---|---|---|
| F-01 | 主机管理 | Web 端增删改查、测试连接、启停;密码/私钥(含口令)两种认证;删除有历史记录时需二次确认 |
| F-02 | linux.execute_command | hostId / command(≤8000) / operator / timeoutMs(1000~120000,越界钳制);同步返回 stdout/stderr/exitCode/状态/耗时 |
| F-03 | linux.help_info | 无入参,返回 Markdown 六块:概述 / 参数表 / 动态主机清单 / 状态码 / 敏感输入机制 / 示例 |
| F-04 | 实时命令页(/console) | SSE 推送最近 30 条(首帧 records 快照 + 增量 audit 事件,Last-Event-ID 重连),长输出 details 折叠 |
| F-05 | 主机管理页(/console/hosts) | 与执行流拆分的独立页面;两页顶部导航切换(2026-09-05 变更,见决策 D-4) |
| F-06 | 敏感输入 | 正则识别 sudo(自动改写 sudo -S)/ 无值 mysql -p / docker login,且主机用户非 root → 挂起为 WAITING_SECRET_INPUT 并生成挑战;MCP 立即返回 WAITING(含 challengeId 与有效期),不阻塞连接;Web 弹窗提交 /api/challenges/{id} 后经 SSH stdin 注入继续执行;密码错误可重试(默认最多 3 次),最终结果经 Web 控制台查看(2026-09-06 真机验证 as-built) |
| F-07 | 登录/登出 | 单管理员(admin / API_KEY);会话 Cookie + CSRF 头;登出后接口 401 |
| F-08 | 统一鉴权 | API_KEY 既是 Web 登录密码,又是 MCP X-API-Key / Bearer |
| F-09 | 存储 | SQLite 单文件 WAL,建表即用不做迁移(schema 冗余列保留不写);执行记录 FIFO 保留 500 条 |
| F-10 | 高风险命令人工审批(v2.1 新增,D-6) | 内置 7 条高置信度规则(递归删除根/家/当前目录、格式化、dd 写块设备、关机重启、重定向覆盖块设备、fork 炸弹、drop/truncate 数据库对象、下载脚本直接执行);命中 → 挂起 WAITING_APPROVAL 并生成审批单(approval_challenges 表,TTL 默认 300s),MCP 立即返回 approvalId;Web 详情面板内嵌「批准/拒绝」,批准后按需转入密码挑战或直接执行,拒绝终态 REJECTED;审批单一次性消费;Agent 等待期间勿重试 |
| F-11 | 配置备份/还原(主机管理页,方案 A) | 主机管理页 /console/hosts 提供「备份配置」「还原配置」按钮:GET /api/hosts/backup 导出 hosts 表全列 JSON(含凭据密文原样、密钥单向指纹);POST /api/hosts/restore(UploadFile)按主机 ID 合并 upsert,保留其他主机与历史;还原时校验密钥指纹,不匹配弹告警但继续;不做还原前自动快照 |
PENDING ──→ RUNNING ──→ SUCCESS | FAILED | TIMEOUT
│
└─→ WAITING_SECRET_INPUT ──(Web 提交)──→ RUNNING
│
└─(120s 未提交)──→ FAILED(SECRET_INPUT_EXPIRED)
PENDING ──→ WAITING_APPROVAL ──(Web 批准)──→ RUNNING | WAITING_SECRET_INPUT
│ └─(Web 拒绝)──→ REJECTED
└─(300s 未审批)──→ APPROVAL_EXPIRED
MCP 工具(2 个):linux.execute_command、linux.help_info(Streamable HTTP /mcp,X-API-Key 或 Bearer 鉴权)。
Web 页面:/login、/console(实时命令)、/console/hosts(主机管理);未登录访问控制台 303 → /login。
Web API:/api/hosts(GET/POST)、/api/hosts/{id}(PATCH/DELETE)、/api/hosts/{id}/test、/api/hosts/{id}/toggle、/api/hosts/backup(GET,下载 JSON)、/api/hosts/restore(POST,UploadFile)、/api/commands/recent、/api/console/stream(SSE)、/api/challenges/{id}(POST)、/api/approvals/{id}(POST,action=approve/reject)、/login、/logout、/health。
CHALLENGE_EXPIRE_SECONDS),过期惰性翻转;密码错误可重试,最多 3 次APPROVAL_EXPIRE_SECONDS),审批单一次性消费,拒绝/过期均为终态pyproject.toml package-data 必须含 web/*.html,改页面后须跑一次 wheel 构建核验| 编号 | 验收点 |
|---|---|
| AC-01 | /health 200 且 database ok |
| AC-02 | tools/list 恰好 2 个工具;AC-02b help_info 含主机清单 |
| AC-03/04 | 登录成功返回 CSRF;错误密码 401;登出后接口 401 |
| AC-05 | 未登录访问 /console 与 /console/hosts 均 303 → /login |
| AC-06/07 | 主机新增/重复 409/列表无凭据/PATCH 改名不清凭证 |
| AC-08 | 普通命令 SUCCESS,记录含 stdout/stderr/exitCode |
| AC-09 | sudo(非 root)进入 WAITING_SECRET_INPUT;AC-09b Web 提交后流转完成 |
| AC-10 | 密码不出现在任何执行记录字段 |
| AC-11 | TIMEOUT / 非法 hostId / 主机停用拒绝 |
| AC-12 | 明文凭据命令拒绝(不进入执行流程) |
| AC-13 | 记录列表含命令与输出字段;AC-13b SSE 首帧 records 快照 |
| AC-14 | 空/纯空白命令在 schema 层与 service 层分别拒绝 |
| AC-15 | 登出成功 |
| AC-16 | 页面拆分:/console 不含主机表格,/console/hosts 不含执行流表格,两页均有顶部导航 |
| AC-17 | 高风险命令进入 WAITING_APPROVAL(含命中规则标注);非法 action 400/422;拒绝 → REJECTED 终态;批准 → 进入执行;审批单一次性消费;sudo+高风险先审批后转密码挑战(先审批后密码,D-6) |
linux.help_info 工具,承载参数说明与动态主机清单。/console 保留为实时命令页。_sudo_stdin_command 复合命令缺陷——原实现只改写行首第一个 sudo,sudo -k; sudo whoami 中第二个裸 sudo 改写后无法获得 stdin 密码通道。现改为行首及 ;/&/|/换行分隔后的每个 sudo 段均改写 sudo -S(已含 -S 的段跳过);risk.py 的 SUDO_PATTERN 同步支持换行分隔以保持识别/改写一致。已知边界:单条命令内多个都需要密码的 sudo 段,第二个起仍会因 stdin 密码已被消费而失败(stdin 单次注入机制限制),建议拆分执行。approval_challenges 表(应用户确认,项目无历史数据包袱);审批先于密码挑战(复合命令先审批、批准后再输密码);审批价值定位为防 Agent 误判/幻觉命令,静态规则库按"宁少拦不误拦"原则持续迭代。| 编号 | 原待确认问题 | 落地 |
|---|---|---|
| Q-01 | MCP 对敏感命令是否同步等待 | 否(as-built 修正):立即返回 WAITING_SECRET_INPUT + challengeId,不阻塞 MCP 连接;挑战 120s 有效、密码错误最多重试 3 次,结果经 Web 控制台查看 |
| Q-02 | 裸 sudo 是否自动改写 -S | 是(首处 sudo 自动补 -S) |
| Q-03 | 历史保留条数 | 500 条 FIFO |
| Q-04 | 输出单值上限 | 65,536 字符 |
| 日期 | 版本 | 说明 |
|---|---|---|
| 2026-09-05 | v2.0(原稿,已丢失) | 3 轮需求收敛后成稿 |
| 2026-09-05 | v2.1(重建版) | 依据决策记录与 as-built 代码重建;补充 D-4 双页面拆分与 AC-16;SSE/敏感输入/help_info 等条目与实现逐项核对 |
| 2026-09-05 | v2.1 | D-5:修复复合命令中后续裸 sudo 未改写 -S 的缺陷;识别正则同步支持换行 |
docker run -d \
--name "${CONTAINER_NAME}" \
--restart unless-stopped \
--user "$(id -u):$(id -g)" \
-p "${HOST_PORT}:${CONTAINER_PORT}" \
-v "${DATA_VOLUME_HOST}:/app/data" \
-e HOST="${HOST}" \
-e PORT="${PORT}" \
-e LOG_LEVEL="INFO" \
-e LOG_JSON="true" \
-e API_KEY="${API_KEY}" \
-e CREDENTIAL_ENCRYPTION_KEY="oK7EKqEJkcOQ-uC71Yz2ioLw65LP_JV-l0v_SPhPydA=" \
-e DATABASE_PATH="./data/linux_agent.db" \
-e HOST_CONCURRENCY_LIMIT="3" \
-e SESSION_IDLE_TIMEOUT_SECONDS="1800" \
-e SESSION_COOKIE_SECURE="false" \
-e CHALLENGE_EXPIRE_SECONDS="180" \
-e APPROVAL_EXPIRE_SECONDS="300" \
-e MCP_ALLOWED_HOSTS="192.168.100.120:*" \
-e DEBUG="false" \
"linux_handler_mcp"
call me: [email protected]
Content type
Image
Digest
sha256:1480390cb…
Size
62.7 MB
Last updated
22 days ago
docker pull muziyuyang/linux_handler_mcp:v2.2.0