File Browser 命令执行指南:Hook Runner 事件钩子与交互式 Shell 全解析
2026/9/19 4:56:59 网站建设 项目流程

File Browser 命令执行指南:Hook Runner 事件钩子与交互式 Shell 全解析

【免费下载链接】filebrowserFile Browser provides a file managing interface within a specified directory and it can be used to upload, delete, preview and edit your files.项目地址: https://gitcode.com/gh_mirrors/fi/filebrowser

本文围绕 File Browser 的「命令执行」能力展开,系统讲解两大核心功能:Hook Runner(在文件复制、重命名、上传、删除、保存等事件前后自动执行 shell 命令)与交互式 Shell(通过 WebSocket 在浏览器内执行白名单命令)。你将掌握钩子事件的触发机制、可用环境变量、filebrowser cmds命令族的完整用法、用户级命令授权方式,以及启用这些功能前必须了解的安全边界。文中所有结论均以当前仓库源码为准,可对照 runner/runner.go、http/commands.go 与 docs/command-execution.md 验证。

安全警告:自 v2.33.8 起默认禁用

在展开任何配置之前,必须先了解一个关键前提:由于持续存在的已知安全漏洞,从 v2.33.8 版本起,Hook Runner 与交互式 Shell 对所有现存及新建的安装实例默认处于禁用状态。官方文档(docs/command-execution.md)对此给出了[!CAUTION]级警告,建议仅在完全知晓并接受相关安全风险的前提下才启用这些功能。

这意味着本文介绍的所有能力都需要你主动、显式地开启才能使用,且开启后应当配合最小权限原则进行授权(详见下文各节)。

Hook Runner:在文件事件前后自动执行命令

Hook Runner 是 File Browser 内置的命令运行器,其设计目标是让你在某个文件事件发生之前或之后执行任意 shell 命令,例如:上传完成后自动压缩、删除文件前自动备份、保存后自动触发编译等。

支持的事件类型

目前钩子支持以下 5 类事件,每一类事件都有对应的before_after_两种触发时机:

事件名触发时机
copy复制文件/目录前后
rename重命名前后
upload上传(含目录创建、TUS 分块上传完成)前后
delete删除前后
save保存编辑内容前后

从源码可以清晰地看到这一机制:在 runner/runner.go 的RunHook方法中,命令执行被包裹在实际文件操作(通过回调函数fn完成)的两侧——先执行before_<event>系列命令,再执行实际的文件操作,最后执行after_<event>系列命令。若前置钩子执行失败,会直接返回错误,实际文件操作不会执行

以 HTTP 层的调用为例,删除操作在 http/resource.go 中触发:

err = d.RunHook(func() error { return d.user.Fs.RemoveAll(r.URL.Path) }, "delete", r.URL.Path, "", d.user)

上传(含 POST 创建目录)与编辑保存同样经由RunHook包裹(参见 http/resource.go、http/resource.go);使用 TUS 协议的大文件分块上传在全部块完成后也会触发upload钩子(参见 http/tus_handlers.go)。

钩子命令可用的环境变量

在执行钩子命令时,File Browser 会注入以下环境变量,方便你的脚本获知上下文:

变量名含义
FILE被变更文件的绝对路径
SCOPE用户作用域(scope)的路径
TRIGGER触发事件的名称(如before_copyafter_upload
USERNAME触发事件的用户名
DESTINATION目标的绝对路径,仅用于 copy 与 rename 事件

这些变量在 runner/runner.go 中被注入进程环境:

cmd.Env = append(os.Environ(), fmt.Sprintf("FILE=%s", path)) cmd.Env = append(cmd.Env, fmt.Sprintf("SCOPE=%s", user.Scope)) cmd.Env = append(cmd.Env, fmt.Sprintf("TRIGGER=%s", evt)) cmd.Env = append(cmd.Env, fmt.Sprintf("USERNAME=%s", user.Username)) cmd.Env = append(cmd.Env, fmt.Sprintf("DESTINATION=%s", dst))

同时,命令参数中形如$FILE$SCOPE的占位符也会在启动前被替换为对应值(通过os.Expand实现,映射逻辑见同文件的envMapping),即既可以通过$VAR内插,也可以直接在脚本中读取环境变量。此外,FILESCOPETRIGGERUSERNAMEDESTINATION之外的变量名会回落到系统环境变量,因此你仍可访问$PATH等系统环境。

阻塞与非阻塞执行

钩子命令默认是阻塞式执行的:命令结束后 File Browser 才会继续处理下一个操作。但从源码看,存在一个非阻塞开关——若命令字符串以&结尾,则会被识别为非阻塞命令(runner/runner.go):

if strings.HasSuffix(raw, "&") { blocking = false raw = strings.TrimSpace(strings.TrimSuffix(raw, "&")) }

非阻塞命令通过cmd.Start()启动后立即返回,命令的实际退出结果在后台 goroutine 中通过cmd.Wait()监听并记录日志。两种模式的运行日志均会以[INFO] Blocking Command: "..."[INFO] Nonblocking Command: "..."的格式输出。这意味着你可以在钩子中实现「发出即忘」的异步任务,例如上传后异步转码。

命令解析与 Shell 支持

File Browser 并不直接调用系统 shell 来解析钩子命令,而是先按 shell 风格把命令字符串拆分成「命令名 + 参数列表」(实现位于 runner/commands.go,Unix 使用 shlex 解析,Windows 使用内置的 Windows 风格解析器以正确处理反斜杠路径分隔符与双引号转义)。

随后在 runner/parser.go 的ParseCommand中决定执行方式:

  • 若全局设置中的Shell配置为空,则直接调用可执行文件本身command[0]即为命令名);
  • 若配置了Shell(例如["sh", "-c"]["cmd", "/c"]),则整条原始命令字符串作为参数传给 shell 执行。

这解释了为何官方文档建议将命令配置为echo $FILE这类 shell 语法——当启用了 Shell 配置时,变量替换、管道、重定向等 shell 特性才能生效。

管理钩子命令:CLI 命令族

钩子命令可以通过命令行接口(CLI)管理。在启用对应功能前,请先确认启动参数(详见下文),相关子命令挂载在filebrowser cmds下,其入口定义见 cmd/cmds.go。

查看全部钩子命令:cmds ls

filebrowser cmds ls

该命令会遍历所有事件的命令列表,并按照事件名(索引): 命令的格式输出,例如after_copy(0): echo $FILE。索引编号在删除命令时至关重要。

ls还支持通过-e/--event参数只查看某个特定事件的前后钩子:

filebrowser cmds ls --event upload

该命令会同时展示before_uploadafter_upload两组列表(实现见 cmd/cmds_ls.go)。

添加钩子命令:cmds add

filebrowser cmds add <event> <command>

例如官方文档中的示例:

filebrowser cmds add before_copy "echo $FILE"

参数<event>需要带上before_/after_前缀(如before_copyafter_upload),后续所有参数以空格拼接为一条完整命令(实现见 cmd/cmds_add.go)。同一事件可反复添加多条命令,它们将按添加顺序依次执行。

删除钩子命令:cmds rm

filebrowser cmds rm <event> <index> [index_end]

例如:

filebrowser cmds rm before_copy 0

index必须与cmds ls输出中的索引一致。注意每次增删操作后,命令列表的索引都会重新编号,因此连续删除多条命令时务必以最新的ls输出为准。cmds rm还支持可选的index_end参数,用于一次删除从indexindex_end(含端点)的连续命令段(实现见 cmd/cmds_rm.go)。

通过 Web 界面管理

除 CLI 外,钩子命令同样可以在 Web 界面中维护:Settings → Global Settings(全局设置)中提供了命令管理入口。两种方式操作的是同一份全局配置存储,最终都写入 settings 中的Commands字段(map[string][]string,键为before_<event>/after_<event>)。

交互式 Shell:浏览器内的命令终端

File Browser 的另一个命令执行入口是交互式 Shell:点击界面右上角的< >图标,屏幕底部会打开一个 Shell 命令窗口,你可以在其中输入并执行命令,实时查看标准输出与错误输出。

启用条件

交互式 Shell 默认关闭,需要显式开启。有两种等价方式:

  • 环境变量:FB_DISABLE_EXEC=false
  • 启动参数:--disable-exec=false(该 flag 在 cmd/root.go 中注册为disableExec

反向理解更直白:默认情况下disable-exec为真(禁用),把它设为false即表示「允许执行」。从 HTTP 层源码 http/commands.go 可以看到,WebSocket 处理逻辑的第一步就是快速失败检查:

// Fail fast if !d.server.EnableExec || !d.user.Perm.Execute { conn.WriteMessage(websocket.TextMessage, cmdNotAllowed) return 0, nil }

全局开关EnableExec与当前用户的Execute权限必须同时为真,否则直接返回Command not allowed.

命令白名单:按用户授权

即便全局启用了执行能力,也默认没有任何命令可用——因为每个用户的命令列表为空。你必须在以下两个位置为命令授权:

  • 按用户授权:Settings → User Management → (编辑指定用户)→ Commands。这也适用于 Admin 用户。
  • 为后续新建用户设置默认值:Settings → Global Settings(全局设置)中的命令列表会作为新用户的默认命令,从该时刻起创建的所有新用户都会继承(见 settings/defaults.go 中UserDefaults.Commands字段及Apply方法)。

这条白名单在 http/commands.go 中强制校验:命令经ParseCommand解析出命令名name后,必须命中d.user.Commands列表(slices.Contains),否则返回Command not allowed.。也就是说,即使你开启了 Shell,用户也只能执行管理员显式允许的有限命令集——例如只开放lscatdf,而不开放rmshutdown等高危命令。

底层通信:WebSocket 实时管道

交互式 Shell 基于 WebSocket 实现,而非普通的 HTTP 请求。其完整流程位于 http/commands.go:

  1. 客户端发起 WebSocket 升级(websocket.Upgrader),等待客户端发送第一条非空命令消息;
  2. 服务端完成「全局开关 + 用户 Execute 权限 + 命令白名单」三重校验;
  3. 启动exec.Command,并将进程的工作目录设置为用户当前浏览目录的绝对路径(cmd.Dir = d.user.FullPath(r.URL.Path));
  4. 通过StdoutPipe/StderrPipe捕获输出,用bufio.Scanner逐行读取并通过 WebSocket 实时推送给浏览器端;
  5. 命令退出后返回结果并关闭连接。

这也引出了文档中的一条重要运维注意事项:如果 File Browser 部署在反向代理(Nginx、Caddy、Traefik 等)之后,必须为对应站点启用 WebSocket 支持(Upgrade/Connection头透传),否则交互式 Shell 无法建立连接。

Docker 部署:为 Shell 添加自定义命令

在 Docker 场景下使用交互式 Shell 时,还有一个常见约束:只能执行基础镜像中已存在的命令。如果你需要运行镜像中没有的程序(例如压缩工具7z),不能直接在运行时安装,而是需要以filebrowser/filebrowser为基础镜像构建自定义镜像。官方文档给出了最小示例:

FROM filebrowser/filebrowser RUN sudo apt install p7zip-full

构建后使用该自定义镜像部署,即可在交互式 Shell 与钩子命令中使用7z等新安装的命令。需要说明的是,此示例假定基础镜像基于 Debian/Ubuntu 系并具备sudo与 apt 环境,实际基础镜像中包管理器与用户权限可能有所不同,请结合你所使用的镜像版本与 Dockerfile 进行调整(例如改为apt-get、或直接以 root 构建)。

最佳实践与安全建议

综合上文,启用命令执行功能时建议遵循以下原则:

  1. 默认关闭,按需开启:除非确有自动化需求,否则保持 v2.33.8+ 的默认禁用状态;开启时优先使用--disable-exec=falseFB_DISABLE_EXEC=false显式控制,并确认只有受信任的管理员能接触到该实例。
  2. 钩子命令收敛事件面:仅对必要事件(如after_upload)注册钩子,并利用$FILE$DESTINATION等环境变量把操作精确限定在目标文件上;命令失败会中断文件操作,因此before_钩子应保证幂等与快速失败。
  3. Shell 命令白名单最小化:交互式 Shell 的命令列表务必按最小权限授予,不向普通用户开放rmmvshutdown、包管理等危险命令;Admin 用户同样需要显式配置,并不会自动拥有全部命令。
  4. 代理需放行 WebSocket:任何反向代理配置都必须支持 WebSocket 升级,否则 Shell 功能不可用。
  5. Docker 自定义命令走镜像构建:需要新命令时以filebrowser/filebrowser为基础构建镜像,而不是在运行时临时安装。
  6. 留意执行日志:阻塞与非阻塞命令均会输出[INFO] ... Command: "..."日志(runner/runner.go),可用于审计钩子与 Shell 的实际执行行为。

参考资料

  • 功能总览与官方警告:docs/command-execution.md
  • 钩子运行器核心实现:runner/runner.go、命令解析:runner/parser.go、runner/commands.go
  • 交互式 Shell 的 WebSocket 处理:http/commands.go
  • 钩子在实际文件操作中的调用点:http/resource.go、http/tus_handlers.go
  • CLI 命令族实现:cmd/cmds.go、cmd/cmds_add.go、cmd/cmds_ls.go、cmd/cmds_rm.go
  • 全局开关注册:cmd/root.go(disable-execflag);新用户默认命令:settings/defaults.go

【免费下载链接】filebrowserFile Browser provides a file managing interface within a specified directory and it can be used to upload, delete, preview and edit your files.项目地址: https://gitcode.com/gh_mirrors/fi/filebrowser

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询