Skip to content

feat(connection): 连接级 AI 访问策略(issue #437) - #444

Merged
feigeCode merged 1 commit into
mainfrom
feat/connection-ai-access-policy
Oct 22, 2026
Merged

feigeCode merged 1 commit into
mainfrom
feat/connection-ai-access-policy

Conversation

@feigeCode

@feigeCode feigeCode commented Oct 22, 2026 •

Copy link
Copy Markdown
Owner

背景

Closes #437

用户用 Navop 管理多台服务器,一部分只允许人工操作。风险不是「AI 看不到连接」,而是
AI 在推理后自行换一条路做写操作:用户明确让 AI 用只读数据库工具排查问题,AI 若判断某个
已保存连接就是测试库,仍可能通过 connections.open_session / ssh.exec 登到那台服务器上
执行写入、删除命令。

因此需要在连接这个粒度上声明「AI 是否可操作」,并且声明必须在 AI 真正执行命令的那一跳生效。

方案

连接新增 ai_access_level:allowed(默认)/ read_only(只能调只读工具)/ denied(AI 完全不能碰)。

策略三层落地,单点拦截不够:

层 位置 作用
可见性 main/src/public_mcp_runtime/resource_pool.rs、crates/ai_chat_view/src/resource_builder.rs、crates/navop_runtime/src/connections/management.rs 被禁止的连接不进资源池 / 资源目录 / @ 提及,connections.list/find 也不返回;只读连接的能力按白名单裁剪
路由 ToolRuntimeMcpProvider 的 target 归一化 能力不满足时直接 TargetNotInPool / TargetLacksCapabilities,模型拿不到句柄
执行 crates/tool_runtime 的 ToolAccessGuard + main/src/public_mcp_runtime/access_guard.rs ToolRegistry::call 是外部 MCP 与内置 AI 的共同汇聚点;connection / connection_id / target / session_id 都解析成连接后按级别拒绝,读存储失败时 fail-closed

终端会话(用户自己手动打开的 SSH 会话)按 session.connection_id 回查策略:denied 的会话整条
移出资源池,read_only 的会话削掉 TerminalExec / TerminalInput。活动中的 Redis 通道同理。

策略随连接同步

「生产就是生产」——策略属于连接内容,必须跨设备一致:

  • 迁移 crates/core/migrations/20261022000001_connection_ai_access.sql 给 connections 加列,
    NULL = 默认「允许」,旧库升级不需要回填数据;
  • 云同步载荷 ConnectionPlainData 新增 ai_access_level(#[serde(default)]),上/下行都带上,
    并加入团队冲突判定的 connection_content_changed 白名单(否则只改策略不会重新上传,冲突解决后
    会被云端旧值覆盖);
  • 导入导出走 StoredConnection 的 serde,字段随文件走,旧文件缺该字段按「允许」解析。

界面

  • 通用组件 crates/connection_form/src/ai_access.rs:ai_access_selector 统一渲染三档选择器
    (选项顺序、文案、选中态、说明),表单只持有一个 Entity<AiAccessLevel> 并把它交给组件。
    数据库 / SSH / Redis / MongoDB / middleware 五个表单共用同一份实现,新增连接类型时一行接入,
    不再各复制一遍单选钮。ai_access_label / level_label / level_hint 收拢文案,stored_level /
    write_level 负责读、写连接字段;
  • 侧栏行内徽标(只读 / 禁止)+ 右键菜单「AI 访问」子菜单可直接切换;
  • 写失败会显式报错(沿用各表单既有错误行 + SaveError 事件),不静默放宽。

验证

  • cargo test -p one-core --lib:844 passed(含策略列往返、SELECT 列清单、下行同步保留本地 id、
    冲突白名单变异验证)
  • cargo test -p tool_runtime -p navop_runtime -p connection-form -p ai_chat_view -p db_view -p terminal_view -p redis_view -p mongodb_view:全绿
  • cargo test -p main --bin navop:718 passed(含「被禁止的连接立刻拦截、改回允许后同一 runtime
    立即恢复」端到端用例)
  • RUSTFLAGS="-D warnings" cargo check --workspace --all-targets:干净

已知限制

  • 老客户端不认识该字段,上行时会把它覆盖回默认值,多端混用建议升级到同一版本;
  • 策略随连接(含团队共享连接)走,单机想更严格需改用个人连接/副本;
  • PermissionMode::Ask 下会先弹审批再被拒绝(结果仍是拒绝,只多一次提示)。

设计与取舍记录:docs/design/connection-ai-access-policy.md

@feigeCode
feigeCode force-pushed the feat/connection-ai-access-policy branch 4 times, most recently from df74e1f to 9d33f4f Compare October 22, 2026 03:24
问题:AI 可能绕过只读工具,直接登录「只打算人工管理」的连接执行写命令。

方案:连接新增 `ai_access_level`(允许 / 只读 / 禁止),三层落地:
- 可见性:资源池与内置 AI 的资源目录、@ 提及按级别过滤或裁剪能力;
  `connections.list/find` 不返回被禁止的连接;
- 路由:目标解析在能力不匹配时直接失败,模型拿不到句柄;
- 执行:`ToolRegistry` 挂 `ToolAccessGuard`,外部 MCP 与内置 AI 共用同一闸门,
  覆盖 `connection` / `connection_id` / `target` / `session_id` 四类目标。

策略属于连接内容:随云同步与导入导出走(载荷新增 `ai_access_level`,并加入团队
冲突判定的内容变更白名单),保证换机器、换客户端后「生产就是生产」的限制仍生效。

界面:连接表单(数据库 / SSH / Redis / MongoDB / middleware)三选一,侧栏徽标与
右键菜单可直接切换;写失败显式报错,不静默放宽。

Refs #437
@feigeCode
feigeCode force-pushed the feat/connection-ai-access-policy branch from 9d33f4f to 0c7b757 Compare October 22, 2026 03:25
feigeCode added a commit that referenced this pull request Oct 22, 2026
合入 dev 做整体联调(启动验证后再决定是否合 main)
@feigeCode
feigeCode merged commit 6a62310 into main Oct 22, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: mcp或cil增加自定义权限对ai只暴露允许的连接

1 participant