跳到正文
Hacker News· d3Xt3r·· 11 小时前AI 评分38

Greg Kurtzer 的 Ginux 转型为用户空间内核

Greg Kurtzer's Ginux is now a userspace kernel

AI 导读

Greg Kurtzer 的 Ginux 已从独立 Rust 内核转型为可配置的用户空间内核,作为普通 Linux 进程运行,在受管工作负载与宿主内核之间建立显式、版本化的契约。

正文 · AI 翻译

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