feat(guests): let a guest nest OCI containers (task 0009) #35
37
.claude/tasks/0009-guest-nesting.md
Normal file
37
.claude/tasks/0009-guest-nesting.md
Normal file
@@ -0,0 +1,37 @@
|
||||
---
|
||||
spec: guests
|
||||
blocked-by: 0002-guest-walking-skeleton
|
||||
---
|
||||
|
||||
## What to build
|
||||
|
||||
The Host-side placement that makes the OCI fallback a plain Guest, so image-only software has a declarative home without a separate mechanism.
|
||||
A Host sets `nesting` to grant the nested-container prerequisites so the Guest's interior can run Podman and other OCI containers.
|
||||
The Skeleton emits the nested-container cgroup-delegation and capability prerequisites once, so the operator flips one boolean and the interior's `oci-containers` runtime works, with Podman as the default runtime.
|
||||
|
||||
## Acceptance criteria
|
||||
|
||||
- [x] A Host setting `guests.<path>.nesting` grants the nested-container cgroup-delegation and capability prerequisites, off by default.
|
||||
- [x] With `nesting` on, the Guest's interior can define `oci-containers` running Podman as the default runtime.
|
||||
- [x] With `nesting` off, those prerequisites are absent and the Guest is unaffected.
|
||||
- [x] A Host with a nesting Guest that defines an OCI container builds via `nix flake check`.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
- `nesting` is a Host-side placement field on the guest, a bool defaulting off, alongside the other placement fields in `lib.nix`.
|
||||
On, it grants the guest's container `CAP_NET_ADMIN` and the `/dev/net/tun` and `/dev/fuse` device nodes, the capability and devices an OCI runtime reaches for to network its containers and back their overlay storage.
|
||||
Off, both `additionalCapabilities` and `allowedDevices` are empty, matching the NixOS defaults, so a non-nesting guest is untouched.
|
||||
|
||||
- The capability prerequisite is `CAP_NET_ADMIN` alone.
|
||||
A container-backend guest runs privileged (`privateUsers = "no"`), so it already retains the broad nspawn capability set including `CAP_SYS_ADMIN`; the one addition an OCI runtime needs is network administration for its bridges and firewall rules.
|
||||
|
||||
- cgroup delegation is not toggled by `nesting`, a deliberate deviation from the criterion's wording that the flag "grants" it and that it is "absent" when off.
|
||||
The NixOS container backend sets `Delegate = true` on every container's unit unconditionally, so the delegated cgroup subtree an OCI runtime manages is always present.
|
||||
Re-emitting it under `nesting` would be redundant, and forcing it off for non-nesting guests to make it literally "absent" would remove a harmless, useful default for no gain.
|
||||
The Skeleton records the prerequisite as satisfied-elsewhere with an absence pointer comment, so a reader does not think delegation was forgotten.
|
||||
|
||||
- A new `guests/nesting-sample.nix` carries an interior that defines an `oci-containers` workload, the payload the criteria exercise, and `neogaia` enables it with `nesting = true`.
|
||||
This follows the walking-skeleton's precedent of proving a guest path through the one Host's `nix flake check`: the flake check builds the nested `nixos-system-nesting-sample` in full, pulling in `podman` and the generated `podman-hello.service` unit, which is how criteria two and four are verified on the build seam.
|
||||
The sample carries the same modest caps as the walking-skeleton guest so an interior container cannot starve the laptop.
|
||||
|
||||
- The two behaviors the build seam cannot prove — that the interior Podman actually starts a container and that its networking works — are left to manual verification on the target Host and to the VM integration test of task 0010, per the spec's testing decisions.
|
||||
11
guests/nesting-sample.nix
Normal file
11
guests/nesting-sample.nix
Normal file
@@ -0,0 +1,11 @@
|
||||
args@{ my, ... }:
|
||||
# A sample guest whose interior runs an OCI container on Podman.
|
||||
# The image is pulled at runtime, so the guest builds with no build-time fetch.
|
||||
my.guest {
|
||||
name = "nesting-sample";
|
||||
interior = {
|
||||
virtualisation.oci-containers.containers.hello = {
|
||||
image = "docker.io/library/hello-world";
|
||||
};
|
||||
};
|
||||
} args
|
||||
@@ -54,6 +54,16 @@
|
||||
tasksMax = 512;
|
||||
};
|
||||
|
||||
# The nesting guest, run with `nesting` on: proves an interior OCI container
|
||||
# on Podman builds end to end through this host's `nix flake check`.
|
||||
guests.nesting-sample.enable = true;
|
||||
guests.nesting-sample.nesting = true;
|
||||
guests.nesting-sample.limits = {
|
||||
memory = "1G";
|
||||
cpu = "100%";
|
||||
tasksMax = 512;
|
||||
};
|
||||
|
||||
modules.agents.claude-code.enable = true;
|
||||
modules.agents.tools.gitea-axi.enable = true;
|
||||
modules.agents.pi.enable = true;
|
||||
|
||||
32
lib.nix
32
lib.nix
@@ -279,6 +279,19 @@ let
|
||||
'';
|
||||
};
|
||||
};
|
||||
nesting = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = false;
|
||||
description = ''
|
||||
Grant the guest's interior the prerequisites to run Podman or other
|
||||
OCI containers of its own. Off by default, so a guest cannot nest
|
||||
containers. On, the guest's container gains the network-administration
|
||||
capability its container runtime uses to build bridges and firewall
|
||||
rules, along with the tun and fuse device nodes such a runtime reaches
|
||||
for, so the interior's `virtualisation.oci-containers` works with
|
||||
Podman as its default runtime.
|
||||
'';
|
||||
};
|
||||
autoStart = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = true;
|
||||
@@ -335,6 +348,25 @@ let
|
||||
# A private-user mapping would shift the ids and reintroduce those errors, so it stays off.
|
||||
privateUsers = lib.mkDefault "no";
|
||||
|
||||
# A nesting guest runs Podman or other OCI containers in its interior.
|
||||
# The network-administration capability lets that runtime build its
|
||||
# bridges and firewall rules.
|
||||
# The tun and fuse device nodes are what it reaches for to network
|
||||
# those containers and back their overlay storage.
|
||||
# The remaining prerequisite, a delegated cgroup subtree for the
|
||||
# runtime to manage, the container backend already grants every guest.
|
||||
additionalCapabilities = lib.optionals cfg.nesting [ "CAP_NET_ADMIN" ];
|
||||
allowedDevices = lib.optionals cfg.nesting [
|
||||
{
|
||||
node = "/dev/net/tun";
|
||||
modifier = "rwm";
|
||||
}
|
||||
{
|
||||
node = "/dev/fuse";
|
||||
modifier = "rwm";
|
||||
}
|
||||
];
|
||||
|
||||
bindMounts = userMounts // secretMounts;
|
||||
|
||||
inherit specialArgs;
|
||||
|
||||
Reference in New Issue
Block a user