Skip to content

Commit c005c71

Browse files
dianpopaalxiord
authored andcommitted
docs: add documentation on jailer
Signed-off-by: Diana Popa <[email protected]>
1 parent f082669 commit c005c71

File tree

1 file changed

+75
-0
lines changed

1 file changed

+75
-0
lines changed

docs/jailer.md

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
## JAILER USAGE
2+
3+
The jailer is invoked in this manner:
4+
```bash
5+
jailer --id <id> --node <numa_node> --exec-file <exec_file> --uid <uid> --gid <gid> [--chroot-base-dir <chroot_base>]
6+
```
7+
8+
* **id** is the unique VM identification string, which has to be alphanumeric for now.
9+
10+
* **numa_node** represents the NUMA node the process gets assigned to. More details are available below.
11+
12+
* **exec_file** is the path to the Firecracker binary that will be exec-ed by the jailer. The user can provide a path to any binary, but the interaction with the jailer is mostly Firecracker specific.
13+
14+
* **uid** and **gid** are the uid and gid the jailer switches to as it execs the target binary.
15+
16+
* **chroot_base** represents the base folder where chroot jails are built. The default is `/srv/jailer`.
17+
18+
19+
## JAILER OPERATION
20+
21+
22+
After starting, the Jailer goes through the following operations:
23+
24+
* Validate **all provided paths** and the **VM id**.
25+
26+
* Open `/dev/kvm` as *RW*, `/dev/net/tun` as *RW non-blocking*, and bind a Unix domain socket listener to `<chroot_base>/<exec_file_name>/<id>/api.socket`. **exec_file_name** is the last path component of **exec_file** (for example, that would be “firecracker” for `/usr/bin/firecracker`). All three file descriptors remain open across exec-ing into the target binary, which would be otherwise unable to open/create the associated files.
27+
28+
* Create the `<chroot_base>/<exec_file_name>/<id>/root` folder, which will be henceforth referred to as **chroot_dir**. Nothing is done if the path already exists (it should not, since **id** is supposed to be unique).
29+
30+
* Copy **exec_file** to `<chroot_base>/<exec_file_name>/<id>/root/<exec_file_name>`. This (as opposed to hard linking) is currently the default behavior. Being able to create hard links instead of copies will be implemented as a command line option shortly.
31+
32+
* Create the cgroup subfolders. At the moment, the jailer uses three cgroup v1 controllers: `cpu`, `cpuset`, and `pids`. On most systems, these (along with others) are mounted by default somewhere in `/sys/fs/cgroup` (they should be mounted by the user otherwise). The jailer will parse `/proc/mounts` to detect where each of the three controllers can be found (multiple controllers may share the same path). For each identified location (referred to as `<cgroup_base>`), the jailer creates the `<cgroup_base>/<exec_file_name>/<id>` subfolder, and writes the current pid to `<cgroup_base>/<exec_file_name>/<id>/tasks`. Also, the value of **numa_node** is written to the appropriate **cpuset.mems** file.
33+
34+
* Chroot into **chroot_dir**.
35+
36+
* Drop privileges via setting the provided **uid** and **gid**.
37+
38+
* Exec into ```<exec_file_name> --jailed```. The **--jailed** command line argument to the target binary is then interpreted by Firecracker, that realizes it’s running inside a jail, and continues the execution accordingly.
39+
40+
41+
## EXAMPLE RUN AND NOTES
42+
43+
Let’s assume Firecracker is available as `/usr/bin/firecracker`, and the jailer can be found at `/usr/bin/jailer`. We pick the **unique id 0abbcf2**, and we choose to run on **NUMA node 0**, using **uid 123**, and **gid 100**. For this example, we are content with the default `/srv/jailer` **chroot base dir**.
44+
45+
We start by running ```/usr/bin/jailer --id 0abbcf2 --node 0 --exec-file /usr/bin/firecracker --uid 123 --gid 100```.
46+
47+
After opening the file descriptors mentioned in the previous section, the jailer will create the following resources (and all their prerequisites, such as the path which contains them):
48+
49+
* `/srv/jailer/firecracker/0abbcf2/api.socket` (created via `bind`)
50+
* `/srv/jailer/firecracker/0abbcf2/root/firecracker` (copied from `/usr/bin/firecracker`)
51+
52+
Let’s also assume the **cpu**, **cpuset**, and **pids** cgroups are mounted at `/sys/fs/cgroup/cpu`, `/sys/fs/cgroup/cpuset`, and `/sys/fs/cgroup/pids`, respectively. The jailer will create the following subfolders (which will inherit settings from the parent cgroup):
53+
54+
* `/sys/fs/cgroup/cpu/firecracker/0abbcf2`
55+
* `/sys/fs/cgroup/cpuset/firecracker/0abbcf2`
56+
* `/sys/fs/cgroup/pids/firecracker/0abbcf2`.
57+
58+
It’s worth noting that, whenever a folder already exists, nothing will be done, and we move on to the next directory that needs to be created. This should only happen for the common **firecracker** subfolder (but, as for creating the chroot path before, we do not issue an error if folders directly associated with the supposedly unique **id** already exist).
59+
60+
The jailer then writes the current pid to `/sys/fs/cgroup/cpu/firecracker/0abbcf2/tasks`, `/sys/fs/cgroup/cpuset/firecracker/0abbcf2/tasks`, and `/sys/fs/cgroup/pids/firecracker/0abbcf2/tasks`. It also writes ```0``` to `/sys/fs/cgroup/cpuset/firecracker/0abbcf2/cpuset.mems`.
61+
62+
Finally, the jailer chroots into `/srv/jailer/firecracker/0abbcf2/root`, switches the **uid** to ```123```, and **gid** to ```100```, and execs `/firecracker —jailed`.
63+
64+
We can now use the socket at `/srv/jailer/firecracker/0abbcf2/api.socket` to interact with the VM.
65+
66+
### Observations
67+
68+
* The user must create hard links for (or copy) any resources which will be provided to the VM via the API (disk images, kernel images, named pipes, etc) inside the jailed root folder. Also, permissions must be properly managed for these resources; for example the user which Firecracker runs as must have both **read and write permissions** to the backing file for a RW block device.
69+
* It’s up to the user to load balance VM placement among multiple NUMA nodes (if present), using the ```--node``` command line argument.
70+
* The user must also manage any further fine tuning of resource partitioning via cgroups (most likely the ones created by the jailer), or any other means.
71+
* It’s up to the user to handle cleanup after running the jailer. One way to do this involves registering handlers with the cgroup **notify_on_release** mechanism, while being wary about potential race conditions (the instance crashing before the subscription process is complete, for example).
72+
73+
## Caveats
74+
75+
* If all the cgroup controllers are bunched up on a single mount point using the “all” option, our current program logic will complain it cannot detect individual controller mount points.

0 commit comments

Comments
 (0)