常见问题解答

我可以使用线程吗?

可以,Sandbox2 支持线程。

所有线程都必须经过沙盒化

根据 Linux 的工作方式,seccomp-bpf 政策仅应用于当前线程:这意味着,该政策不会应用于其他现有线程,但未来的线程会继承该政策:

  • 如果您在第一种模式(即在 execve() 之前启用沙盒)使用 Sandbox2 时,所有线程都会继承该政策,而且不会出现问题。这是沙盒的首选模式。
  • 如果您使用的是第二模式,在该模式下,执行器具有 set_enable_sandbox_before_exec(false),并且 Sandboxee 会在想要使用 SandboxMeHere() 进行沙盒化时告知执行器,请确保将过滤器应用于所有线程。否则,存在沙盒逃逸的风险:恶意代码可能会从沙盒化线程迁移到未沙盒化的线程。

应如何编译 Sandboxee?

如果不小心,很容易继承许多依赖项和副作用(额外的系统调用、文件访问甚至网络连接),这会使沙盒更难(跟踪所有副作用)且安全性较低(因为系统调用和文件政策更宽)。一些编译选项有助于减少此类问题:

  • 静态编译 Sandboxee 二进制文件,以避免动态链接使用大量系统调用(open/openatmmap 等)。
  • 由于 Bazel 默认添加 pie,但 static 与其不兼容,因此请考虑使用 cc_binary 规则中的以下选项强制将其停用:

    linkstatic = 1,
    features = [
        "fully_static_link",  # link libc statically
        "-pie",
    ],
    

但是:使用 static 会降低 ASLR 堆熵(从 30 位到 8 位)的缺点,从而简化漏洞攻击。根据您的沙盒实现方式和政策,仔细确定可取的做法:

  • 非静态:良好的堆 ASLR,可能更难执行初始代码,但代价是沙盒政策效率较低,可能更容易突破。
  • static:错误的堆 ASLR,可能更容易让初始代码执行,但更有效的沙盒政策,可能更难破解。

很遗憾,您做出了这样的选择,因为编译器不支持静态 PIE(位置无关可执行文件)。PIE 的实现方式是将二进制文件设为动态对象,并且动态加载器会先将该文件映射到随机位置,然后再执行。然后,由于一直以来,堆会放置在二进制文件基址之后的随机偏移处(并使用 brk 系统调用进行扩展),这意味着对于静态二进制文件,堆 ASLR 熵只是此偏移量,因为没有 PIE。

如需这些编译选项的示例,请查看静态示例 BUILD.bazelstatic_bin.cc 是以静态方式编译的,这允许我们采用非常严格的系统调用政策。这对于沙盒化第三方二进制文件也非常有效。

我可以沙盒化 32 位 x86 二进制文件吗?

Sandbox2 只能采用与编译时相同的架构进行沙盒化。

此外,Sandbox2 已移除对 32 位 x86 的支持。如果您尝试使用 64 位 x86 Executor 对 32 位 x86 二进制文件进行沙盒化,或使用 64 位 x86 二进制文件进行 32 位系统调用(通过 int 0x80),两者都将产生可通过架构标签 [X86-32] 识别的沙盒违规行为。

这种行为背后的原因是架构之间的系统调用编号不同,并且由于系统调用政策是在执行程序的架构中编写的,因此允许 Sandboxee 使用不同的架构是很危险的。事实上,这可能会导致允许看似无害的系统调用,实际上意味着另一个有害的系统调用可能会打开沙盒逃避。

对执行程序进程可以请求的沙盒数量是否有限制?

对于每个 Sandboxee 实例(从 forkserver 生成的新进程),都会创建一个新线程,而这正是限制所在。

执行器是否可以请求创建多个沙盒?

不是。存在 1 对 1 的关系:执行器实例存储沙盒对象的 PID,管理与沙盒实例的通信实例,等等。

为什么我在 forkserver.cc 中看到“未实现函数”?

Sandbox2 仅支持在合理的新内核上运行。我们目前的终止版本是 3.19 内核,但将来可能会发生变化。这是因为我们使用的是相对较新的内核功能,包括用户命名空间和带有 TSYNC 标志的 seccomp。

如果您在生产环境中运行,这应该不成问题,因为几乎整个舰队都在运行足够新的内核。如果您对此有任何疑问,请与我们联系。

如果在 Debian 或 Ubuntu 上运行,则更新内核就像运行一样简单:

sudo apt-get install linux-image-<RECENT_VERSION>