用途
unifi-safe-changes 是 UniFi MCP 服务器中的一项安全变更技能,目标是在通过 MCP 服务器创建、更新或删除 UniFi 状态时,不丢失网络连通性、不丢失审计记录、不损害操作者对系统的信任。服务器暴露的每一次写操作都受到约束,本技能解释每种约束的作用、哪些由代码强制执行、哪些只是约定,以及如何做出可撤销的变更。
四道约束
四道约束相互独立,一次变更必须全部通过才会发生:
- 角色:调用者的令牌角色必须授予该工具所需的层级。在 src/access.py 中检查,由工具包装层执行。
- confirm=True:调用者必须显式传入。对所有带 confirm 参数的工具,由工具包装层强制执行。
- dry_run=True:调用者可请求预览。对所有带 dry_run 参数的工具,由工具包装层强制执行。
- 服务器模式:UNIFI_READ_ONLY 和 UNIFI_DRY_RUN 会缩小可运行范围,在工具注册阶段由工具注册表控制。
前三项是运行时控制,由代码强制执行;缺少任何一道约束都视为缺陷。
confirm 与 dry_run
confirm 是强制性的,并非建议性。每个变更类工具都带 confirm 参数,包装层在缺少该参数时拒绝调用;拒绝会作为错误抛出并写入审计日志,标记为 denied。示例:不传 confirm 调用 create_firewall_zone 会被拒绝且不发生任何变化;传入 confirm=True 才会被接受。不带 confirm 参数的工具属于读取工具,由服务器注册表(src/utils/tool_kind.py 中的 @read_tool / @write_tool)区分,角色层级则据此检查。
dry_run=True 会返回变更将产生的结果而不实际执行,是任何首次变更的正确第一步。但 dry run 只能证明请求结构和服务器校验通过,不能证明变更本身安全,也不能证明控制器会接受该变更。
服务器模式
- UNIFI_READ_ONLY=true:完全不注册任何变更类工具,即使管理员也无法调用。
- UNIFI_DRY_RUN=true:所有写入都被强制转为预览,调用者无法关闭 dry_run。
两者都是部署决策而非单次调用决策。对共享服务器而言,只读部署是安全的默认选择。
一次变更的流程
- 读取当前状态:为即将变更的对象使用读取工具;建立在猜测上的变更无法干净地撤销。
- 预览:以 confirm=True、dry_run=True 调用,阅读预览并与预期核对。
- 应用:以 confirm=True 且不带 dry_run 调用。
- 验证:重新读取该对象;网络变更需检查客户端是否仍连接、预期行为是否已改变。
- 记录:审计日志已保存尝试与结果。
可能破坏网络的变更
以下变更一旦出错可能导致设备失联,无论看起来多小都应按高风险处理:
- 在自身连接所在的端口上禁用 LAN:会丢失会话。应通过控制台 UI 或另一条网络路径操作。
- 编辑 WAN 配置:站点会失去互联网。应先预览并核对预览中的地址。
- 从区域中移除区域或网络:该区域内的设备会失去策略。应先读取区域矩阵并确认区域内设备。
- 修改 VLAN 子网:该 VLAN 上所有设备会失去地址。应先读取 DHCP 预留和静态租约。
- 重命名或删除 SSID:该 SSID 上的客户端会断开。应先读取该 SSID 的客户端列表。
- 覆盖防火墙策略:本不应放行的流量会开始流动。应先读取当前策略;该更新是整对象 PUT。
- 重启网关或交换机:其后方所有设备都会掉线。应先检查后方有哪些设备。
其中整对象 PUT 是主要陷阱:多个工具会替换整个对象而非合并字段,部分参数可能抹掉未提及的设置。应读取对象、修改字段、再把完整对象发回。
回滚
服务器没有撤销功能。回滚意味着执行反向变更,因此需要先前值:变更前读取当前值,这次读取就是撤销依据;优先选择可逆的变更,新增规则可逆,删除规则除非已保存否则不可逆;备份不等于撤销,list_backups 显示控制器保存的内容,恢复备份本身是大型且具破坏性的操作;策略编辑的反向操作就是原来的策略,应保留读取输出。
审计记录
每次变更调用、每次拒绝,以及在 UNIFI_AUDIT_READS=true 时的每次读取都会被记录,记录包含主体、工具、层级、控制器、脱敏参数和结果;工具响应从不会被记录。可通过 A2A 审计路由查看委派调用,该路由需要管理员角色,查看者或操作者令牌会被以 403 拒绝:
curl -s -H "Authorization: Bearer $MCP_AUTH_TOKEN" \
http://127.0.0.1:3000/a2a/audit
失败模式
- 提示 "requires confirm=True":约束生效,应先读取当前状态再有意识地添加 confirm=True。
- 委派调用返回 "requires confirmation":A2A 安全层拦截了破坏性调用,应通过 /a2a/confirm 批准或放弃。
- 写入工具不存在:UNIFI_READ_ONLY=true,工具未注册,无可调用。
- 写入返回预览:UNIFI_DRY_RUN=true,阅读预览即可,未发生变更。
- Permission denied:角色未授予所需层级,破坏性操作需 admin,写入需 operator。
- 变更已应用但无效果:站点或控制器不对,应在正确范围重新读取对象。
- 客户端断开:某些变更是预期结果,继续操作前先用 list_active_clients 验证。
规则
- 绝不做无法描述其反向操作的变更。
- 绝不基于未读取的值做变更。
- 对从未做过的变更始终先预览。
- 绝不条件反射式地传 confirm=True,它是最后一步而非第一步。
- 在重新读取对象之前,绝不报告变更已完成。
限制
资料未提供该技能的安装命令、版本信息、所属平台细节或收费情况。