深入解析 Dangerzone + gVisor:用应用内核为容器化文档转换筑起第二道防线

发布时间:2026/9/13 13:20:50
深入解析 Dangerzone + gVisor:用应用内核为容器化文档转换筑起第二道防线 深入解析 Dangerzone gVisor用应用内核为容器化文档转换筑起第二道防线【免费下载链接】gvisorApplication Kernel for Containers项目地址: https://gitcode.com/GitHub_Trending/gv/gvisor摘要导读本文以 gVisor 官方博客《Safe Ride into the Dangerzone》为骨架系统拆解 Dangerzone 0.7.0 如何利用 gVisor 加固可疑文档安全转换这一高风险场景。你将理解 Linux 容器隔离方案的攻击面短板、gVisor 应用内核Sentry Gofer拦截与重实现系统调用的底层原理以及容器套容器跨平台集成方案中的关键配置ptrace、SYS_CHROOT、SELinux 标签并看到 runsc、seccomp 过滤器、gofer 文件代理在 gVisor 仓库中的真实实现证据。为什么要安全地打开文档Dangerzone 的使命计算机安全界最常被重复的一句忠告是不要打开陌生人发来的随机附件。但对记者journalist来说打开附件和文档本身就是工作内容的一部分——他们已经在应对来自线人source的各种安全威胁文档的安全打开不应该再成为额外负担。Dangerzone 正是为解决这一问题而诞生它让你有信心打开可疑文档然后不挡你的路。Dangerzone 的用途是净化sanitize文档移除任何可能危害你电脑或泄露线人身份的元素——典型威胁包括恶意软件和文档元数据metadata。为了支持多种文档格式PDF、Office 文档、图片格式等它必须用可能存在安全缺陷的软件去打开它们这与直接用电脑打开未知来源附件的风险完全相同。因此 Dangerzone 必须把这一过程与电脑的其他部分隔离让容器内发生的一切都出不了盒子。 Dangerzone 的隔离依赖Linux 容器。容器有两个优点恰好都被用到一是跨操作系统行为一致便于开发与测试二是与宿主机其余部分隔离。需要说明的是在 Windows 和 macOS 上Dangerzone 依赖 Docker Desktop——它先在虚拟机里运行 Linux再在其中运行 Linux 容器。在计算机安全领域隔离的黄金标准是虚拟机VM。Dangerzone 曾尝试使用虚拟机但跨平台实现维护成本过高最终回到容器方案——但团队一直希望提升 Dangerzone 的安全属性。这为 gVisor 的登场埋下了伏笔。Dangerzone 的攻击面容器隔离的四个短板要理解如何保护用户免受利用exploit最好的方式是站在攻击者角度思考。当 Dangerzone 在容器内处理恶意文档时攻击的第一个目标是打开文档的应用程序。Dangerzone 的设计前提是坚定的攻击者一定会找到这类应用的漏洞并接管它们Dangerzone 团队曾就 LibreOffice 的关键漏洞发布过安全公告。下一步攻击就是绕过容器的 Linux 内核保护或直接攻陷 Linux 内核。容器内的进程通过系统调用system call和虚拟文件系统与 Linux 内核交互攻击者可以利用这些接口中的安全缺陷。我们把容器对 Linux 内核的访问面称为攻击面attack surface——它越小系统越安全。Dangerzone 在 0.7.0 之前通过以下机制缩小攻击面移除进程能力capabilities减少容器在内核中拥有的权限集合移除网络访问防止容器联网泄露文档数据通过 seccomp 过滤允许的系统调用减少容器可对内核发起的系统调用即动作类型集合最小化用户 IDUID映射降低容器访问同一台机器上其他用户文件的风险。即便如此仍存在可观的残余攻击面宿主机用户仍被映射进容器一旦容器逃逸攻击者就能访问用户的个人文件浏览器数据、文档等系统调用过滤器仍然相对宽松具体被拦截的系统调用取决于容器管理器和版本例如 Docker 的过滤器通常只拦截冷门或系统管理员专属的系统调用如重启、修改系统级设置并不阻止容器打开任意文件或与网络协议栈交互——这些仍是安全缺陷的利用向量容器根文件系统虽然临时ephemeral但可写攻击者可利用 Linux 文件系统栈中的潜在漏洞Linux 内核仍然直接暴露给容器即使把攻击面压缩到最小该架构仍要求容器通过系统调用直接访问 Linux只要某个安全缺陷落在被放行的系统调用集合内攻击依然可能成功。这些残余风险正是 gVisor 想要解决的核心问题。gVisor 是什么一个会扭曲每一句正常描述的应用程序内核gVisor是一个容器安全解决方案核心目标是让恶意代码极难突破容器边界。它由 Google 于 2018 年 5 月以 Apache 2.0 协议开源用 Go 编写运行于 Linux可与 Docker、Podman、Kubernetes 等主流容器管理软件集成。其本质是一个应用程序内核application kernel实现了 Linux 系统调用接口的绝大部分。gVisor 位于容器与 Linux 内核之间同时扮演两个角色——对容器而言它是内核对 Linux 而言它只是一个普通应用程序。因此容器不再能直接与 Linux 内核打交道攻击面得到大幅缩减。用原文作者的话说gVisor 会扭曲每一句正常描述。比如这句完全正常的句子A process opens a document on the filesystem一个进程在文件系统上打开文档在 gVisor 语境下每个词都被重写on the filesystem在文件系统上不存在的。gVisor 容器运行在空文件系统里opens a document打开文档不行gVisor 容器甚至没有执行open系统调用的权限——而且本来也没有文件可打开A process一个进程有趣的是gVisor 容器连exec系统调用都无法执行。从 Linux 内核视角看gVisor 进程看起来就是一个典型的多线程程序尽管沙箱内正运行着许多相互独立的进程。然而 gVisor 可以毫无障碍地容器化大多数应用——例如 Dangerzone 的容器镜像在集成 gVisor 时完全未做任何改动。它是怎么做到的靠两个核心组件Sentry哨兵运行容器化应用的组件。它拦截应用发出的每一个系统调用并用 Go 重新实现。作为实现的一部分它可能决定向宿主机 Linux 内核发起一个或多个系统调用但自身被严格的 seccomp 过滤器重重限制——这就是为什么open、socket、exec这类系统调用在 Sentry 上不被允许Gofer搬运工运行在容器之外、负责文件系统操作的组件。Sentry 可以向 gofer 发起 I/O 请求gofer独立校验这些请求后代表容器执行 I/O 操作——这就是容器虽然不允许open却仍能读取宿主机文件的原因。这两个组件由名为runsc的容器运行时管理。runsc与其他容器运行时暴露相同的接口因此可以无缝接入 Podman、Docker 或 Kubernetes。在 gVisor 仓库中runsc的入口在 runsc/main.go其注释明确写着Binary runsc implements the OCI runtime interface通过runsc/cli/maincli派发全部子命令——OCIOpen Container Initiative运行时规范兼容是它能够作为 Docker/Podman 即插即用替代品的前提。通过上述架构gVisor 让应用误以为自己在与一个常规 Linux 内核交互。实际上 gVisor 用 Go 重实现了 Linux 提供的大部分基础能力内存管理、调度、系统调用接口、I/O、网络只在确有必要时才向 Linux 内核发起系统调用——比如读取 Dangerzone 要转换的文档时。为什么 gVisor 内核难以被攻破内存安全Linux 的许多安全痼疾源于 C 语言这一内存不安全语言。而 gVisor 是常规 Go 应用继承了 Go 的内存安全特性直接消除了一大类漏洞更小的代码面与传统内核不同gVisor 不必处理硬件设备等事务只实现足以支撑大多数应用工作的 Linux 内核接口子集。部件更少bug 存在的机会也更少。启动期纵深防御除了内核间接层gVisor 还会在启动时通过一批安全措施自我加固其中一些与常规容器类似隔离Isolation运行在独立的一组命名空间用户命名空间、进程命名空间、网络命名空间等中进一步与宿主机隔离文件访问预防运行在自己的根目录中初始对宿主机文件零可见性特权撤销丢弃全部能力capability以最小权限运行系统调用过滤为 gVisor Sentry 专门定制严格的系统调用过滤器。与 Docker/Podman 的默认过滤器不同这是一个极度受限的集合会拦截打开文件、创建网络连接、执行其他进程等基础操作。注意该过滤器的存在并不阻止沙箱内使用这些系统调用——gVisor 内核会在内部拦截并重实现它们无需向 Linux 内核发出真实系统调用Gofer 同样应用上述所有技术尽可能隔离自身。在仓库中Sentry 的 seccomp 过滤器由 pkg/seccomp/seccomp.go 的Program.Install生成并安装它基于给定的系统调用集合生成 BPF 代码违规系统调用将触发RET_KILL_PROCESS直接杀死进程。注释还提示了一个实用排障技巧——怀疑 Sentry 因 seccomp 违规被杀时可在runsc/boot下找debugFilter开关gofer 的对应过滤逻辑则在 runsc/fsgofer/filter/filter.go它会优先加载预编译的 seccomp 程序以加速启动。此外gVisor 还通过 Syzkaller 持续进行模糊测试fuzzing并已在 Google 等大型企业的生产环境中经受过实战检验。代价是什么执行大量系统调用和重 I/O 的应用会有一定性能损失依赖 Linux 内核冷门特性的应用可能无法运行。实践中绝大多数应用不受此影响参见仓库中的兼容性文档 g3doc/user_guide/compatibility.md。集成实战用容器套容器解决跨平台难题gVisor 看起来是 Dangerzone 的理想选择——Dangerzone 是相对简单的应用系统调用不密集且 gVisor 提供了可即插即用的容器运行时。但事情没那么简单Dangerzone 是跨平台应用绝大多数用户在 Windows 和 macOS 上而 gVisor 严格只运行于 Linux。僵局如何破Dangerzone 团队想到了一个典型的手里有锤子看什么都像钉子Maslows hammer的方案用容器解决容器的问题——把 gVisor 容器化让它跑在 Docker Desktop 上。毕竟 Docker Desktop 本身就是在虚拟机里运行 Linux。集成后 Dangerzone 拥有两个职责不同的容器外层 Docker/Podman 容器 可移植层portability负责打包运行 gVisor 所需的配置文件、脚本和程序并打包 gVisor 将要拉起容器所用的镜像内层 gVisor 容器 隔离层isolation唯一职责就是运行 Dangerzone 将文档渲染为像素的实际逻辑。在容器内运行 gVisor 带来了三个已知挑战外层容器的 seccomp 过滤器必须放行ptrace系统调用实测近期 Docker Desktop 版本和 Podman ≥ 4.0 的默认 seccomp 过滤器已允许该系统调用对更老版本Dangerzone 指定了自定义 seccomp 过滤器将其放行。顺带一提gVisor 仓库中 Sentry 平台层如 pkg/sentry/platform/platform.go 定义的Platform抽象正是系统调用拦截与执行上下文管理的核心接口SELinux 兼容性gVisor 在默认设置下无法运行于 SELinux enforcing 模式因此 Dangerzone 将容器标记为container_engine_t标签对应 GitHub issue #880外层容器必须具备SYS_CHROOT能力gVisor 需要它来在开始文档处理前限制自身对文件系统的访问。除此之外外层容器丢弃其余全部能力和特权。加固效果对比0.7.0 相比 0.6.1 改变了什么下表对比了默认 Linux 容器防护、Dangerzone 0.6.1纯容器与 Dangerzone gVisor 0.7.0双层嵌套的安全属性️ 防护项默认容器Dangerzone (0.6.1)Dangerzone gVisor (0.7.0)Linux 内核暴露 暴露不暴露️系统调用过滤器中等 中等严格️能力Capabilities默认 无 无宿主机用户映射 映射不映射文件系统暴露 可写只读网络暴露 禁用✌️双层禁用SELinux是container_t 是container_t 是container_engine_t️硬件虚拟化无 无 无最关键的变化是文档转换进程不再能访问 Linux 内核它只能访问 gVisor 内核Sentry 内攻击者必须先突破 gVisor才能触达此前可直接访问的 Linux 内核。除此之外Dangerzone 自身还对外层与内层容器做了如下加固特权撤销移除内层容器文档转换进程的全部权限与能力将外层容器的能力集最小化到仅剩SYS_CHROOT文件修改预防将内层容器的根文件系统设为只读用户隔离让外层容器运行在不包含 Dangerzone UI 用户的用户命名空间中需要 Podman ≥ 4.1 的 Linux 发行版内核安全设置配置外层容器的系统调用过滤器与 SELinux 标签宿主机访问预防两个容器均不使用任何挂载mount网络访问预防同时禁用两个容器的联网能力。源码侧印证gofer 如何替容器执行文件 I/O容器不能执行open却仍能读取宿主机文件这一机制在仓库中有直接实现证据。gofer 基于 lisafs 协议提供服务见 runsc/fsgofer/lisafs.goconnectionImpl.Mount在收到 Sentry 的挂载请求时通过unix.Open(mountPath, flags, 0)在宿主机侧打开挂载点根目录随后所有文件操作Walk、Open、OpenCreate、Mkdir、Mknod、Symlink等都由运行在容器外的 gofer 进程代为执行并独立校验。这正是Sentry 不允许open但 I/O 请求经 gofer 校验后仍可完成的架构落点。同时gVisor 对外层容器 OCI 规格中的 seccomp 配置也提供了处理入口runsc/boot/seccomp.go 中的buildOCISeccompProgram负责把 OCI 运行时规范中的 seccomp 定义编译为 BPF 程序——这解释了 Dangerzone 为何能通过自定义 seccomp 过滤器放行ptrace只要在容器的 OCI 规格如 Podman 或 Docker 传入的配置中声明即可由 runsc 在启动阶段完成编译与安装。结论Dangerzone 与 gVisor 的集成是一个极佳范例在不要求应用层做任何改动的情况下gVisor 为一个项目增加了一道全新防线。集成后Dangerzone 的设计复杂度有所上升主要是为了迁就其跨平台特性但幅度并不大。对于 Dangezone 这样高度注重安全的项目而言这笔成本完全值得。从更宏观的视角看这个故事展示了容器安全的演进路径先用能力移除、网络禁用、seccomp 过滤、最小 UID 映射把容器的攻击面压到常规极限再通过 gVisor 这类应用内核把容器直接访问 Linux 内核这一最后的暴露面彻底消除——对需要处理不受信任输入的安全敏感应用病毒扫描、文档转换、沙箱执行等来说这条双层嵌套 应用内核的路线具有很强的参考价值。想进一步深入阅读的读者可继续查看仓库内的架构文档 g3doc/architecture_guide/intro_to_gvisor.md安全模型总览、g3doc/architecture_guide/security.md安全细节、g3doc/architecture_guide/platforms.md平台抽象以及本系列博客中的 2024-02-01-seccomp.mdgVisor 自身 seccomp 过滤器的深入剖析。【免费下载链接】gvisorApplication Kernel for Containers项目地址: https://gitcode.com/GitHub_Trending/gv/gvisor创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考