Run one command under confinement
This page takes a Linux host from nothing to one confined command, with no daemon. The
b10x-substrate command line opens a confinement adapter in its own
process and runs the command inside it.
Prerequisites
- Linux on x86_64.
- For the
ociadapter: Docker. - For the
linuxadapter: bubblewrap and a systemd user session on cgroup v2.
Install the command line
From release 0.14.0, each release carries the command line and SHA256SUMS:
version=0.16.1
asset="b10x-substrate-$version-x86_64-unknown-linux-gnu.tar.gz"
base="https://github.com/beyond10x/substrate/releases/download/$version"
curl -fLO "$base/$asset" -fLO "$base/SHA256SUMS"
sha256sum --check --ignore-missing SHA256SUMS
tar -xzf "$asset"
Put the b10x-substrate binary from the archive on your PATH.
For an earlier release, build it from its tag with Rust 1.97 or newer:
cargo install --git https://github.com/beyond10x/substrate --tag 0.13.0 b10x-substrate-cli --locked
Preview a run
With Docker, pull an image and name it by digest:
docker pull alpine:3.22
image=$(docker inspect --format '{{index .RepoDigests 0}}' alpine:3.22)
mkdir job
b10x-substrate confine run --backend oci --image "$image" --dir job --dry-run -- /bin/echo hello
The adapter never pulls an image itself.
--dry-run admits the run as a real run would, prints the command the adapter would start and
runs nothing. Here that is a docker create with no network, no capabilities, a read-only root
and job at /workspace.
Run a confined command
Drop --dry-run. The command writes to job, mounted at /workspace:
b10x-substrate confine run --backend oci --image "$image" --dir job \
-- /bin/sh -c 'echo hello > /workspace/out.txt'
cat job/out.txt
Without Docker
The linux adapter runs the command under bubblewrap, in a private cgroup beneath a delegated
cgroup v2 root. Ask your systemd user manager for a delegated scope with a shell in it:
systemd-run --user -p Delegate=yes --scope bash
In that shell, move the shell out of the scope's root, which must hold no process. Then enable the controllers and name the root:
root="/sys/fs/cgroup$(cut -d: -f3 /proc/self/cgroup)"
mkdir "$root/shell"
echo $$ > "$root/shell/cgroup.procs"
echo "+cpu +memory +pids" > "$root/cgroup.subtree_control"
export SUBSTRATE_CONFINE_CGROUP_ROOT="$root"
b10x-substrate confine run --dir job -- /bin/sh -c 'echo hello > /workspace/out.txt'
See a named refusal
Ask for something the adapter cannot prove, such as the host network:
b10x-substrate confine run --backend oci --image "$image" --dir job --net -- /bin/true
b10x-substrate: confinement.fact-unserved: host_network
It exits 2 and runs nothing. Substrate refuses by name; it never runs with weaker isolation. An
image named by tag is refused the same way, as confinement.image-unpinned.
Next steps
- Command line: every
confine runflag, and the fixed bounds. - Confinement adapters: what each adapter enforces and declares.
- Run a bounded command: run the daemon and drive it through its API.