【开发者自荐】一种新的 Linux沙箱工具 Tree Sandbox

Tree Sandbox for Linux

Linux 上有流氓软件了吗?还没有,但以后说不定随时会有…

Linux用户一定玩过 Podman、Firejail、Flatpak、Bubblewrap

那些工具有些地方不甚如意,我亲手肝了几个月,造了一款新的。

我的 Tree Sandbox,免特权Linux沙箱 ,已在GPL协议下开源,Github仓库地址在这里

就算不跑流氓软件,用来 跑龙虾 :lobster: 也不错,要隔离嘛、要安全嘛 :star_struck:

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

  • 看门狗

声名

  1. 所用之场、所带之器、所载之物、所作之事,法理之宜,用户自判自决自断自承,好坏责任,咸归用户。
  2. 请用户遵守要运行的App的使用条款,若因用户违反产生的纠纷与本项目无关。

开源协议

在GPL协议下开源

1 个赞

star了,还没试,就不知道怎么推广开

感谢支持。估计这里linux用户本来就少,慢慢让有需要的人发现吧

支持,已star

这个好啊,省了虚拟机了