Skip to content

Security: samuelkarp/runj

Security

docs/security.md

Security

runj is a proof-of-concept and the implementation has not been evaluated for its security. Do not use runj on a production system. Do not run workloads inside runj that rely on a secure configuration.

With that said, this document attempts to describe the security-related choices that have been made in runj.

Directories

runj makes use of a state directory located at /var/lib/runj. Directories for individual jails exist underneath this one (in /var/lib/runj/jails/<id>) and contain runj's own state file (state.json), a copy of the OCI configuration provided in the bundle (config.json, plus runj.ext.json if present), and the exec.fifo used to synchronize create and start.

Default jail configuration

Names

Jails are identified by a name and an ID (JID). runj uses the user-supplied ID parameter as the jail's name and receives an automatically-assigned JID.

Persistence

Jails are created with the "persist" parameter set, which runj passes directly to the jail_set(2) syscall. This allows jails to exist without any running processes.

Mounts

runj mounts exactly the filesystems described in the OCI configuration's mounts; it does not inject any mounts of its own. The example specification generated by runj demo spec includes a devfs mount with the ruleset=4 ruleset (equivalent to devfsrules_jail), which allows basic devices like null, random, and STDIO to be available inside the jail. (Some tools like ps have a dependency on /dev/null to function.)

Dependencies

On the system

runj manages jails by invoking the FreeBSD jail syscalls directly (jail_set(2), jail_get(2), jail_attach(2), and jail_remove(2)). It does, however, rely on a few host userland utilities:

  • mount(8) to mount and unmount filesystems (the Go runtime does not implement mounting on FreeBSD).
  • ifconfig(8) to move VNet interfaces into and out of a jail.
  • ps(1) (run outside the jail) to enumerate processes and determine whether a jail is still running.
  • jexec(8) (along with kill(1) inside the jail) to implement runj kill; see "Inside the jail" below.

The default behaviors of these utilities are used in runj.

Inside the jail

runj kill makes use of the kill(1) command inside the jail's rootfs; if this command does not exist (or is not functional), runj kill will not work. If the kill command has been replaced by a malicious binary, invoking runj kill will cause that binary to run instead of the normal kill command.

There aren't any published security advisories