Files
Nexus/docs/design/specs/2026-06-01-files-root-ownership-ui-design.md
T

15 KiB
Raw Blame History

文件管理:自动提权、归属显示、权限修改 — 设计文档

内容
日期 2026-06-01
状态 已实现 Phase 15 + 二期递归 chmod/不可读提示(2026-06-01
范围 #/files 远程文件管理(浏览、写入、压缩、chmod 等)
关联 sync_v2.py · asyncssh_pool.py · remote_archive.py · FilesPage.vue

1. 背景与目标

1.1 背景

当前文件管理已具备浏览、编辑、上传、压缩、右键 chmod 等能力,但生产环境普遍存在:

  • SSH 登录用户为 deploy/www 等非 root 账号;
  • 站点目录与文件属主为 rootwww,普通用户 读得到、写不进
  • 列表展示 drwxr-xr-x 等原始 ls 权限串,运维难以一眼看出「这是谁家的文件」;
  • chmod 仅支持八进制输入,无预设、无 chown,且无 sudo 回退,与保存/压缩已做的提权能力不一致。

用户诉求(原文):

  1. 自动获取 root 权限(文件操作应能自动提权,而不是每次失败再手工改服务器);
  2. 列表显示权限,且 简化为用户维度(直接显示 www / root 等,而非冗长权限串);
  3. 右键修改权限,弹出 结构化对话框(而非仅一个 chmod 输入框)。

1.2 目标

目标 可验收标准
G1 自动提权 写、删、压缩、chmod/chown 等在配置允许时自动 sudo -n 重试,成功率与 root 登录接近
G2 归属可读 列表默认列显示 属主(如 www),悬停可看完整 权限 + 用户:组
G3 权限可改 右键「修改权限」打开对话框:预设 + 自定义八进制 + 可选改属主/属组
G4 安全可审计 提权范围可配置、命令白名单、所有 chmod/chown 记审计日志
G5 不破坏架构 API → Service/SSH 基础设施层,不在路由里散落 sudo 逻辑

1.3 非目标(本期不做)

  • 不要求 Nexus 在目标机上 永久改写 sudoers(类似 Agent 安装的临时注入)作为默认路径——仅文档 + 可选一键脚本;
  • 不实现交互式 sudo 密码输入(违反无人值守运维);
  • 不把文件管理改成「必须 root SSH 才能用」。

2. 现状摘要

能力 现状 缺口
浏览 ls -la,解析 perms未返回 owner/group 前端无法显示 www/root
保存/上传 SFTP → SSH tee/mvsudo -n 部分已做 未统一、chmod 未接入
压缩 remote_archive.py 已 sudo 回退
chmod chmod mode path,无 sudo 非 root 常失败
提权模型 Agent 安装用 _sudo_wrap 临时 sudoers 文件管理未复用同一策略

后端 browse 已解析 parts[2]owner)、parts[3](group),但未写入 API 响应(见 sync_v2.py 约 270302 行)。


3. 方案对比

3.1 自动提权

方案 说明 优点 缺点
A. 仅 SSH 用户 不 sudo,失败即报错 最简单 无法满足用户「自动 root」
B. 统一 sudo -n 回退 先普通执行,Permission denied 再 sudo -n bash -c 与现网 partial 实现一致;可审计 需目标机配置 NOPASSWD 白名单
C. 每次临时写 sudoers 复用 servers._sudo_wrap 无预先配置 安全风险高、慢、需 root 写 sudoers
D. 文件管理强制 root SSH 凭据层只用 root 权限足 违背最小权限;很多站点不给 root

选定:B(主) + 安装向导/文档配置 NOPASSWD(辅)

  • 业务命令统一走 exec_ssh_command_with_fallback()(从 remote_archive 提升到 remote_shell.py)。
  • 非 root SSH 用户:失败且 stderr 含 permission denied → 自动 sudo -n bash -c '...'
  • root SSH 用户:不包 sudo。
  • 提供 服务器级开关(见 §5.2),默认 auto_sudo

不推荐 C 作为默认:与 Agent 安装场景不同,文件管理高频、面过大。

3.2 权限展示

方案 列表主列 说明
S1 drwxr-xr-x 现状
S2 www 仅属主
S3 www · 755 属主 + 八进制简写(从 perms 解析)
S4 Chip www + tooltip 全文 推荐:主信息属主,详情悬停

选定:S4

  • 列名:归属owner 字段,展示 ownerowner:group 若不同)。
  • 辅助解析 mode_octal755 / 644 供排序或二级展示(可选列,默认隐藏或放在 tooltip)。
  • symlink 仍显示 → target,归属列显示 - 或链接目标属主。

3.3 修改权限对话框

方案 内容
P1 仅八进制 chmod(现状)
P2 预设按钮 + 八进制 + chown推荐
P3 完整 Unix 权限矩阵(rwx 勾选)

选定:P2 — 满足运维习惯,实现成本低于 P3。


4. 选定方案详设

4.1 统一远程 Shell 执行层

新增模块(建议路径):

server/infrastructure/ssh/remote_shell.py

职责:

# 概念接口(实现时写完整类型)

class FilesElevation(str, Enum):
    OFF = "off"           # 从不 sudo
    AUTO = "auto_sudo"    # 默认:失败且权限错误时 sudo -n
    ALWAYS = "always_sudo"  # 所有命令直接 sudo -n(可选,仅排障)

async def exec_remote_command(
    server: Server,
    command: str,
    *,
    timeout: int = 30,
    elevation: FilesElevation | None = None,  # None → 读服务器/全局配置
) -> CommandResult: ...

def wrap_sudo(command: str) -> str:
    return f"sudo -n bash -c {shlex.quote(command)}"

接入范围(须全部走统一层)

操作 当前入口 改造
browse exec_ssh_command(ls) 保持直接执行(只读,一般不需 sudo)
write-file / upload remote_write_bytes 已有多级回退,并入 elevation 策略
delete / mkdir / rename file-ops 加 fallback
compress / decompress remote_archive 已有 fallback,迁到 remote_shell
chmod / chown chmod 端点 扩展 payload + fallback
read-file cat 一般只读;可选对不可读文件 sudo cat(二期)

提权判定

exit_code != 0
且 (
  "permission denied" in stderr.lower()
  或 "operation not permitted" in stderr.lower()
)
→ 若 elevation == AUTO,重试 wrap_sudo(command)

4.2 目标机 sudo 配置(运维一次性)

docs/deploy/ 增加模板 nexus-files.sudoers(示例):

# /etc/sudoers.d/nexus-files — 由运维审阅后安装
deploy ALL=(ALL) NOPASSWD: /bin/chmod, /bin/chown, /usr/bin/chown,
    /bin/mv, /bin/cp, /bin/mkdir, /bin/rm, /bin/tar, /usr/bin/zip,
    /usr/bin/unzip, /usr/bin/tee, /usr/bin/cat

说明:

  • Nexus 不会默认自动创建该文件(避免未授权扩权);
  • 安装向导或「服务器详情 → 文件管理」页提供 检测 APIGET /api/servers/{id}/files-capability
    • 检测项:ssh_usercan_sudo_nopasswdwhitelist_ok
  • 检测失败时,前端 Toast 链到文档锚点。

若 SSH 用户已是 rootcan_sudo_nopasswd=true,无需 sudoers。

4.3 浏览 API 扩展

POST /api/sync/browse 响应项扩展

{
  "name": "index.php",
  "type": "file",
  "size": 1234,
  "modified": "Jun 1 12:00",
  "permissions": "rw-r--r--",
  "mode_octal": "644",
  "owner": "www",
  "group": "www",
  "link_target": null
}
字段 来源 说明
permissions ls 第 1 列 保留,供 tooltip / 高级用户
mode_octal 从 perms 解析 rwxr-xr-x755(含 setuid 等用 4 位,少见则原样)
owner ls 第 3 列 列表主展示
group ls 第 4 列 与 owner 相同时 UI 只显示 owner

解析工具:frontend/src/utils/fileMode.ts + 后端 server/utils/unix_ls.py(避免前后端不一致,以后端为准)。

browse 可选增强(二期)ls -la --time-style=long-iso 统一时间格式。

4.4 前端列表 UI

列调整(FilesPage.vue

默认 展示
名称 显示 不变
归属 显示(新) v-chipwww / rootwww:nogroup 简化为 www
大小 显示 不变
修改时间 显示 不变
权限 隐藏(可选展开) tooltipdrwxr-xr-x · 755

样式约定

  • owner === 'root' → chip 颜色 warning(提醒系统文件)
  • 当前 SSH 用户无写权限且非 root 属主 → 行尾可选 icon mdi-shield-lock(仅提示,不阻断只读)

4.5 右键「修改权限」对话框

入口:已有右键 chmod 菜单项,改为打开新组件 FilePermissionDialog.vue

对话框结构

┌─ 修改权限 ─────────────────────────────┐
│ 文件:index.php                         │
│ 当前:www · 644  (rw-r--r--)            │
├─────────────────────────────────────────┤
│ 常用预设(按 type 过滤)                 │
│  [ 644 文件 ] [ 755 目录 ] [ 600 私密 ]   │
│  [ 775 协作目录 ] [ 自定义… ]            │
├─────────────────────────────────────────┤
│ 八进制权限  [ 644        ]  (验证 34 位) │
│ 属主      [ www        ]  (可选)         │
│ 属组      [ www        ]  (可选)         │
│ □ 同时修改子项(仅目录,二期)             │
├─────────────────────────────────────────┤
│ 提权:将使用 sudo 执行(SSH 用户 deploy  │
│        [ 检测提权能力 ] 链接文档           │
├─────────────────────────────────────────┤
│           [ 取消 ]  [ 应用 ]             │
└─────────────────────────────────────────┘

预设表(前端常量,后端可校验)

标签 mode 适用
文件默认 644 file
目录默认 755 directory
仅本人 600 file
协作目录 775 directory
可执行 755 file(脚本)

APIPOST /api/sync/chmod 扩展

class FileChmod(BaseModel):
    server_id: int
    path: str = Field(..., min_length=1, max_length=4096)
    mode: str = Field(..., pattern=r"^[0-7]{3,4}$")
    owner: str | None = Field(None, max_length=64)   # 可选 chown
    group: str | None = Field(None, max_length=64)
    recursive: bool = False  # 二期:chmod -R

远程命令(经 exec_remote_command + AUTO elevation

# 单文件
chmod 644 '/path/file' && chown www:www '/path/file'

# 仅 chmod
chmod 644 '/path/file'

顺序:先 chmodchown(或反之,实现时固定一种并写测试)。

响应

{
  "success": true,
  "path": "/www/.../index.php",
  "mode": "644",
  "owner": "www",
  "group": "www",
  "elevated": true
}

前端成功后 browseCurrent() 刷新列表。

4.6 服务器级配置(三写之一扩展)

服务器编辑settings 增加(MySQL servers 表或 settings JSON):

字段 类型 默认 说明
files_elevation enum auto_sudo off / auto_sudo / always_sudo

不在安装向导强制,避免与 Agent sudoers 混淆;在服务器表单「高级 → 文件管理」折叠区配置。


5. 安全与性能

5.1 安全

  • 最小权限sudo -n 仅允许白名单命令,禁止 ALL=(ALL) NOPASSWD: ALL
  • 禁止在 API 中拼接未校验的 owner/group(仅 [a-zA-Z0-9_-.]+);
  • 路径继续 normalize_remote_abs_path,禁止 ..
  • 审计file_chmod / file_chown 合并为 file_permission_change,记录 mode、owner、group、elevated
  • 失败信息不泄露 sudoers 路径内容。

5.2 性能

  • browse 仍单次 ls -la,不增加 SSH 往返;
  • sudo 回退最多 2 倍 命令耗时(仅失败路径),可接受;
  • 批量 chmod(二期 -R)需限制路径深度或要求确认框。

6. 实现步骤(供技术文档拆分)

阶段 内容 预估
Phase 1 remote_shell.py + 迁移 write/compress/chmod/file-ops 1d
Phase 2 browse 返回 owner/group/mode_octal;前端归属列 0.5d
Phase 3 FilePermissionDialog + chmod API 扩展 chown 1d
Phase 4 files-capability 检测 + 文档 + 设置项 files_elevation 0.5d
Phase 5 测试 + changelog + 审计 0.5d

依赖顺序:Phase 1 → 2/3 可并行 → 4 → 5。


7. 验收标准

L1 提权

  • SSH 用户 deploy + 已配置 nexus-files.sudoers:编辑、保存、压缩、chmod 均成功,elevated: true 出现在审计(若执行了 sudo)。
  • SSH 用户 root:不调用 sudo,功能正常。
  • 无 sudo 且非 root:操作失败,提示含「配置免密 sudo」链接,不返回误导性「SSH 连接失败」。

L2 展示

  • 列表「归属」列显示 www / root,悬停可见完整权限串。
  • 不再默认展示 drwxr-xr-x 宽列(可列设置打开)。

L3 修改权限

  • 右键单文件/目录 → 对话框预设 644/755 一键填充。
  • 修改属主为 www 后,ls 刷新显示更新。
  • 非法 mode/owner 前端校验 + 后端 400。

L4 回归

  • 文件树、编辑器、剪贴板粘贴、下载不受回归影响。

8. 风险与对策

风险 对策
用户以为「自动 root」= Nexus 破解权限 UI 文案写清:在服务器已授权 sudo 前提下自动提权
sudo 白名单遗漏命令 capability 检测 + 失败时记录完整 stderr
ls 解析在不同 OS 上列错位 优先 GNU coreutils;异常行跳过并记日志
chown 误操作 对话框展示当前值;危险路径(/etc、/usr)二次确认(二期)

9. 相关文档(实现后补齐)

  • 技术实施:docs/design/plans/2026-06-01-files-root-ownership-ui.md(待写)
  • Changelogdocs/changelog/2026-06-01-files-root-ownership-ui.md(待写)
  • 运维:docs/deploy/nexus-files-sudoers.example

10. 决策记录

决策 选择 理由
自动 root 实现 sudo -n 回退,非临时写 sudoers 安全、可审计、与现有 partial 实现一致
列表权限 主显 属主,权限进 tooltip 符合用户「www/root」认知
chmod UI 预设 + 八进制 + 可选 chown 平衡能力与复杂度
browse 是否 sudo 否(默认) 列目录极少需要 root;减少噪音

请评审确认后进入 Phase 1 实现。 若需「始终 sudo」或「强制 root SSH」,可在服务器级 files_elevation 中开启 always_sudo