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_copy、after_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内插,也可以直接在脚本中读取环境变量。此外,FILE、SCOPE、TRIGGER、USERNAME、DESTINATION之外的变量名会回落到系统环境变量,因此你仍可访问$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_upload与after_upload两组列表(实现见 cmd/cmds_ls.go)。
添加钩子命令:cmds add
filebrowser cmds add <event> <command>例如官方文档中的示例:
filebrowser cmds add before_copy "echo $FILE"参数<event>需要带上before_/after_前缀(如before_copy、after_upload),后续所有参数以空格拼接为一条完整命令(实现见 cmd/cmds_add.go)。同一事件可反复添加多条命令,它们将按添加顺序依次执行。
删除钩子命令:cmds rm
filebrowser cmds rm <event> <index> [index_end]例如:
filebrowser cmds rm before_copy 0index必须与cmds ls输出中的索引一致。注意每次增删操作后,命令列表的索引都会重新编号,因此连续删除多条命令时务必以最新的ls输出为准。cmds rm还支持可选的index_end参数,用于一次删除从index到index_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,用户也只能执行管理员显式允许的有限命令集——例如只开放ls、cat、df,而不开放rm、shutdown等高危命令。
底层通信:WebSocket 实时管道
交互式 Shell 基于 WebSocket 实现,而非普通的 HTTP 请求。其完整流程位于 http/commands.go:
- 客户端发起 WebSocket 升级(
websocket.Upgrader),等待客户端发送第一条非空命令消息; - 服务端完成「全局开关 + 用户 Execute 权限 + 命令白名单」三重校验;
- 启动
exec.Command,并将进程的工作目录设置为用户当前浏览目录的绝对路径(cmd.Dir = d.user.FullPath(r.URL.Path)); - 通过
StdoutPipe/StderrPipe捕获输出,用bufio.Scanner逐行读取并通过 WebSocket 实时推送给浏览器端; - 命令退出后返回结果并关闭连接。
这也引出了文档中的一条重要运维注意事项:如果 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 构建)。
最佳实践与安全建议
综合上文,启用命令执行功能时建议遵循以下原则:
- 默认关闭,按需开启:除非确有自动化需求,否则保持 v2.33.8+ 的默认禁用状态;开启时优先使用
--disable-exec=false或FB_DISABLE_EXEC=false显式控制,并确认只有受信任的管理员能接触到该实例。 - 钩子命令收敛事件面:仅对必要事件(如
after_upload)注册钩子,并利用$FILE、$DESTINATION等环境变量把操作精确限定在目标文件上;命令失败会中断文件操作,因此before_钩子应保证幂等与快速失败。 - Shell 命令白名单最小化:交互式 Shell 的命令列表务必按最小权限授予,不向普通用户开放
rm、mv、shutdown、包管理等危险命令;Admin 用户同样需要显式配置,并不会自动拥有全部命令。 - 代理需放行 WebSocket:任何反向代理配置都必须支持 WebSocket 升级,否则 Shell 功能不可用。
- Docker 自定义命令走镜像构建:需要新命令时以
filebrowser/filebrowser为基础构建镜像,而不是在运行时临时安装。 - 留意执行日志:阻塞与非阻塞命令均会输出
[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),仅供参考