nexcage

nexcage 0.17.0

One binary, both backends. The release’s binary manages Proxmox LXC containers and is the OCI runtime a container engine drives, with the crun backend built in. The .deb installs it with the libraries it needs.

A pod can run under an isolation profile. The node’s administrator names, in the configuration file, what a container may not exceed. An engine’s RuntimeClass picks a profile by the handler’s program name, nexcage@<profile>. On a k3s node, two pods under two profiles were checked from inside each pod.

Read “Upgrading” before you install. The configuration file now fails closed, so a file that loaded under 0.16.0 can be refused.

One release: one binary, one .deb (#380)

From 0.11.2 to 0.16.0 a release carried two binaries:

So an engine configuration or an isolation profile next to the .deb pointed at a backend the installed binary did not have.

Now there is one binary with both backends, and one .deb that packs it:

Before it publishes, the release installs the .deb in a clean Debian 13 container and asks the crun backend for features. crun_build.yml does the same on every pull request, and k8s_e2e.yml installs the .deb on the E2E node to run its pods. The .deb’s copyright file names the licenses of libcrun (LGPL-2.1-or-later) and libocispec, which the binary carries.

Thanks to @IhorTelepenko, whose #301 started this.

Isolation profiles

{ "profiles": {
    "hardened": { "runtime": "crun", "crun": {
      "user_namespace": "require",
      "seccomp": "require",
      "capabilities": { "drop": ["CAP_NET_RAW", "CAP_MKNOD", "CAP_SYS_CHROOT"] },
      "limits": { "memory": "1G", "pids": 1024 } } } } }

create applies a profile to the bundle before libcrun sees it.

A profile only takes away: it never adds a capability or raises a limit.

Naming a profile.

Refusals. These exit 2 and create nothing:

packaging/config/config.oci.example.json now defines two profiles, hardened and small. INSTALL.md shows the symlink, the k3s handler and the RuntimeClass for each, and CLI_REFERENCE.md the keys.

Checked from inside the container, not from nexcage’s log

tests/k8s/pod_on_node.sh on the E2E node (k3s v1.36.5) runs one pod under each of two classes:

  nexcage-hardened nexcage-small
uid_map a user namespace the host’s, 0 0 4294967295
Seccomp of pid 1 2 2
CapBnd of pid 1 and of a kubectl exec no NET_RAW, SYS_CHROOT, MKNOD no NET_RAW
memory.max 1073741824 (the pod asked for 2Gi) 268435456 (the pod set none)
pids.max 1024 256

A third pod under nexcage-hardened without hostUsers: false is refused, and its events say “requires a user namespace”.

tests/crun/profile.sh runs in crun_build.yml on every push. It checks the same on a bundle, through exec --process and through cgroup v2’s memory.swap.max.

The configuration file fails closed (#371)

A key nexcage does not read, a value of the wrong type, and a runtime or log level it does not know all refuse the file. The message names the file and the key, nothing runs, and the exit status is 1.

Under 0.16.0:

The keys that remain are the README’s table, plus profiles.

A container’s backend comes from the container (#372)

Every command used to choose its backend from the routing rules again, so a rule changed after create sent state, kill or delete to the other backend. Routing now decides only for create and run. Every other command goes to the backend that has the container. --runtime lxc for a container on the crun backend exits 2 instead of being obeyed.

Fixed

Not in this release

A pod as a Proxmox LXC container (#316). ADR-006 records what a probe on the E2E node (Proxmox VE 9.2.20) showed. It can be done:

Its costs fall on every pod:

All it would add is visibility in pct list. A profile with "runtime": "lxc" keeps refusing the file.

Upgrading from 0.16.0

  1. Check the configuration before you replace the binary. Run the new binary against your file:

    ./nexcage-0.17.0-amd64 --config /etc/nexcage/config.json version
    
    • It prints the version if the file loads.
    • If it does not, it names the file and the key to remove or fix, and exits 1.
    • The keys 0.16.0 parsed and never used are refused like misspelt ones: runtime_type, default_runtime, runtime.root_path, data_dir, cache_dir, temp_dir, network.ip, network.gateway, security, resources, container_config.default_container_type, and pct_path, node and legacy_api under proxmox.
    • container_config.crun_name_patterns is refused, with runtime.routing named as its replacement.
  2. There is no -crun asset. A script that downloads nexcage-<version>-amd64-crun has to drop the suffix: nexcage-0.17.0-amd64 is that build now.
  3. Install the .deb: apt install ./nexcage-0.17.0-amd64.deb.
  4. A node set up from 0.16.0’s instructions has the -crun binary at /usr/local/bin/nexcage, and its engine names that path. The .deb puts nexcage at /usr/bin/nexcage. Either change BinaryName (or CRI-O’s runtime_path) to /usr/bin/nexcage and restart the engine, then remove /usr/local/bin/nexcage, or leave the engine as it is and replace /usr/local/bin/nexcage with the new binary. Do not leave two different versions installed.
  5. Profiles are optional. A file without profiles behaves as before.