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, 凭证保护, 可视化界面, 安全助手, 数据防泄露, 文件系统, 终端安全, 访问控制, 通知系统