Greg Kurtzer 的 Ginux 转型为用户空间内核
Greg Kurtzer's Ginux is now a userspace kernel
Greg Kurtzer 的 Ginux 已从独立 Rust 内核转型为可配置的用户空间内核,作为普通 Linux 进程运行,在受管工作负载与宿主内核之间建立显式、版本化的契约。
Ginux 是一个可配置的用户空间内核,用于隔离的 Linux 兼容实例。 它以普通 Linux 进程的形式运行,并在受管理工作负载与宿主内核之间建立一份显式、带版本号的契约。
重要
Ginux 最初是一个独立的 Rust 内核,直接通过 UEFI 启动,并运行 Rocky Linux 10 用户空间。该研究阶段已经完成。其最终源码永久保存在 bootable-kernel 分支和 bootable-kernel-final 标签上。当前开发工作已在此处作为用户空间内核继续推进。
项目为何改变方向
独立内核实验证明,由经验丰富的人类通过架构、门禁和可执行测试来指导 AI 编码代理,可以构建出一个能够运行有意义的 Linux 用户空间的新内核。它也让这一结果的局限性变得显而易见。
Linux 远不止是源代码。它是一个稳定的接口、一套开发流程、一个全球社区、厂商与硬件的一致性,以及数十年的兼容性与运维经验。重新实现内核机制并不能复制这一生态系统,而与 Linux 竞争也从来不是目标。
更有价值的结果是实验所揭示的边界。Linux 必须保留宽泛的用户空间 ABI;正如 Linus Torvalds 所说,“而我们绝不破坏用户空间。” 内核回归文档记录了这一规则。Ginux 可以位于 Linux 之上,并有意为每个受管实例提供更小的契约。在受管系统调用对宿主产生效果之前,Ginux 决定是模拟它、执行经过验证的受控操作,还是拒绝它。
直白地说:Linux 不得破坏用户空间;Ginux 则被设计为有意且选择性地打破完整的 Linux ABI 保证。 实例只获得其已批准工作负载所需的能力和接口行为。每一处偏离都应当是显式、带版本号、可审查且经过测试的,而不是意外的不兼容。
这一转向也是开发方法的结果。AI 让构建一次性但可执行的原型代码、测试架构构想、从运行中的系统学习,并将有价值的部分带入更好的设计变得切实可行。原型是证据,而不是把每个实验都变成产品的义务。
Ginux 现在是什么
Host Linux kernel
`- host systemd (real PID 1)
`- ginux (ordinary trusted host process)
`- instance contract
|- virtual processes and identities
|- virtual descriptors and kernel views
|- assigned devices and resources
`- managed Rocky Linux userspace
`- instance systemd (virtual PID 1)
Ginux 监管原生 x86-64 程序,并在受管系统调用对宿主产生效果之前将其拦截。其核心调度器解析实例契约,并选择三种结果之一:
- 模拟:完全由 Ginux 拥有的状态来应答。
- 中介:验证并转换请求,然后执行一项范围狭窄的、由 Ginux 拥有的宿主操作。
- 拒绝:返回配置好的 Linux 兼容错误,或终止任务。
当前实现包括:
- 一个以安全为重点的 x86-64 解释器和一个均衡的原生 ptrace 后端;
- 严格、带版本号的实例配置与生命周期;
- 一个以摘要固定的 OCI 根、不可变文件系统索引和有界 overlay;
- Ginux 拥有的进程、凭证、信号、同步、定时器和事件模型;
- 具有显式分配、所有权和撤销的虚拟设备;以及
- 有界的虚拟
/proc、/sys、cgroup v2、挂载、命名空间、机器和启动身份视图; - 一个仍通过系统调用中介的宿主共享网络后端;以及
- 一个固定的 Rocky Linux 10 systemd 基线,可达到
multi-user.target, 完成受管控制台登录认证,通过 Rocky 已启用的 仓库进行更新,并干净地关闭实例。
这是一个具名的兼容性基线,并非声称完全兼容 Linux 或
systemd。其确切的镜像、就绪标记、测试、证据和
限制记录在
hdae/gates-todo/9-systemd-baseline.md 中。
Agent 隔离研究
Ginux 也正在被测试作为 AI agent 及其他可执行命令、修改文件、打开网络 连接并启动额外进程的自动化软件的隔离边界。操作者无需 将主机的完整 Linux ABI 交给 agent,而是可以将其进程树放入 一个实例中,该实例的内核契约仅暴露任务所需的操作和虚拟资源。
研究目标是构建一个可审查的边界,涵盖系统调用、ioctl、
文件系统、设备、网络、进程权限以及虚拟 /proc 和
/sys 状态。例如,契约可以允许 agent 读取时间、检查
自身进程并修改一个指定的工作区,同时拒绝时钟
更改、主机文件系统访问、未经批准的网络监听器以及无关
设备。随后,Ginux 可以审计 agent 及其子进程运行期间
做出的契约决策。
这是一个活跃的验证目标,并非声称 Ginux 目前是 生产级 agent 沙箱。契约清单、可配置策略、 对抗性测试、性能成本和逃逸边界均通过 HDAE 门控进行跟踪,并且必须在做出更强的隔离声明之前得到证明。
与其他系统的关系
用户空间内核这一理念是既有的研究和工程领域。 Ginux 探索了该设计空间中的一个特定点:
| 方法 | 主要边界 | 侧重点差异 |
|---|---|---|
| Linux seccomp | 过滤主机系统调用 | 限制现有 ABI,而非定义完整的虚拟契约 |
| User-Mode Linux | 将 Linux 作为主机进程运行 | 在虚拟硬件上复用完整的 Linux 内核 |
| gVisor | 在用户空间实现应用内核 | 密切相关的模型,但兼容性和性能目标不同 |
| Gramine | 将应用程序与库操作系统打包 | 使用清单和库操作系统边界 |
| Proxos | 在私有和通用操作系统之间路由调用 | 使信任放置可跨系统调用接口配置 |
| Ginux | 为每个实例提供版本化的内核契约 | 将声明的契约、虚拟状态、设备、资源和生命周期视为一个可审查的边界 |
Ginux 不承诺完整的 Linux ABI。兼容性声明仅适用于 具名契约、工作负载和可执行证据。
开发与测试
参考开发环境为 macOS arm64。QEMU TCG 启动一个固定的 Rocky Linux 10 x86-64 主机,Ginux 在该主机上作为普通的非特权 进程运行。主机的 systemd 仍为真实的 PID 1。
检查前置条件:
make doctor
通过显式网络操作准备固定资产:
make host-image-fetch make image-helper-fetch make instance-image-fetch make systemd-image-fetch
在缓存填充后离线构建并运行确定性测试套件:
make build
make check
make test直接检查最新完成的能力:
make test-systemd # cached image, external networking disabled make bench-prepare # one-time networked setup for the pinned benchmark image make bench # matched native/Ginux matrix plus mandatory budget checks make bench-modes-prepare # one-time setup for the three-way dispatch comparison make bench-modes # native/selective/paranoid tables and graphs make demo-gate-15 # bounded reproducible three-way comparison make test-systemd-live # shared network plus real upstream dnf -y update make host-run # general interactive Rocky host with Ginux installed make host-reset # discard that host's cache, instances, and writable state make demo-gate-9 # Gate 9 checkpoint through the same host workflow
实例配方在 [contract.syscalls] 下选择系统调用控制:
[contract.syscalls] control = "selective" # default; use "paranoid" for all-syscall mediation default = "deny" allow = ["*"] deny = []
selective 是默认模式,仅当实例契约允许该操作且 Ginux 的版本化目录独立将其归类为直接安全时,才允许直接宿主执行。拒绝、虚拟状态、检查、翻译、模拟、指针或描述符验证以及修改后的结果在两种模式下仍由 Ginux 调度。paranoid 是显式选择加入,会在完成前将每个系统调用发送到 Ginux 用户空间调度器。
make bench-compare 对照 4% 原生预算检查每一行,并且在 benchmarks/gate11/ 下发布可接受的基线后,也会对照该 Ginux 基线进行检查。环境不匹配与性能回归分开报告。make demo-gate-11 运行有界的人工可检查比较,并在强制行未达到预算时返回非零值。
Gate 15 提供了跨原生 Rocky、选择性 Ginux 和偏执 Ginux 的独立表征工作流。原生是无中介对照,不是 Ginux 实例模式。benchmarks/gate15/ 下签入的模板固定了完整的测试协议,可以复制而无需编辑脚本。make bench-modes 在 build/gate15/ 下生成配对的原始样本、元数据、Markdown 表格和 SVG 图。有关确切协议、自定义工作流、输出模式和解释限制,请参阅 可复现的性能测量。
make host-run 启动 QEMU Rocky 主机并让您登录其普通 shell。Ginux 安装在 PATH 中。首次 create 或 import 会下载并验证固定的 Rocky OCI 镜像,并实时显示每层进度;后续操作复用缓存。可写的 Rocky 主机覆盖层在 make host-run、普通清理、构建和测试之间持久存在,因此缓存的镜像、实例定义、根和停止状态仍然可用。make host-reset 是显式的破坏性操作,会停止主机并移除该覆盖层;下一次 make host-run 从固定的干净 Rocky 镜像开始。完整的首次会话是:
ginux create rocky ginux list ginux status rocky ginux start rocky --attach
create 从文档化的默认配方开始。使用 ginux import NAME FILE 从自包含配方创建或原子替换实例,使用 ginux export NAME 打印该配方,使用 ginux edit NAME 验证并原子应用编辑器会话。
实例默认受契约锁定。对于显式维护窗口,停止实例,运行 ginux unlock NAME,执行所需维护,再次停止它,然后运行 ginux lock NAME。ginux list 和 ginux status NAME 显示当前状态,启动未锁定的实例会打印警告。解锁仅放宽可配置的配方限制;Ginux 仍然拥有虚拟内核边界,拒绝不支持的权限,减少主机身份,并应用 NoNewPrivs。锁定状态是主机部署状态,永远不会写入导出的可移植配方。
系统调用审计默认通过 [audit] enabled = false 禁用。将 --audit 添加到 ginux start 以在该次运行中启用它,或设置 enabled = true 以在每次启动时启用它。启用的审计记录始终进入实例的 audit.log;它们与附加的控制台保持分离。使用 mode = "stderr" 或 mode = "disabled" 的现有配方保持读取兼容,并在重写时规范化为新的布尔形式。
实例规范支持类型化配方覆盖、入口和出口契约策略、卷、虚拟身份以及有序的预启动钩子。主机网络连接和转换端口是部署设置,在创建或更改实例时作为可重复标志提供:
ginux import web /etc/ginux/examples/rocky-systemd.toml \
--network eth0:1 \
--forward protocol=tcp,external_port=8080,instance_port=80 \
--forward protocol=tcp,external_port=8443,instance_port=443
ginux set web \
--network default \
--forward none省略 --network 会使用宿主机的默认网络。多个标志会以原子方式校验并持久化;无效组合不会产生任何更改。钩子仅作为受中介的实例进程运行;Ginux 不支持任意宿主上下文钩子。有关模式、优先级规则、示例和验证目标,请参阅 Gate 9.75 实例规范。
同一个实例文件在 [contract] 及其 syscalls、filesystem、network、devices、privileges、process 和 kernel 子节下包含一个自包含的分层契约。系统调用名称接受针对固定目录编译的有界通配符;显式拒绝始终优先。该契约是实例的一部分,而不是单独的操作员对象。启动前请验证并检查完整的实例配方:
ginux validate /etc/ginux/examples/rocky-systemd.toml ginux inspect /etc/ginux/examples/rocky-systemd.toml ginux explain /etc/ginux/examples/rocky-systemd.toml filesystem.path.write
有关语法、优先级、迁移行为和完整的 CLI 工作流,请参阅 自包含用户空间契约。
所连接的终端将成为 Ginux 实例控制台。使用密码 ginux 以 root 登录,正常探索,按 Ctrl-] 分离并让实例继续运行,并以实例 root 身份运行 kill -RTMIN+4 1 来关闭 systemd 并返回宿主机。分离运行使用 ginux start rocky、ginux status rocky 和 ginux stop rocky。退出 Rocky 宿主机 shell 以干净地停止 QEMU。这会停止 VM 而不删除其持久化 overlay;make clean 会重置该宿主机状态。make demo-gate-9 使用相同的操作员工作流;make run-systemd 保留为兼容性别名,make test-systemd-interactive 是自动化回归测试。
实时测试和交互式演示启用共享外部网络。在 QEMU TCG 下,上游 DNF 更新可能需要很多分钟,因为每个受管理的系统调用都会被拦截和审计。离线 systemd 测试仅使用准备好的、固定摘要的缓存。
各个组件和 gate 目标记录在 Makefile 以及 docs/ 和 hdae/ 下的相应文件中。
安全模型与限制
主要对手控制实例内的一个进程。宿主机内核、硬件、Ginux 可执行文件和已批准的配置、启动链以及操作员目前均受信任。平衡的原生后端仍然依赖宿主机进行 ELF 加载、地址空间、缺页、信号、调度和 ptrace;这些仍然是宿主机内核的攻击面。
Ginux 是研究软件。它尚未获得独立的安全认证,不提供完整的 Linux 兼容性,也不适合生产工作负载。未知的契约操作会失败关闭,但当前的覆盖范围和保证仅限于已测试的 gate。
精确的支持声明、保留的 supervisor 权限、受信任组件、排除项、兼容性差距、性能限制和可复现的边界测试记录在 安全边界与限制 中。
对于 agent 隔离,受保护对象是宿主机以及任何未明确分配给实例的权限。Ginux 约束 agent 及其工具所执行的 Linux 进程;它不检查模型推理、不保护外部模型提供商,也不使不受信任的代码在强制实例边界之外变得安全。
完整的意图、威胁模型、不变量和 gate 计划位于:
开发方法
Ginux 采用
Human-Directed Agentic Engineering (HDAE) 方式开发:
由人类掌控产品意图、架构、约束和验收边界,并结合由智能体执行的实现、诊断和测试
循环。每个获批的关卡都会在 hdae/ 中记录其范围、非目标、验收标准、
验证、证据和停止点。
历史独立内核
这个可独立启动的内核仍可用于研究和历史 参考:
它不再是活跃的开发线。代码仍按仓库现有条款提供,供任何独立选择研究 或 fork 它的人使用。
这个名字和 Linux 一样是个玩笑:Ginux 结合了 Linux、
GPT 和 Greg。为了让作者的自我意识保持在配置的资源限制内,
G 正式代表 GPT。
许可证
Ginux 在 Apache License 2.0 下提供。参见 LICENSE 和
NOTICE。
配置的原生启动
在 x86-64 Linux 上,ginux launch <config> <root> [--result-file <fresh host path>]
会在提供的宿主根目录内,以配置的参数和环境
执行 [instance].init。引导程序需要 chroot 和凭据
更改权限,以及支持 close_range 的内核(Linux 5.9 或更高版本)。
它在执行工作负载前会降权到 UID/GID 1000。配置必须
选择 profile = "balanced" 和 backend = "ptrace-native";存储和设备
分配未由该命令实现,会被拒绝。
[instance].workdir 是可选的,默认为 /work;也支持 /。
如果父目录存在,引导程序会创建工作目录,然后在降权后
进入该目录。Init 和工作目录路径限制为
128 字节,且必须是绝对路径,不能包含 .. 路径组件。最多接受七个
各 128 字节的参数和八个各 256 字节的 NAME=value 环境条目。
无效或过大的输入会在启动前失败。
工作负载的 stdout 与 stderr 上的监督程序诊断信息保持分离。工作负载只继承
stdin、stdout 和 stderr。启动器返回工作负载退出码,若因信号终止则返回 128 + signal。启动
失败返回 2;监督或结果写入失败返回 1。不支持原生
fork、clone 和后续 exec;在实现子进程监督之前,fork 返回 ENOSYS。
非 x86-64 系统调用 ABI 和作业控制组停止会显式失败。审计输出上限为 64 KiB 加一条截断
通知。此路径不实现实例元数据中的资源配额。
请求时,结果文件会在启动前被独占保留, 模式为 0600,最大路径长度为 512 字节。现有文件绝不会 被覆盖。完成后,它包含一个已同步的 JSON 对象:
{"version":1,"outcome":"exited","code":0,"signal":null,"generation":null}outcome 为 exited、signaled 或 supervisor-error;只会填充相关的
code 或 signal。存储代际未实现,因此
generation 为 null。结果缺失、为空或格式错误都属于协议失败。
即使工作负载成功,写入、同步或关闭结果失败也会返回 1。
消费者应等待启动器完成后再读取它。
聚焦的 Linux 集成场景使用现有二进制文件:
make build make build-launch-fixture make build-gate1-fixture sudo make test-gate1-setup sudo make test-launch sudo make test-native-supervision
原生进程场景
单独的 process-native <config> <executable> 命令会演练原生
fork、clone、vfork、exec、线程、等待、终止信号和管道。它
以调用者身份执行受信任的宿主测试夹具,不使用 chroot;在允许跟踪子进程的宿主上
不需要 root。配置的 launch 仍是单独的入口点,尚未使用此进程循环。
进程创建返回虚拟身份,包括克隆 TID 单元。
未跟踪、重新父进程化、命名空间以及不支持的克隆标志均被拒绝。
线程槽在退出时回收。CLONE_FILES 共享一个描述符表;
普通 fork 会复制它,exec 在关闭 CLOEXEC 条目之前会解除共享。
工作线程 exec 保留进程组的进程身份。继承的宿主
stderr 以上的文件在执行前关闭。不支持 PI futex 操作以及
查询其他任务的 robust list。
显式构建测试夹具,然后运行它们各自的独立场景:
make build build-process-fixtures make test-process-native make test-process-native SCENARIO=shared_pipe make test-process-native SCENARIO=thread_exec
这些场景使用 .artifacts/process 下的现有二进制文件,包括
对系统调用 ABI 分发、克隆限制、描述符继承、
线程重用、成功和失败的工作线程 exec、信号、等待状态写入、
非阻塞等待以及 vfork 的检查。现有的原生 fork 夹具也会运行。这些
检查并不声称具备完整的 libc、作业控制或 systemd 兼容性。
来源:Hacker News · github.com