# 沙箱
沙箱使用 Linux Landlock 将进程限制在一组可配置的文件系统路径内。它需要显式启用,在所有宿主(`deft`、`retro`、`retra`)中均可用,并由内核强制执行——越出授权路径的 `[fs/write]` 或文件打开操作会直接失败,而不会静默成功。
网络不受限制。Landlock 仅针对文件系统;进程保持完整的网络访问。
## 启用
在环境中设置 `DEFT_SANDBOX=1`(或 `true`):
```bash
$ DEFT_SANDBOX=1 deft script.dft
$ DEFT_SANDBOX=1 retro
````
未设置该变量时,沙箱不生效。在非 Linux 平台以及不支持 Landlock 的旧内核(内核 5.13+,见 内核支持)上同样不生效;这些情况会打印一条消息并继续以非沙箱方式运行,而不是拒绝启动。
沙箱在预置文件和配置注册表加载之后、用户代码运行之前、TUI 事件循环启动之前生效,因此所有初始化读取都已完成。启用是一道单向门(`landlock_restrict_self`):进程此后永远无法扩大自身的访问权限。
## 配置
静态路径列表位于项目预置文件的 `:sandbox` 键下(见预置文件):
%Prelude{
:sandbox %{
:readonly @{ "/etc" "/usr/bin" "/usr/lib" "/usr/lib64" "/usr/share" "/proc" "/sys" }
:writable @{ "~/.config" "~/.local" }
}
}
- `:readonly` —— 可读(并可执行)但不可写的路径。
- `:writable` —— 完全可写的路径:读取、写入、创建、删除。
两个列表都是可选的。应用沙箱时,开头的 `~` 会展开为 `$HOME`,因此 `"~/.config"` 会授权真实主目录下的该子树。
`deft init` 会为你写入一个模板 `:sandbox` 块。你可以直接在 `.deft/prelude.dft` 中编辑这些列表,也可以使用 `deft sandbox` CLI 代为编辑:
```
$ deft sandbox list # show both lists
$ deft sandbox add /srv/data # add read-only
$ deft sandbox add ~/code/write-dir -w # add writable
$ deft sandbox remove /srv/data
````
完整的子命令参考见 [`deft` 二进制文件](/docs/cli?lang=zh)。
## 始终授予的权限
运行时自身会应用一小组宿主必需、由环境派生的授权,且无法通过编辑去除:
| 路径 | 访问权限 | 原因 |
|------|--------|-----|
| cwd 子树 | 完全 | 运行时所在的项目 |
| `/tmp` | 完全 | 临时文件、套接字 |
| `$XDG_RUNTIME_DIR` | rw socket | Wayland/X11 合成器套接字 |
| `$HOME` | 只读 | Xauthority、fontconfig 缓存、配置读取 |
| `/dev`、`/dev/pts` | rw + ioctl | GPU 渲染节点、输入、集成终端 PTY |
| `/` | 仅执行 | 二进制文件的绝对路径解析 |
| 字体路径(TUI) | 只读 | 当配置的字体位于 cwd + `/tmp` 之外时 |
`:writable` 列表中的可写子树(如 `~/.config`)在同一规则集内与只读的 `$HOME` 规则求并集,因此它们变为可写,而不会扩大主目录其余部分的权限。
## 语义与限制
- 目录粒度的授权。 规则以目录为根并覆盖其子树。添加指向文件的列表项会发出警告并被跳过——应改为添加其父目录。
- 单向门。 启动后,进程的文件系统视图在其生命周期内固定不变。不存在运行时逃生通道。
- 仅限文件系统。 套接字、网络和进程信号不受影响。这不是完整的安全沙箱——对于不可信代码,应配合 `runtime/*` 堆隔离使用(见运行时);子运行时共享同一进程,因此也共享同一个沙箱。
- 不是逐应用的边界。 沙箱约束的是整个进程。TUI 宿主中的所有面板以及所有子运行时都运行在同一组授权之内。
## 内核支持
系统会自动检测 Landlock ABI,并使用所支持的最广泛访问权限集:
| ABI | 内核 | 说明 |
|-----|--------|-------|
| v6 | 6.13+ | 作用域访问 |
| v4 | 6.7+ | 规则集属性中的 networking 字段 |
| v3 | 6.2+ | 最高支持 `TRUNCATE` |
| v2 | 5.19+ | 最高支持 `REFER` |
| v1 | 5.13+ | 基线:读/写/创建/删除/执行 |
在不支持 Landlock 的内核上,进程会打印 `sandbox: landlock unavailable (kernel too old)` 并继续不受限制地运行。
## 诊断
被拒绝的操作会以常见的操作系统级错误失败(`permission denied`、打开时的 `no such file or directory`),并作为 `fs/*` 和 `sqlite/*` 标准库函数抛出的普通 Deft 错误呈现。检查启动行 `sandbox: active (landlock, cwd + /tmp + prelude paths)` 以确认沙箱已生效,然后将缺失的路径添加到相应的列表:
```
$ deft sandbox list
read-only:
/etc
/usr/bin
writable:
~/.config
$ deft sandbox add ~/data -w
added ~/data (writable)
````
## 另见
- 预置文件 —— `:sandbox` 键位于 `.deft/prelude.dft`
- [`deft` 二进制文件](/docs/cli?lang=zh) —— `deft sandbox` 子命令
- 运行时 —— 堆隔离与操作系统沙箱的对比