Skip to content

Commit 5b2f040

Browse files
committed
sandlock-oci: add crate README
Signed-off-by: Cong Wang <cwang@multikernel.io>
1 parent ecb4393 commit 5b2f040

2 files changed

Lines changed: 140 additions & 2 deletions

File tree

crates/sandlock-oci/Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ edition.workspace = true
55
license.workspace = true
66
repository.workspace = true
77
homepage.workspace = true
8-
description = "OCI runtime shim for sandlock namespace-less, Landlock-based"
9-
readme = "../../README.md"
8+
description = "OCI runtime shim for sandlock: namespace-less, Landlock-based"
9+
readme = "README.md"
1010

1111
[lib]
1212
name = "sandlock_oci"

crates/sandlock-oci/README.md

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
# sandlock-oci
2+
3+
An OCI runtime shim for the [sandlock](https://github.com/multikernel/sandlock)
4+
sandbox. It implements the OCI Runtime Specification command interface so that
5+
container runtimes (containerd, CRI-O, Kubernetes) can use sandlock as a
6+
drop-in low-level runtime, with no kernel namespaces and no cgroups.
7+
8+
Where `runc` builds containers from namespaces, cgroups, and `pivot_root`,
9+
`sandlock-oci` confines the workload with **Landlock** (filesystem, network,
10+
and IPC), **seccomp-bpf** (syscall filtering), and **seccomp user
11+
notification** (resource limits, IP enforcement, and `/proc` virtualization).
12+
The result is a namespace-less container that runs unprivileged.
13+
14+
## How it fits in
15+
16+
`sandlock-oci` is a thin OCI-compatible front end over `sandlock-core`. It
17+
parses an OCI bundle, translates `config.json` into a sandlock policy, and
18+
drives the sandbox lifecycle on behalf of a higher-level runtime.
19+
20+
```
21+
containerd / CRI-O / kubelet
22+
│ (OCI runtime calls)
23+
24+
sandlock-oci ──fork──> supervisor daemon
25+
│ │
26+
│ ▼
27+
│ sandlock-init (confined PID 1 in the sandbox)
28+
│ │ fork + execve
29+
│ ▼
30+
│ workload (+ any exec'd siblings)
31+
32+
sandlock-core
33+
Landlock · seccomp · seccomp-notify · COW
34+
```
35+
36+
Each call from the runtime maps to one `sandlock-oci` subcommand. A long-lived
37+
supervisor daemon owns the sandbox between calls, and an in-sandbox
38+
`sandlock-init` acts as PID 1 so the workload and every `exec`'d process share
39+
one sandbox and one seccomp supervisor.
40+
41+
## Lifecycle
42+
43+
```
44+
create <id> -b <bundle> spawn the supervisor, build the sandbox, park the
45+
child before execve, and save state
46+
start <id> release the parked child so it execve's the workload
47+
state <id> print state.json, reconciled against liveness
48+
kill <id> <signal> forward a signal to the workload (or its group with -a)
49+
delete <id> shut the supervisor down and remove the state dir
50+
exec <id> <cmd> run a sibling process in the same sandbox
51+
list list all sandboxes managed by sandlock-oci
52+
checkpoint <id> snapshot a running sandbox to an image directory
53+
restore <id> recreate and resume a sandbox from a checkpoint image
54+
check report kernel Landlock support
55+
```
56+
57+
The `create` then `start` split matches the OCI two-phase model: `create`
58+
forks the child and installs the full policy (Landlock, seccomp-notify,
59+
resource limits, and the network ACL) with the child parked just before
60+
execve, and `start` releases it. The supervisor reports the child PID back to
61+
the caller over a pipe, so there is no sleep or race in the handshake.
62+
63+
## OCI spec translation
64+
65+
`config.json` is mapped to a sandlock policy by intent rather than by
66+
replaying Linux container primitives:
67+
68+
| OCI field | sandlock mapping |
69+
|---|---|
70+
| `root.path` | chroot target for the sandbox |
71+
| `root.readonly` | grants the rootfs read-only instead of read-write |
72+
| `mounts` (bind) | `fs_mount` with read-only or read-write from options |
73+
| `mounts` (tmpfs) | host-backed scratch dir, isolated and cleaned on delete |
74+
| `mounts` (proc) | host `/proc` mounted read-only, virtualized by seccomp |
75+
| `mounts` (sysfs) | host `/sys` mounted read-only |
76+
| `process.cwd` | working directory |
77+
| `process.env` | environment (started clean, then populated from the spec) |
78+
| `process.user` | run-as uid/gid, with a user namespace only if needed |
79+
| `linux.resources.memory.limit` | `max_memory` |
80+
| `linux.resources.pids.limit` | `max_processes` |
81+
| `linux.resources.cpu.quota/period` | sub-core `max_cpu` throttle |
82+
| `linux.resources.cpu.cpus` | CPU affinity via `sched_setaffinity` |
83+
| `linux.namespaces` | ignored by design |
84+
85+
Supported OCI spec versions are 1.0.x, 1.1.x, and 1.2.x. Bundles declaring any
86+
other version are rejected at `create` rather than silently mis-mapped.
87+
88+
Mount types with no safe namespace-less equivalent (`devpts`, `mqueue`,
89+
`cgroup`, `cgroup2`) are skipped. Port binding is remapped so in-container
90+
servers can `bind()` without colliding on host ports.
91+
92+
## runc compatibility
93+
94+
The CLI accepts the global and per-command flags that containerd and CRI-O
95+
pass to `runc`, so it can be wired in as a drop-in replacement. Flags that do
96+
not apply to a namespace-less, cgroup-less runtime are accepted and ignored:
97+
`--systemd-cgroup`, `--rootless`, `--no-pivot`, `--no-new-keyring`, and the
98+
`--debug` flag. Fatal errors are appended to the `--log` file in text or JSON
99+
form so the containerd shim can surface the real failure reason.
100+
101+
The supervisor daemon exits with the workload's status (re-raising the killing
102+
signal where applicable), so a reaping shim sees a wait-status that mirrors the
103+
workload, the same as it would from `runc`.
104+
105+
## Usage
106+
107+
Point a runtime at the `sandlock-oci` binary as its OCI runtime, or drive it
108+
directly against a bundle:
109+
110+
```sh
111+
sandlock-oci create mycontainer -b /path/to/bundle
112+
sandlock-oci start mycontainer
113+
sandlock-oci state mycontainer
114+
sandlock-oci exec mycontainer sh -c 'echo hello'
115+
sandlock-oci kill mycontainer SIGTERM
116+
sandlock-oci delete mycontainer
117+
```
118+
119+
State lives under `$XDG_RUNTIME_DIR/sandlock-oci` for unprivileged users and
120+
`/run/sandlock-oci` for root, overridable with the global `--root` flag.
121+
122+
## Requirements
123+
124+
- Linux 6.12 or newer (Landlock ABI v6)
125+
- A built `sandlock-core` (this crate depends on it directly)
126+
127+
Run `sandlock-oci check` to confirm the running kernel supports Landlock.
128+
129+
## Limitations
130+
131+
- The workload and all `exec`'d processes share one sandbox and one seccomp
132+
supervisor, via the in-sandbox `sandlock-init` PID 1.
133+
- `exec` is non-TTY only. `-t` and `--console-socket` are accepted for runc
134+
compatibility but ignored, as there is no PTY support yet.
135+
136+
## License
137+
138+
Apache-2.0. See the repository root for details.

0 commit comments

Comments
 (0)