Docs navigation
Docs / OpenSquilla 沙箱技术:从零理解 Linux、macOS、Windows 与 Docker

OpenSquilla 沙箱技术:从零理解 Linux、macOS、Windows 与 Docker

面向第一次接触操作系统安全的读者。本文不讲实现代码,只讲“为什么需要、底层怎样工作、OpenSquilla 实际用了什么、它能防什么和不能防什么”。

项目事实以仓库提交 5778f53c 为准。安全实现会变化,实际运行时应以 opensquilla sandbox status 和 opensquilla doctor 的结果为准。

先记住一句话

OpenSquilla 的沙箱不是一个万能盒子,而是一组互相配合的安全层:

策略层决定“允许做什么”
        ↓
系统沙箱决定“进程实际上碰得到什么”
        ↓
审批系统只为某个明确动作临时开门
        ↓
审计记录最后发生了什么

形象一点:

  • 策略层像保安,先判断请求是否合理。
  • 沙箱后端像房间的墙和门锁,负责真正限制程序。
  • 临时授权像一次性访客证,只对指定门、指定时间有效。
  • Docker 或虚拟机像整栋楼之外的第二道围墙。

Linux、macOS 和 Windows 没有完全相同的“门锁”,所以 OpenSquilla 在三个平台使用了不同技术。


一、开始前必须懂的 7 个词

1. 宿主机

**宿主机(Host)**就是实际运行 OpenSquilla 的电脑。

沙箱中的程序看到的世界可能很小,但文件、CPU、内存和网络最终仍然来自宿主机。

2. 进程

**进程(Process)**就是一个正在运行的程序实例。

打开两个记事本,通常就是两个进程。进程拥有自己的内存、打开的文件和身份信息。

3. 内核

**内核(Kernel)**是操作系统最核心的部分,负责管理:

  • 进程;
  • 内存;
  • 文件系统;
  • 网络;
  • 硬件设备;
  • 用户权限。

普通程序不能直接命令硬盘或网卡,必须请求内核代办。

4. 系统调用

**系统调用(System Call)**是程序向内核提交的底层请求,例如:

  • 打开文件;
  • 创建进程;
  • 建立网络连接;
  • 分配内存;
  • 查看另一个进程。

可以把内核想成政务大厅,系统调用就是程序递交到窗口的申请单。

5. 权限

权限回答的是:

“这个身份能否对这个资源做这个动作?”

例如,某个用户可以读取文件但不能修改;某个进程可以访问普通网络,却不能修改系统路由表。

6. 沙箱

**沙箱(Sandbox)**是给不完全可信的程序划出的受控执行环境。

它不会自动判断程序善恶,只会把边界变成内核能够执行的规则,例如:

  • 哪些文件可见;
  • 哪些目录可写;
  • 能否联网;
  • 能看到哪些进程;
  • 能使用哪些系统调用;
  • 超时后如何清理。

7. 沙箱不等于虚拟机

**虚拟机(Virtual Machine,VM)**模拟一台完整电脑,通常拥有独立内核。

Bubblewrap、Seatbelt、Windows 受限令牌和 Linux 容器通常仍与宿主共享内核。它们启动快、开销小,但隔离边界一般没有独立虚拟机那么厚。


二、OpenSquilla 怎样把一次工具请求送进沙箱

一次工具请求大致经过五步:

  1. 识别动作:它想读取、写入、联网,还是修改宿主系统?
  2. 构造 RunContext:收集工作区、挂载目录、域名授权和临时授权。
  3. 选择运行模式:决定在系统沙箱还是宿主环境执行。
  4. 翻译平台规则:Linux、macOS、Windows 后端把统一策略翻译成各自的系统能力。
  5. 执行和收尾:限制时间与输出,终止残留子进程,返回结果或拒绝原因。

RunContext 是什么?

**RunContext(运行上下文)**可以理解成进程随身携带的“权限背包”,主要装着:

  • Workspace(工作区):任务主要操作的目录。
  • Mount(挂载):将某个宿主目录展示给沙箱。
  • 只读 ro:可以看,不可以改。
  • 读写 rw:可以看,也可以改。
  • Domain Grant(域名授权):允许访问的网络域名。
  • Temporary Grant(临时授权):只供当前动作使用的一次性许可。
  • Fingerprint(动作指纹):把命令、目录、目标文件、域名和会话等组合成摘要。

动作指纹的作用是防止:

用户批准的是动作 A,程序执行时却把目标偷偷换成动作 B。

三种运行模式

模式实际含义
Standard-Sandbox始终在系统沙箱执行;越界需要精确批准;沙箱后端不可用就拒绝。
Managed Execution仍然使用系统沙箱;确定性的低风险规则可以减少重复确认,必要时只为同一动作批准一次精确的宿主重试。
Full Host Access不使用 OpenSquilla 内部系统沙箱,直接在当前宿主环境执行。

这里最容易误解的是:

Managed Execution 不是“关闭沙箱”,Full Host Access 才改变执行边界。

每次动作还可能被评为 Standard、Strict 或 Locked。运行模式决定“在哪里跑”,安全等级决定“门锁多紧”。例如 Locked 可以把工作区改为只读、取消额外挂载并断网。


三、Linux:Bubblewrap 给进程换一个“世界观”

Bubblewrap 是什么?

Bubblewrap,简称 bwrap,是 Linux 沙箱组装工具。

它本身不是安全裁判,不负责判断某个命令是否应该获准。它的工作是把 OpenSquilla 已经决定好的策略组装成 Linux 内核隔离环境。

可以把它理解成:

OpenSquilla 先画好房间平面图,Bubblewrap 再用 Linux 内核提供的墙、门和窗把房间搭出来。

它主要组合:

  • Namespace:隔离进程看到的系统视图;
  • Mount:重新组织文件系统;
  • Capability:拿走系统特权;
  • seccomp:过滤高风险系统调用;
  • no_new_privs:封死通过特殊程序重新提权的道路;
  • 进程和资源清理机制。

Namespace:每组进程拥有自己的“视角”

**Namespace(命名空间)**并不复制整台电脑,而是让同一台电脑上的不同进程看到不同的系统视图。

就像同一座商场给不同员工发不同地图:建筑还是同一座,但每个人能看到的房间不同。

OpenSquilla 的 Bubblewrap 路径使用以下命名空间。

1. User Namespace:隔离用户身份

**User Namespace(用户命名空间)**可以重新映射用户编号和权限。

沙箱中的进程可能觉得自己拥有一个内部身份,但这不等于获得宿主机的 root。它更像“在模拟教室里当班长”,不代表能管理整所学校。

它让其他命名空间能够在不直接授予宿主 root 的情况下建立。

2. PID Namespace:隔离进程树

**PID(Process ID)**是进程编号。

PID Namespace 让沙箱拥有独立的进程编号视图:

  • 沙箱内只看到自己的进程树;
  • 宿主机仍然能看到沙箱进程;
  • 沙箱程序通常看不到宿主机其他进程。

这像单向玻璃:管理员能看到房间里的人,房间里的人看不到整栋楼。

3. UTS Namespace:隔离“这台系统叫什么”

UTS 是 UNIX Time-Sharing System 的历史缩写。今天真正需要记住的是:

UTS Namespace 隔离主机名等系统身份标识。

例如:

  • 宿主机认为自己叫 my-computer;
  • 沙箱 A 认为自己叫 sandbox-a;
  • 沙箱 B 认为自己叫 sandbox-b。

它主要隔离:

  • Hostname(主机名):系统显示的机器名称;
  • NIS Domain Name:早期 UNIX 网络身份系统使用的域名,现代普通应用较少使用。

UTS 只是“换门牌”,不会隔离文件、网络或进程。也不要把 UTS 与 UTC(协调世界时)混淆;这里与时间无关。

4. IPC Namespace:隔离进程通信登记表

IPC 是 Inter-Process Communication,进程间通信。

Linux 提供一些由内核管理的进程通信资源:

  • 消息队列:像内核中的邮箱;
  • 信号量:像协调多人使用资源的门锁;
  • 共享内存:像多个进程共用的一块白板。

Linux 内核会为这些资源维护登记表。创建 IPC Namespace 后,每个命名空间拥有自己的登记表:

宿主 IPC 表:消息队列 100 → 宿主邮箱
沙箱 A IPC 表:消息队列 100 → A 的邮箱
沙箱 B IPC 表:消息队列 100 → B 的邮箱

编号虽然相同,但实际对象不同,就像不同城市都可以存在“人民路 10 号”。

当进程请求打开某个 IPC 对象时,内核先检查它属于哪个 IPC Namespace,然后只在对应登记表里查找。因此不同沙箱通常无法看见彼此的 System V IPC 对象和 POSIX 消息队列。

但 IPC Namespace 不隔离所有通信:

  • 网络连接由 Network Namespace 负责;
  • 普通文件由 Mount Namespace 和文件权限负责;
  • Unix Socket 通常还依赖文件系统或网络隔离;
  • POSIX 共享内存通常表现为 /dev/shm 下的对象,还需要 Mount Namespace 配合。

所以它准确的作用是:

隔离特定的内核 IPC 资源集合,而不是禁止一切进程交流。

5. Mount Namespace:重新搭建文件世界

**Mount Namespace(挂载命名空间)**决定进程看到怎样的文件系统树。

它不是复制所有文件,而是重新安排“哪些宿主目录从沙箱哪个位置出现,以及以只读还是可写方式出现”。

OpenSquilla 当前的典型做法包括:

  • 默认把宿主根目录以只读方式展示;
  • 工作区和明确授权目录按 ro 或 rw 展示;
  • 为沙箱重新提供 /proc,使其匹配新的进程视图;
  • 提供受控的 /dev,避免直接暴露全部设备;
  • 用空文件或空目录遮住被拒绝路径,像在门前砌一堵墙;
  • 某些策略下使用沙箱内部临时文件系统,而不是宿主临时目录。

需要特别注意:

“宿主根目录只读”表示不能随便修改,不表示所有秘密都不可见。

如果要防止读取 SSH 私钥、云凭据等敏感文件,必须配置明确的拒绝读取路径。

6. Network Namespace:独立网络栈

**Network Namespace(网络命名空间)**为沙箱提供独立的:

  • 网络接口;
  • IP 地址;
  • 路由表;
  • 端口空间;
  • 防火墙视图。

就像给房间安装一套独立电话系统。房间里即使有电话,也不一定能直接接入外线。

OpenSquilla 在非 Host 网络模式下创建独立 Network Namespace。需要联网时,沙箱不能直接使用宿主网络,而是通过受管代理转发。

7. cgroup Namespace:隐藏宿主资源管理树

先理解 cgroup(Control Group,控制组)。

cgroup 是 Linux 用来分组统计和限制资源的机制,可以限制 CPU、内存、进程数量和 I/O。它像公司的部门预算表:

整台电脑
├─ 系统服务
├─ 普通用户
└─ Docker
   ├─ 容器 A
   └─ 容器 B

宿主机可能知道某个沙箱真正位于:

/services/opensquilla/task-123

创建 cgroup Namespace 后,沙箱会把自己当前所在的 cgroup 当成内部看到的根 /:

宿主看到:/services/opensquilla/task-123
沙箱看到:/

这相当于重新定义坐标原点,使沙箱看不到上方和旁边的宿主管理结构。

但是:

cgroup Namespace 只隐藏或重映射“视图”,不会自动设置 CPU、内存或进程限额。

真正限制资源的是 cgroup 控制器本身。宿主施加的上级限制即使在沙箱中不可见,仍然有效。

Capability:把 root 的万能钥匙拆成小钥匙

传统 root 像持有一把万能钥匙。Linux 将大量系统特权拆成了 Capability(能力):

  • CAP_SYS_ADMIN:挂载等大量系统管理操作;
  • CAP_NET_ADMIN:修改网络配置;
  • CAP_NET_RAW:创建原始网络数据包;
  • CAP_SYS_PTRACE:检查或控制其他进程;
  • CAP_DAC_OVERRIDE:绕过普通文件权限。

OpenSquilla 使用 cap-drop ALL,意思是:

把沙箱进程持有的全部 Capability 都拿走。

“拿走”不是把权限交给另一个程序,而是从沙箱进程的能力集合中删除。

即使进程在自己的环境里显示为 root,没有对应 Capability 时,许多系统级操作仍会被内核拒绝。

不过 cap-drop ALL 不是“什么都不能做”。程序依然可以计算、读取获准文件、写入获准目录,并在其他安全层允许时联网。被删除的是特殊系统权力,不是普通使用权限。

seccomp:站在内核窗口前的门卫

**seccomp(Secure Computing)**是 Linux 系统调用过滤机制。

每次程序向内核提交系统调用,seccomp 都可以先检查:

程序提出请求
      ↓
seccomp 检查系统调用编号和部分参数
      ↓
允许:交给内核执行
拒绝:返回错误,或者按策略终止程序

在 OpenSquilla 当前的非 Host 网络策略中,这一层会禁止一些高风险入口:

  • ptrace:可暂停、查看甚至修改其他进程;
  • 跨进程内存读取和写入:可能窃取密码、令牌或注入数据;
  • io_uring:高性能异步 I/O 接口,本身不是恶意功能,但接口复杂、触达面广,禁用后可以减少内核攻击面。

攻击面就是攻击者可以尝试利用的入口总数。关掉程序不需要的入口,等于减少可被撬动的门。

网络受限模式还会过滤 Socket 操作。

**Socket(套接字)**是程序进行网络或本机通信的端点,像电话插座。过滤器可以限制:

  • 能创建哪些协议族的 Socket;
  • 能否监听、连接、发送或接收;
  • 是否允许某些本机通信方式。

但 seccomp 通常不能完整判断“允许访问哪个网站”。网站、IP 和端口级规则仍需要 Network Namespace、代理或防火墙。

当前必须知道两个边界:

  1. 这套 seccomp 辅助层只在非 Host 网络策略中启用。
  2. 当前系统调用编号按 x86_64/amd64 架构编写,因此在 ARM64 等架构上不会启用这一层。

x86_64 和 amd64 基本是同一种常见的 64 位 Intel/AMD 指令架构。不同 CPU 架构的系统调用编号不同,不能直接混用过滤规则。

no_new_privs:把权限提升变成单向棘轮

Linux 可执行文件可能带有特殊权限:

  • setuid:执行后临时变成文件所有者;
  • setgid:执行后临时获得文件所属组身份;
  • File Capability:执行后获得某一项系统能力。

no_new_privs 可以理解成一个只能锁上、不能解开的权限棘轮:

当前进程及其后代可以保留已有权限,但不能因为执行某个特殊文件而获得更高权限。

在 OpenSquilla 安装 Linux seccomp 的路径中,执行包装器会先让内核设置这个标记,再安装过滤器。

设置后,内核在执行新程序时会忽略可能带来额外权限的 setuid、setgid 和文件 Capability。这个标记:

  • 会被子进程继承;
  • 执行新程序后仍保留;
  • 一旦设置,不能取消。

它和 cap-drop ALL 的区别是:

cap-drop ALL:你现在没有特权钥匙
no_new_privs:你以后也不能靠执行特殊程序领取新钥匙

它不会自动删除设置前已经拥有的权限,也不会禁止所有系统调用。因此必须和 Capability、Namespace、seccomp、文件权限一起使用。

环境变量、进程清理和资源限制

环境变量是父进程传给子进程的键值配置,其中可能包含代理地址、令牌或密钥。

OpenSquilla 会先清空继承环境,再补回允许的变量,避免秘密被无意传入沙箱。

进程方面:

  • 父进程死亡时,Bubblewrap 负责结束沙箱;
  • 超时或取消时,OpenSquilla 清理整棵子进程树;
  • 输出有大小上限,避免无限输出吃光内存。

资源方面当前主要是:

  • 墙钟超时,也就是现实世界经过了多长时间;
  • 输出大小限制;
  • 尽力设置 CPU 时间和进程数量的 rlimit。

rlimit 是单个进程及其后代能使用某类资源的传统上限。它不等同于完整 cgroup 控制。

当前 memory_mb 没有真正落实成 Linux 内存硬上限;创建 cgroup Namespace 也不会补上这个限制。

Bubblewrap 实践中的前提和局限

系统需要:

  • 已安装 bwrap;
  • Linux 内核支持 User Namespace;
  • Bubblewrap 支持 OpenSquilla 所需选项;
  • 不能是缺少相应内核能力的 WSL1。

Standard-Sandbox 下后端不可用会失败关闭:

安全组件失效时拒绝执行,而不是偷偷退回宿主。

它的主要局限:

  • 仍与宿主共享 Linux 内核;
  • 默认更偏向防误写,不是自动隐藏所有秘密;
  • 路径检查到真正挂载之间仍可能存在小范围 TOCTOU 竞态。

TOCTOU 是“检查时”和“使用时”之间的状态变化。例如检查路径时它指向安全目录,真正打开前符号链接却被替换到了敏感目录。


四、macOS:Seatbelt 让每个动作都刷门禁

Seatbelt 是什么?

Seatbelt 是 macOS 的系统沙箱机制,属于 MAC(Mandatory Access Control,强制访问控制)。

普通文件权限像“房主决定谁能进”;MAC 像“大楼管理处额外规定,即使房主同意也必须通过整栋楼的安全规则”。

Seatbelt 与 Linux Namespace 的思路不同:

  • Bubblewrap 是给进程重新搭建一套视图;
  • Seatbelt 仍让进程待在原来的系统视图中,但每个敏感动作都由内核检查规则。

OpenSquilla 怎样使用 Seatbelt?

实际流程是:

  1. 根据本次请求生成 SBPL 规则;
  2. 从 deny default 开始;
  3. 加入必要的文件、进程、网络和系统服务许可;
  4. 使用系统的 sandbox-exec 启动目标程序;
  5. 执行结束后删除临时规则文件。

**SBPL(Sandbox Profile Language)**是描述 Seatbelt 规则的语言。

deny default 表示“默认拒绝”,只有明确列出的动作才放行。它像活动现场的白名单:不在名单上的人一律不能进入。

Seatbelt 具体限制什么?

当前规则会按策略允许:

  • 启动子进程和 fork;
  • 读取必要的系统文件;
  • 读取宿主文件系统的基线视图;
  • 写入明确工作区;
  • 使用 /dev/null、伪终端和部分共享内存;
  • 访问必要的 Mach Service;
  • 断网、使用宿主网络,或只连接本地受管代理。

Mach Service 是 macOS 程序访问系统服务的一类本机通信入口,可以理解成操作系统内部的服务窗口。

子进程会继承同一份 Seatbelt 限制,不是启动子进程后就自动逃出沙箱。

OpenSquilla 还会把工作区中的 .git、.codex 和 .agents 等控制目录重新保护为只读,避免任务顺手修改关键元数据。

Seatbelt 与 Bubblewrap 的关键差别

Seatbelt 没有为 OpenSquilla 提供 Linux 那套 User、PID、Mount、Network Namespace:

  • 路径还是原来的宿主路径;
  • 进程仍是同一个 macOS 用户;
  • 文件系统没有重新搭一棵独立目录树;
  • 安全依赖每次操作时的规则检查。

形象理解:

Bubblewrap 是重新搭一间房;Seatbelt 是仍在原房间,但开门、写文件、联网和访问系统服务都必须刷门禁。

当前边界

  • 没有真正应用 CPU、内存和进程数量硬限额;
  • 主要资源保护是墙钟超时、输出限制和进程组清理;
  • 某些按通配符拒绝读取的策略无法安全表达,此时应明确失败;
  • 当前仓库没有完整的动态 Sandbox Extension 签发和消费流程。

Sandbox Extension 可以理解为系统签发的临时放行券。本文所述版本不能把它当作通用动态授权机制。


五、Windows:换身份、锁文件、封网络、拴进程

Windows 没有一个与 Bubblewrap 完全对应的单工具。OpenSquilla 使用多种机制组合成后端。

最简单的整体比喻是:

给程序换成专用员工身份,没收万能钥匙,只发几张指定房门的票,封住不该走的道路,再用一根绳索拴住整组子进程。

第一次准备隔离环境

首次设置通常需要 UAC 管理员确认。

**UAC(User Account Control,用户账户控制)**就是 Windows 弹出的管理员确认窗口,用来阻止普通程序静默执行系统级修改。

OpenSquilla 会:

  1. 创建专用本地用户 OpenSquillaSandbox;
  2. 生成随机密码;
  3. 使用 DPAPI 加密保存敏感数据;
  4. 根据专用用户的 SID 配置网络规则。

**DPAPI(Data Protection API)**是 Windows 提供的数据加密保护接口。

**SID(Security Identifier,安全标识符)**是 Windows 给用户、组或其他权限主体分配的唯一身份证号。系统真正认的是 SID,不只是显示出来的用户名。

ACL:每个文件门口的访客名单

**ACL(Access Control List,访问控制列表)**记录:

  • 哪个 SID 可以读取;
  • 哪个 SID 可以写入;
  • 哪个 SID 被明确拒绝。

每次执行前,OpenSquilla 按本次授权给工作区和运行目录设置只读或读写访问,并对敏感路径加入拒绝规则。

Windows 没有像 Mount Namespace 那样为这里重新搭一套独立文件树,所以 ACL 直接作用于宿主路径。也正因如此,OpenSquilla 必须维护 ACL 状态、回滚和异常修复。

Access Token 与 Restricted Token

Windows 每个进程都带有 Access Token(访问令牌)。它像员工工牌,写着:

  • 你是谁;
  • 属于哪些组;
  • 拥有哪些系统特权;
  • 访问资源时使用哪些限制身份。

**Restricted Token(受限访问令牌)**是在普通工牌上再盖一个“仅限这些区域”的红章。

OpenSquilla 使用受限令牌:

  • 关闭最大权限;
  • 启用受限用户模式;
  • 加入写入限制;
  • 只携带本次真正需要的精确身份票据。

Capability SID 可以理解成“只开某类指定门的票”。它不是 Linux Capability,虽然名字相似;两者属于不同操作系统的安全模型。

Job Object:拴住整组进程

**Job Object(作业对象)**是 Windows 管理一组相关进程的机制。

OpenSquilla 会先暂停创建目标进程,把它加入 Job Object,再恢复执行。这样可以保证程序来不及先创建一个未受管理的子进程。

它像进门前先系好安全绳:

  • 主任务结束时,可以终止整棵子进程树;
  • 超时时,可以统一清理残留进程;
  • 不让后台子进程轻易变成孤儿继续运行。

当前 Job Object 主要用于“关闭时杀死进程树”,没有配置 CPU、内存或进程数量硬限额。

Firewall 与 WFP:网络道路关卡

Windows Firewall 是常见的 Windows 防火墙。

**WFP(Windows Filtering Platform,Windows 过滤平台)**是更底层的内核网络过滤框架。

OpenSquilla 按专用用户 SID 设置规则,限制:

  • 外网;
  • DNS;
  • ICMP;
  • SMB;
  • 未授权的回环端口;
  • 绕过受管代理的连接。

ICMP 是网络诊断等用途的控制协议,ping 就常用它。

SMB 是 Windows 常见的文件共享协议。

回环地址指本机自身,例如 127.0.0.1。它不是天然安全,因为宿主机本地可能运行数据库、管理面板或其他敏感服务。

Windows 当前没有使用什么?

必须避免几个常见误解:

  • 没有使用 AppContainer;
  • 没有使用 Low Integrity;
  • 没有使用 Windows Sandbox;
  • 没有使用 Hyper-V 虚拟机。

AppContainer 是 Windows 为应用提供的一类更强隔离身份。

**Integrity Level(完整性级别)**是 Windows 用来限制低信任进程影响高信任对象的等级机制。LUA_TOKEN 指受限用户令牌,不等于 Low Integrity。

因此当前 Windows 后端的核心是:

专用用户 + ACL + Restricted Token + Job Object + Firewall/WFP。

它的主要资源保护仍是墙钟超时和进程树清理,不应误认为已经具备完整 CPU、内存、PID 硬限额。


六、三平台共同的网络防线

为什么只设置代理环境变量不够?

HTTP_PROXY 等环境变量只是告诉守规矩的程序“请走这个代理”。

恶意程序可以选择忽略环境变量并直接连接网络。因此真正的限制还需要系统层边界:

  • Linux:Network Namespace 和 seccomp;
  • macOS:Seatbelt 网络规则;
  • Windows:Firewall 和 WFP。

受管代理检查什么?

代理不仅看用户输入的域名,还会检查:

  • 域名是否获得授权;
  • HTTP Host 与真正目标是否一致;
  • HTTPS CONNECT 的目标是否一致;
  • DNS 最终解析出的 IP;
  • 是否指向回环、私网、链路本地或云元数据地址;
  • 是否发生 DNS Rebinding。

DNS 是把域名转换成 IP 地址的系统。

**DNS Rebinding(DNS 重绑定)**是一种绕过方法:同一个域名第一次解析到正常公网地址,之后却改为宿主内网地址。只检查域名、不检查最终连接 IP 就可能被骗。

云元数据地址是云服务器向本机提供实例身份和临时凭据的特殊地址,泄露后可能产生严重后果。

Linux 受管网络中还会使用 UDS 桥接。

**UDS(Unix Domain Socket)**是同一台机器上进程通信使用的本机管道,它看起来像文件路径,但传递的是进程间数据。

此外,OpenSquilla Gateway 自己进程内发起的 HTTP 请求不一定创建沙箱子进程,因此还需要进程内网络守卫,不能只依赖外部系统沙箱。


七、Docker 从零解释:它怎样做沙箱?

Image 与 Container

**Image(镜像)**是只读运行模板,里面包含程序、依赖和基础文件系统。

**Container(容器)**是镜像启动后的进程及其隔离环境。

可以把镜像理解成模具,容器是用模具做出的具体成品。多个容器可以来自同一镜像,但拥有不同的运行状态。

Overlay Filesystem

**Overlay Filesystem(叠加文件系统)**把多个只读镜像层和一个临时可写层叠在一起。

像在透明胶片上逐层绘画:

  • 底层是基础系统;
  • 上层是应用和依赖;
  • 最上面是当前容器产生的修改。

删除容器时,最上面的临时层通常也随之消失。

Docker 的主要隔离层

Docker 在 Linux 上主要组合:

  • Namespace:隔离进程、挂载、网络、主机名等视图;
  • cgroup:统计和限制 CPU、内存、I/O、进程数量;
  • Capability:拆分并减少 root 特权;
  • seccomp:限制系统调用;
  • LSM:应用额外的内核安全策略;
  • 独立文件系统层;
  • 虚拟网络和端口映射。

**LSM(Linux Security Module)**是 Linux 安全模块框架,AppArmor、SELinux 等安全系统通过它执行额外规则。

Volume:主动打开的文件通道

**Volume(卷)**是容器外的持久存储。

它像容器房间外的储物柜:

  • 容器重建后数据仍可保留;
  • 但每一个宿主目录挂载也是主动打穿文件隔离的一扇门。

把宿主根目录或 Docker Socket 挂进容器尤其危险。

Docker Socket 是控制 Docker 守护进程的接口。能控制它的进程通常可以创建高权限容器、挂载宿主目录,因此在很多环境中接近宿主 root 权限。

Docker 网络

Docker 通常给容器创建独立 Network Namespace,并通过虚拟网桥连接宿主网络。

端口映射决定外部怎样访问容器服务。例如只映射到宿主回环地址,就只有本机可以直接访问;映射到所有网卡,则局域网甚至更外部的网络可能访问。

Docker 为什么不是虚拟机?

Linux 容器通常共享宿主 Linux 内核。它没有为每个容器启动一套完整内核,因此:

  • 启动更快;
  • 占用资源更少;
  • 但内核漏洞可能影响隔离边界。

在 macOS 和 Windows 上运行 Linux 容器时,Docker Desktop 通常先启动一个 Linux 虚拟机,再在虚拟机中运行容器。


八、OpenSquilla 当前怎样使用 Docker?

当前 Docker 的用途是长期运行整个 OpenSquilla Gateway,它是服务的外层容器,不是第四个“单次命令沙箱后端”。

当前已有的安全默认值:

  • 最终进程以非 root 用户、UID 10001 运行;
  • 状态只持久化到 /var/lib/opensquilla;
  • 默认端口只发布到宿主 127.0.0.1;
  • 没有挂载宿主根目录、设备或 Docker Socket;
  • 配置了健康检查和自动重启。

当前 Compose 没有显式配置:

  • 只读根文件系统;
  • cap_drop: ALL;
  • no-new-privileges;
  • 自定义 seccomp;
  • AppArmor 或 SELinux 策略;
  • CPU、内存、PID 限额;
  • 出站网络白名单。

因此它主要继承 Docker Engine 的默认安全策略,不能称为“已经强化的容器”。

另一个非常关键的事实:

当前 Docker 镜像没有安装 Bubblewrap,也不存在 DockerBackend。

所以 Docker 部署是外层边界,不能假设容器内部执行每条工具命令时又自动套了一层 Bubblewrap。

如果在容器内选择 Full Host Access,这里的“Host”首先指容器自己的环境。程序可以完全控制容器、状态卷和容器网络,但不会自动看到物理宿主。只有额外挂入宿主目录、设备或 Docker Socket 时,风险才会明显穿透到物理宿主。


九、四种方案放在一起看

方案核心思路文件控制网络控制进程控制主要边界
Linux / Bubblewrap给进程重建系统视图Mount Namespace 与只读/读写挂载Network Namespace、代理、seccompPID Namespace、父进程联动仍共享 Linux 内核
macOS / Seatbelt每个敏感动作都刷门禁Seatbelt 路径规则Seatbelt 网络规则与代理规则继承、进程组清理没有 Linux 式独立视图
Windows身份、文件、网络和进程组合限制ACLFirewall 与 WFPRestricted Token、Job ObjectACL 直接作用于宿主路径
Docker为整个服务提供容器边界镜像层、可写层、Volume独立网络、网桥、端口映射Namespace、cgroupLinux 上仍共享宿主内核

一句话记忆:

  • Linux:隔离视图;
  • macOS:检查动作;
  • Windows:限制身份和权限;
  • Docker:给整个服务套外层容器。

十、实践中怎样安全使用

1. 先看真实状态

不要根据操作系统名称猜测沙箱已经工作。

  • opensquilla sandbox status:查看当前运行模式和后端状态;
  • opensquilla doctor:检查依赖、配置和平台能力。

2. 根据风险选模式

  • 日常不确定任务优先 Standard-Sandbox;
  • 希望减少重复确认,但仍要系统隔离时使用 Managed Execution;
  • 只有完全信任任务并明确接受宿主风险时才使用 Full Host Access。

全局模式改变后应重启 Gateway,并重新检查状态。

3. 授权要小而精确

推荐顺序:

  1. 能只读就不要给读写;
  2. 能只批准一次就不要永久批准;
  3. 只给具体目录,不给整个用户目录;
  4. 只给必要域名,不开放完整网络;
  5. 批准前检查目标、工作目录和动作指纹是否一致。

4. 不要把“不可写”误认为“不可读”

默认只读挂载主要防止误修改。若任务不能接触:

  • SSH 私钥;
  • 云服务凭据;
  • 浏览器配置;
  • 密码数据库;
  • 其他项目源码;

必须增加明确的拒绝读取规则,或者使用更小的外层容器/虚拟机。

5. Docker 部署守住四条底线

  • 默认保持端口只绑定 127.0.0.1;
  • 开放到局域网前先启用 Token 认证;
  • 不要挂载 Docker Socket;
  • 不要为了方便把宿主根目录整体挂入容器。

高风险或多租户场景,应考虑在强化容器或虚拟机中运行,再叠加 OpenSquilla 自身的审批和路径策略。


十一、最后建立正确的安全观

没有任何单层机制是万能的:

  • Namespace 负责“看见什么”,不自动限制所有资源;
  • cgroup 负责“能用多少资源”,cgroup Namespace 只隐藏视图;
  • Capability 负责“有没有特殊钥匙”;
  • no_new_privs 负责“以后能不能靠执行特殊程序拿到新钥匙”;
  • seccomp 负责“哪些内核入口可以使用”;
  • 文件权限负责“能否读写具体对象”;
  • 网络代理和防火墙负责“连接能去哪里”;
  • Docker 或 VM 提供更外层的边界。

真正可靠的沙箱依靠 纵深防御(Defense in Depth):

即使一道门没有挡住,后面仍然有其他互不相同的门。

最终可以把 OpenSquilla 的安全模型记成一句话:

策略决定该不该做,平台沙箱决定实际上能不能做,审批只为精确动作临时开门,Docker 或虚拟机负责把最坏结果继续关在更外层。

Edit this page on GitHub OpenSquilla docs · synced from main