Tree Sandbox for Linux
Linux 上有流氓软件了吗?还没有,但以后说不定随时会有…
Linux用户一定玩过 Podman、Firejail、Flatpak、Bubblewrap
那些工具有些地方不甚如意,我亲手肝了几个月,造了一款新的。
我的 Tree Sandbox,免特权Linux沙箱 ,已在GPL协议下开源,Github仓库地址在这里
就算不跑流氓软件,用来 跑龙虾
也不错,要隔离嘛、要安全嘛
。
Tree Sandbox 是 “树形”沙箱,多层容器嵌套、分枝,像由多个子容器组成的“树”。
什么是"容器树"
Tree Sandbox 设计成一个沙箱由多层子容器构成,它们连成一棵“容器树”,“树”可以有多个分枝和容器节点。容器节点之间的“连接”可以是各类互相 “已unshare” 或 “未unshare” 的 namespace。
这种设计下,“不信任”进程 与 “半信任”进程 可在一个沙箱的不同层运行;用户要运行的 主app 与其他 辅助进程 也是在不同的层运行。
以下是一个例子,沙箱容器树可能是像这样的:
[Linux Host]
主机X11
主机DBUS服务
[TreeSandbox沙箱]
|
|--[子容器 : 不信任空间]
| |
| |--[子容器 : 用户App : 不信任]
| | 用户的App在这里跑
| |
| |--[子容器 : 辅助进程(组2): 不信任]
| 内部的X11服务
| 内部的DBUS服务
|
|--[子容器 : 辅助进程(组1) : 半信任]
在内、外部X11之间转发的进程
DBUS通信代理和过滤进程
我们用了“容器树”后,在 无需主机 subuid / subgid 的情况下实现沙箱内部的不同“级别”的进程之间的互相隔离。
内部实现可精细控制每层隔离程度、每层可见文件范围。
如果你愿意,也可以用这个机制玩无限嵌套。
快速试用
git clone --shallow-since=2026-03-01 https://github.com/garywill/treesandbox
cd treesandbox
python3 -IBS ./treesandbox.py
(-IBS = 不需要第三方python库)
如果沙箱内的shell提示符出现了,那么恭喜,运行成功!
现在你可以看看 依赖列表 ,考虑是否安装一些额外软件,以解锁更多整合了的功能。
Tree Sandbox 的一些不同玩法
先用些例子演示一下 Tree Sandbox 的一些不同之处。
三言两语,不能尽述,略谈一二。
例 - 两个App放同一沙箱
假设你有两个App, 叫 VSCode 和 MSEdge , 它们来自同一厂商,因此你想要把它们放入一个叫 ms 的沙箱内运行,以让它们两之间更好地交互(假设它们是会互相交互的吧)。
Tree Sandbox 支持将多个不同App放同一沙箱里,并提供“选择-启动”方式。
假设在经过正确的配置后,主机可以通过以下命令来调用 MSEdge 浏览器,打开 Github:
tsbxrun_ms.py --app msedge https://github.com # 1
假设现在我们要干活写代码了,主机又要调用 VSCode 来编辑一些文件:
tsbxrun_ms.py --app vscode main.c zlib.h # 2
tsbxrun_ms.py --app vscode app.js # 3
已经发生了3次调用了。然后,假设主机又要调用沙箱里的浏览器,用新标签打开Linux官网:
tsbxrun_ms.py --app msedge https://www.kernel.org # 4
以上已经假设主机进行了多次调用 tsbxrun_ms.py 。为了让后面的调用复用第一次打开的沙箱,我们需要把沙箱配置成“复用型”的。
本例的 userconfig 配置如下(简略):
uc.sandbox_name = 'ms'
uc.reuseful = True
uc.apps = [
d(cmdvec=['/somepath1/microsoft-edge'], appname='msedge'),
d(cmdvec=['/somepath2/code'], appname='vscode'),
]
例 - localhost的“部分融合”
其他沙箱也有网络方面的类似功能,但我们目前有一点小优势。
假设主机有程序监听本地22、53、8000。不想暴露 22 给沙箱,但 53 和 8000 希望沙箱能访问,而且沙箱 直接通过127.0.0.1 访问, 省去 子网网关IP配置 等。
沙箱内也运行程序,它监听端口 1080 。同时又希望主机能访问沙箱的 1080 ,也是 直接通过127.0.0.1 ,省去 子网客户端IP配置 等。
我们请出 Tree Sandbox 集成的 pasta 。 (pasta 少被人提及,它是Podman的passt项目的一部分,替代slirp4netns 。)我们需要在 Tree Sandbox 的 userconfig 里配置(简略):
uc.net_iface='tuntap-pasta'
uc.pasta_custom_args = [
'-T', '53,8000', '-U', '53,8000' ,
'-t', '1080', '-u', '1080',
...
]
这样实现了对主机与沙箱的 localhost 的“部分融合”。
其他沙箱工具也有类似功能,但我们使用 pasta 优势在于:
- pasta使用的是 tun/tap ,整个过程不涉及主机的root
- 主机不会多出一个像
docker0那样的介面 - 沙箱自身看到的 IP 和 MAC 都可以设置,甚至可让IP与主机的看起来一样,而不冲突
此外,像这样用了自己管理的网络介面,也可设置沙箱内 nftables 规则(免root),玩法就很多了,相信不必多说nftables的强大。
例 - 在沙箱里用 AppImage
你可能遇到过,从网上下载 AppImage文件 后,尝试放进某沙箱里跑,因沙箱禁止了 CAP_SYS_ADMIN ,fuse无法工作,而跑不起来的情况。
Tree Sandbox 可以替 AppImage 完成挂载工作,不需要给它 fuse 权限。用法一般是(配置):
uc.user_mnts = [
d(many_op='appimage', name='SomeName', src=f'/path/xxxx.AppImage')
]
AppImage 里的 squashfs 会挂载到沙箱内的路径,并创建一个对应的启动脚本:
/sbxdir/apps/SomeName/ # squashfs (AppImage) mounted
/sbxdir/apps/run_SomeName # Start script for it
你还应该在配置中加上:
uc.apps = [
d(cmdvec=['/sbxdir/apps/run_SomeName'])
]
或者更简单的: (因为 /sbxdir/apps 会被自动加入到 PATH)
d(cmdvec=['run_SomeName'])
例 - 主机同时操作多个 shell
若你有某个沙箱,经常要从主机同时连接沙箱内多个 shell 会话,那么可以这样配置:
uc.reuseful=True
uc.apps = [
...
d(cmdvec=['bash'], appname='bash'),
...
]
(“reuseful”的含意之前解释过)
要 启动此沙箱 时,或 主机要连接沙箱内新 shell session 时,主机可以用命令:
tsbxrun_mysandbox.py --reusefg --app bash
--reusefg 意思是 “reuse in foreground”。
与其他沙箱工具对比
与 Firejail / Flatpak 对比
| 功能 | Tree sandbox | Firejail | Flatpak |
|---|---|---|---|
| 文件系统: 隐私vs体积vs便捷 | ◐ 从主机fs中选需要的路径。得分2/3 | ✘ 用主机fs,仅遮盖要保护的路径。得分1/3 | ◐ 下载容器镜像。得分2/3 |
| 同App沙箱单实例 (多次发送命令参数到运行中的沙箱) | ● | ● | ● |
| 同App沙箱多实例 (互相隔离独立) | ● | ● | ✘ |
| 容器嵌套 | ● 工作原理为多层容器树。“不信任”进程 与 “半信任”进程 在一个沙箱的不同层运行 | ✘ 拒绝嵌套 | ✘ |
| 免安装,免编译,免守护进程 | ● 单文件.py,完全无需root | ✘ 需要安装并有设置suid | ✘ 需要守护进程 |
| 开箱即用(对于具体App) | ◐ 用户需先设置一些选项 | ● 有一些内置 app profile | ● Flathub |
| 不在真实家目录产生文件 | ● | ● | ✘ |
| 沙箱内部调用xdg-open时在外部打开 | ● 可替换xdg-open为弹出询问,用户复制url/路径/参数 | ✘ | ● 由门户管理 |
| 动态决定沙箱内可访问哪些文件或硬件 | ✘ 固定的事先配置好的挂载表 | ✘ 固定的事先配置好的挂载表 | ● 门户做动态临时挂载/授权,但沙箱内得到的文件路径不确定 |
| 主与机 unshare net ns ,沙箱可以连接互联网,选择性“融合”主机和沙箱的localhost端口 | ● tun/tap + nftables (免root) 细粒度控制 | ◐ | ◐ |
与 Bubblewrap 对比
| 功能 | Tree sandbox | Bubblewrap |
|---|---|---|
| 沙箱配置方式 | 编辑配置文件 | 写CLI参数 |
| 开箱即用(对于基本系统沙箱) | ● | ✘ 需要较长的参数来搭建基本系统 |
| 常用工具的集成 (如隔离X11/转发、DBUS过滤代理),和常用socket路径挂载的便捷选项 | ● | ✘ |
| 同App沙箱单实例 (多次发送命令参数到运行中的沙箱) | ● | ✘ |
| 同App沙箱多实例 (互相隔离独立) | ● | ● |
| 容器内部shell接口暴露给主机,主机随时获取 | ● | ✘ 需要主机root做nsenter |
功能列表与完成状态
-
不需root;不需守护进程;不需主机的Cap或suid;不需要subuid/subgid
-
无镜像容器。内部诸如vim、git等工具无需重复安装
-
沙箱内使用 GUI
- 可选暴露真实 X11 接口给沙箱
- 可选使用 Weston + Xwayland 隔离 X11(GPU可用)(配icewm)
- 可选使用 Xephyr 隔离 X11(配icewm)
- 可选使用 Xpra 隔离的 无缝X11 代理
- 可选同步剪贴板(从沙箱到主机。反过来的时候可暂时用IME的粘贴功能替代)
-
容器内部shell接口暴露给主机,主机随时获取(已部分可用。计划更完善)
-
可选暴露真实物理硬件给沙箱
- 暴露GPU给沙箱
- 暴露所有硬件给沙箱
-
DBus
- 可选暴露真实dbus接口给沙箱
- 可选过滤dbus通信
-
沙箱网络
- 可选的不管理沙箱网络(不 unshare net ns)
- 可选的网络控制
- 主机与沙箱之间双向、单向端口范围暴露(tun,由pasta管理。可通过 localhost:端口号 互访)
- 内部nftbles规则(免root)自定义
-
可挂载 AppImage、squashfs 在内部访问其内容
-
可选暴露 pulseaudio 接口给沙箱
-
可选暴露 CUPS 接口给沙箱
-
同名沙箱的:单App/多App;单实例/多实例 (启动时的App选择、实例管理、命令参数的传递)
说明:以用户设置的
sandbox_name来识别 “同名沙箱”- 一个沙箱可设置多个app,启动时可指定app(例如 我们可把同一厂商出品的不同app可以放同一沙箱里,便于它们之间交互)
- 同名沙箱多实例(从主机多次启动沙箱,会运行多个实例,互相隔离、互相独立)
- 同名沙箱单实例(从主机启动一种沙箱后,再次启动这种沙箱,则传递命令参数至已运行的沙箱)
-
“树形容器”内部原理实现了:
- 每层与其上层之间的每种 ns 的隔离与否 (是否unshare) 选项控制
- 每层内部环境变量控制
- 每层的新 rootfs 挂载细粒度文件系统建立列表控制
- bind 挂载(rw/ro) 目录、文件、套接字、字符设备,symlink,tmpfs 等
- overlayfs (计划中)
-
启动时内部uid变0(提权);进程uid变回1000(降权);Drop caps;noNewPrivs ; procfs hidepid=1
-
看门狗
声名
- 所用之场、所带之器、所载之物、所作之事,法理之宜,用户自判自决自断自承,好坏责任,咸归用户。
- 请用户遵守要运行的App的使用条款,若因用户违反产生的纠纷与本项目无关。
开源协议
在GPL协议下开源