gantrydev/file-guard

GitHub: gantrydev/file-guard

file-guard 在 Linux 上通过 FUSE 拦截层实现凭证文件的进程级读写访问控制,防止以当前用户身份运行的恶意进程窃取磁盘上的密钥。

Stars: 0 | Forks: 0

# file-guard 针对凭证文件的进程级访问控制。*类似于 Little Snitch,但用于文件读取。* 任何以你的身份运行的进程都可以读取 `~/.aws/credentials`、`~/.config/gcloud/...`、 `~/.claude/.credentials.json`、你的 SSH 密钥——并将它们窃取。文件 权限不起作用:恶意代码**以你的身份运行**。这就是 供应链密钥窃取问题:一个被投毒的依赖项会读取磁盘上的每一个密钥。 密码管理器(1Password、`pass`、…)为那些接受 注入的环境变量(`op run`、`.envrc` + `op read`)的工具解决了这个问题。但许多工具**会 将明文凭证写入磁盘并自行读取**——`gcloud`、`aws`、 `docker login`、`kubectl`、`gh` 以及 Claude Code CLI。你无法将这些凭证仅保存在 保险库中。file-guard 会在这些磁盘文件前进行拦截,仅允许 **你已授权**的进程读取*和写入*它们;其他所有操作都会被拒绝 (或触发提示)。 ## 工作原理 对于每个受监视的文件,file-guard 会将真实内容移动到后备存储中, 并通过拦截层在原始路径上提供服务。在每次 `open()` 时,它会 解析**调用进程**并根据你的策略,按照访问的**方向** (读取还是写入)进行检查: | 策略状态 | 操作 | |-------------------------|------------------------------------------| | 允许(规则) | 提供 / 接受真实内容 | | 拒绝(规则) | 返回 `EACCES` | | 允许(本次会话) | 提供 / 接受真实内容 | | 未知 | 提示你(或回退到 `default_action`) | 一个可读写的 FUSE 文件被挂载到原始路径;调用者 PID 来自于 FUSE 请求,并通过 `/proc//exe` 进行解析。读取操作提供存储的 内容;授权的写入会被缓冲,并在 关闭时持久化回存储中。使用的工具无需重新配置:它们读/写的路径保持 不变。 (代码库中存在一个 macOS Endpoint Security 后端,但尚未构建——参见 [macOS](#macos)。) ### 身份:通过内容哈希锁定 临时授权(“允许一次 / 本次会话”)绑定到**确切的进程 实例**(pid + 启动时间),因此被回收的 PID 无法继承它。永久的 “总是允许”规则会**锁定二进制文件的 sha256**(在 macOS 上,还包括其代码 签名);对于解释器,它还会锁定**入口脚本的路径和 内容哈希**,因此“运行 gcloud 的 python”不会授权给其他脚本,并且 对脚本的就地编辑会重新触发提示。如果被锁定的二进制文件或脚本随后 发生更改——比如包升级,或者恶意软件在其位置进行了替换——该锁定将不再 匹配,并且 file-guard 会**重新提示**,而不是默默地遵守旧的 授权。(不匹配会重新提示;这不是硬性拒绝,因此合法的重新构建 只需重新授权即可。) ### 提示:会话代理 Root daemon 没有终端或显示器,因此它不会自行绘制提示—— 它会以你的身份运行的一个小型**会话代理**(`file-guard agent`)发出请求,通过 unix socket 进行通信。该代理渲染提示(通过 `zenity`/`kdialog` 的 GUI,终端 回退,或桌面通知)并返回你的选择。如果无法连接到代理, daemon 会应用 `default_action`(默认为拒绝)——它永远不会 阻塞。有关为什么该 socket 是由 root 锚定的,请参阅[代理 socket 说明](#security-model--limitations)。 ## 安装说明 ### Debian / Ubuntu 从[发布页面](https://github.com/gantryops/file-guard/releases)获取 `.deb` 包: ``` sudo apt install ./file-guard_*_amd64.deb ``` 它会安装二进制文件、一个 root `file-guard.service`,以及一个 socket 激活的 `file-guard-agent@.service`,其监听 socket 由 root 创建(加固 拓扑),并拉取 `fuse3`。在你配置 完成之前,什么都不会启用——请参阅 [2b](#2b-privileged-daemon-the-secure-deployment) 和 [`packaging/README.md`](packaging/README.md)。 ### Nix ``` # 免安装体验 nix run github:gantryops/file-guard -- --help # Dev shell (为 pkg-config 配置 cargo, rustc, clippy, rustfmt, fuse3) nix develop cargo build # 构建二进制文件 nix build github:gantryops/file-guard ./result/bin/file-guard --help ``` ### 从源码构建 任何带有 `pkg-config` 和 `libfuse3`(Debian/Ubuntu:`fuse3`、 `libfuse3-dev`)的 Linux,再加上 Rust 工具链,然后运行 `cargo build --release`。 ## 快速开始 ### 1. 编写配置 从 [`config.example.toml`](config.example.toml) 开始,或者从 [`configs/`](configs/) 中组合各工具的代码块: ``` { cat configs/_settings.toml configs/aws.toml configs/gcloud.toml configs/claude.toml; } \ | sudo tee /etc/file-guard/config.toml ``` ### 2a. 开发模式(你自己的用户 - 不安全,请参阅限制说明) ``` FILE_GUARD_CONFIG=~/.config/file-guard/config.toml file-guard start ``` 在前台运行;未知访问会在终端中提示你。这有助于 查看是什么在触碰你的密钥,但后备存储可以被你自己的用户读取 (因此同 uid 的恶意软件可以绕过它)。要获得真正的保护,请使用 2b。 ### 2b. 特权 daemon(安全部署方式) 以 **root** 身份运行 daemon,这样 `/var/lib/file-guard` 中的后备存储 就由 root 拥有,运行恶意软件的用户将无法读取它。**Debian 软件包和 NixOS 模块都会这样做**——那个由 root 拥有的存储才是 至关重要的保护,并且**默认情况下两者都由 root 锚定提示代理的 socket** (通过 systemd socket 激活由 root 创建,因此同 uid 的攻击者无法 劫持它;请参阅[代理 socket 说明](#security-model--limitations))。 **Debian / Ubuntu。** 安装 `.deb` 包后(替换 `alice`): ``` echo 'FILE_GUARD_USER=alice' | sudo tee -a /etc/default/file-guard # whose ~ is guarded sudoedit /etc/file-guard/config.toml # add [[watch]] blocks sudo systemctl enable --now file-guard-agent@alice.socket # root-anchored socket sudo systemctl edit file-guard-agent@alice.service # add DISPLAY/XAUTHORITY/DBUS env sudo systemctl enable --now file-guard.service ``` **NixOS。** 添加 flake 并启用该模块: ``` # flake.nix { inputs.file-guard.url = "github:gantryops/file-guard"; # … } # configuration.nix { imports = [ inputs.file-guard.nixosModules.default ]; services.file-guard = { enable = true; user = "alice"; # whose ~ is guarded configFile = "/etc/file-guard/config.toml"; # paths use ~ → alice's home }; } ``` 该模块会设置 `programs.fuse.userAllowOther = true`,以便你的工具可以访问 root 拥有的挂载,并连接一个以 `user` 身份运行的 **socket 激活提示代理**。对于 GUI 提示,请将代理指向该用户的会话并切换 方法: ``` services.file-guard = { enable = true; user = "alice"; configFile = "/etc/file-guard/config.toml"; promptMethod = "gui"; # default: "notification" agentEnvironment = { # so dialogs reach alice's display DISPLAY = ":0"; XAUTHORITY = "/home/alice/.Xauthority"; DBUS_SESSION_BUS_ADDRESS = "unix:path=/run/user/1000/bus"; }; }; ``` 该代理的 socket 由 **root**(systemd socket 激活)在一个 root 拥有的目录中创建,因此同 uid 的攻击者既无法劫持 socket 名称, 也无法连接到它。使用 `promptMethod = "notification"`(默认值)时,提示 仅提供信息,未知访问会在超时时被拒绝——请为该模式定义显式的 `[[rule]]`。 要手动(开发时)尝试 GUI 提示路径,请在你的图形会话中运行代理, 并在其旁边运行 daemon(两者均以同一用户身份运行,解析同一个 socket): ``` file-guard agent --method gui & # renders prompts in your session FILE_GUARD_CONFIG=~/.config/file-guard/config.toml file-guard start ``` ## 配置 单个 TOML 文件(目前还没有 `include` 机制)。完整的带注释参考请参阅 [`config.example.toml`](config.example.toml),每个工具可直接插入的代码块请参阅 [`configs/`](configs/)(aws、gcloud、claude、ssh、 docker、kubernetes、github、npm)。 通过“总是允许”/“总是拒绝”提示创建的规则会自动附加到 配置文件中。 ## CLI ``` file-guard start [-d] # run the daemon (foreground; -d is a no-op) file-guard agent [--method M] [--socket P] # run the session prompt agent file-guard stop # SIGTERM the running daemon (unmounts cleanly) file-guard status # daemon state, mount status, recent access file-guard log [-n N] [-f] # print/follow the audit log (needs a file # log_destination; else use journalctl) file-guard rules # list rules (with indices) file-guard rules add --file F --binary B --action allow|deny [--access read|write|any] [--no-pin] file-guard rules remove # remove the rule at INDEX (preserves comments) file-guard store # move a file into the backing store file-guard restore # restore a file from the backing store ``` 当 `log_destination` 是文件 路径时,审计日志为 NDJSON 格式(每次访问对应一个对象),因此既可以通过 `file-guard log` 由 人类阅读,也可以由机器查询 (例如对文件使用 `jq`)。 ## 安全模型与限制 **威胁模型:** 以*你*的身份运行的非 root 恶意软件(被投毒的依赖项), 试图读取或写入凭证文件。**不在**范围内:root 攻击者(root 绕过 FUSE 并可以读取任何内容)、对你的会话具有 `ptrace` 权限的进程 (它可以驱动代理或你的任何进程),或网络窃取。 已知限制——在依赖此工具之前请先阅读: - **必须以特权身份运行,否则它在 Linux 上不起作用。** 后备存储必须 由与受保护用户*不同*的 uid 拥有;否则同样的恶意软件 就会直接读取存储。出于这个原因,Debian 软件包和 NixOS 模块都会 以 root 身份运行 daemon。以你自己的用户身份运行仅适用于 开发环境。 - **提示代理必须由 root 锚定才能被信任**——并且这两种 打包部署方式默认都做到了这一点。如果同 uid 的恶意软件能够占用 代理的 socket,它会自动批准自己的提示;NixOS 模块和 Debian 软件包都通过让 **root** 在 root 拥有的目录中创建 socket (systemd socket 激活)(路径为 `/run/file-guard/agent.sock`,模式为 `0600`)来防止这种情况。唯一未加固的路径是仅在开发时使用的 `file-guard agent` 在 `$XDG_RUNTIME_DIR` 中的自绑定,它会发出强烈的警告,并且是 用于测试的,而不是用于保护的。 - **仅支持 Linux。** macOS Endpoint Security 路径尚未构建——请参阅 [macOS](#macos)。 - **身份 = 二进制哈希(对于解释器,为脚本路径和内容哈希); 受信任工具自身的依赖项仍在边界之内。** 规则会锁定 调用者的二进制 sha256,对于解释器(python/node/…),还会从 argv 中锁定**脚本 路径**和**脚本的内容哈希**——因此“运行 gcloud 的 python” 不会授权给“运行其他程序的 python”,并且对脚本的就地编辑 会重新提示。仍然有两个注意事项:脚本*路径*来自于 argv, *蓄意的*冒充者可以伪造它(这是一种纵深防御,对于 opportunistic 的磁盘扫描恶意软件最为有效,而不是硬边界);此外,没有任何东西可以阻止 合法工具*内部*被破坏的依赖项读取该 工具已被授权使用的密钥。对于编译型工具最为强大,因为二进制文件*本身* 就是身份。 - **Nix/home-manager:** 解析的路径是 `/nix/store/` 路径,每次 包更新时都会更改。哈希锁定的规则在升级后**会重新提示** (这是设计使然)——只需重新确认即可。作为**符号链接**的凭证文件 (例如将 `~/.npmrc` 指向只读的 Nix store)现在会被**拒绝**,而不是被覆盖; 请将监视指向真实文件。 - **GUI 需要会话。** 在 systemd 下,只有在为代理提供用户的 显示环境变量(`agentEnvironment`)时才会出现 GUI 提示;否则它将回退到 通知/终端,未知访问会在超时时被拒绝。 - **写入采用最后写入者获胜策略。** 指向同一文件的并发写入句柄 不会合并;最后关闭的句柄会持久化其缓冲区。这对于 单写入者的凭证文件场景没有问题。 ## 对比 file-guard 占据了一个特定的细分领域:**针对凭证文件的每次访问同意,这些文件自身的工具坚持从磁盘读取**, 且*工具保持不变地继续工作*。这与其他常见的工具有所不同: | 工具 | 模型 | file-guard 的不同之处 | |---|---|---| | **Landlock / AppArmor / SELinux** | 静态内核 MAC:配置文件预先拒绝进程的访问 | file-guard 是*交互式同意* + 针对每个二进制文件的**哈希/脚本身份**,在发生更改时会重新提示;受保护的工具继续在其正常路径上工作,而不是被静态拒绝。Landlock 也是*由进程自身*选择加入的——恶意软件不会自己把自己沙盒化。 | | **bubblewrap / firejail / 容器** | 将*不受信任的*程序隔离在密钥之外 | 当你可以枚举并封装不受信任的内容时非常棒——但是**需要**凭证的工具(`aws`、`gcloud`)不能被与其隔离。无论谁打开文件,file-guard 都会保护该*文件*。 | | **1Password / `op run` / 环境变量注入** | 将密钥作为环境变量注入到接受它们的工具中 | file-guard 的细分领域恰恰是那些**不接受**的工具——它们将明文凭证写入磁盘并重新读取。它位于任何后端的前面,而不是取代保险库。 | | **短期凭证 / OIDC / 硬件密钥** | 完全移除磁盘上的长期密钥 | 在提供商支持的情况下这是正确的修复方法;file-guard 保护的是许多仍然在磁盘上保留长期密钥的工具的反模式。 | **它刻意*不*做的事情:** 阻止在你已经授权的工具*内部*运行的被破坏的依赖项(它会获得该工具的密钥),抵御 root 或具有 `ptrace` 权限的同 uid 进程或控制网络窃取。针对这些情况,请将其与沙盒和短期凭证结合使用—— file-guard 将爆炸半径从*磁盘上的每一个密钥*缩小到*特定已授权二进制文件被允许触碰的特定文件*,它并不是一个完整的边界。 ## 开发说明 ``` nix develop cargo build cargo clippy --all-targets -- -D warnings cargo fmt --check cargo test ``` CI 会在 Linux 上为每次推送/PR 运行上述内容。 ## macOS 代码库中存在一个 macOS Endpoint Security 后端(`src/es.rs`、 `src/process/macos.rs`)但尚未构建:它被排除在 flake/CI 之外,并且 未连接到当前的策略/代理。完成它需要修复 `es_message_t` 布局和 `start_time` 偏移量,并且——至少要能运行——还需要 来自 Apple 的 Endpoint Security entitlement,以及一个经过签名和公证的 root 二进制文件。 策略、规则、身份锁定和提示代理已经是 跨平台的,因此 macOS 的工作是连接执行机制,而不是重写。 ## License MIT - 请参阅 [LICENSE](LICENSE)。
标签:FUSE, macOS端点安全, Python安全, SamuraiWTF, Streamlit, StruQ, 凭证保护, 可视化界面, 安全助手, 数据防泄露, 文件系统, 终端安全, 访问控制, 通知系统