The 66-tools Suite: 66-ns
Author: Eric Vidal <eric@obarun.org>

66-tools

Software

obarun.org

# 66-ns

66-ns(namespace) setup a namespace and execs a program inside it.

## Interface

        66-ns [ -h ] [ -z ] [ -v verbosity ] [ -d notif ] [ -o ns_options,... ] [ -e element:type:options:,... ] [ -r rule ] prog

66-ns sets up a namespace(sandbox) according to the options passed at the command line or/and by a rule file. 66-ns allows to have a fine grain of the namespace configuration and permit to limit the exposure of the system from prog processes. This tool can be used to start a service inside a namespace or directly from a terminal.

The host file system is mounted recursively by default such as if you don’t pass any options to 66-ns the result is a strict copy of the host.

With a good configuration—see Usage examples, it allows to survey a daemon which forks itself, thus multi-processes which fork themselves.

66-ns can only by launched as root user.

## Exit codes

- 0 success
- 100 wrong usage
- 111 system call failed or
- exit status code of prog

## Options

- -h : prints this help.

- -z : enable color. If 66-ns is not launched from a terminal, the color is automatically disabled and the option has no effect.

- -v verbosity : increases/decreases the verbosity of the command.

  - 1 : only print error messages. This is the default.
  - 2 : also print warning messages.
  - 3 : also print tracing messages.
  - 4 : also print debugging messages.

- -d notif : notify readiness on file descriptor notif. If 66-ns is launched from a terminal, the option has no effect. The notification happens right before the launch of prog. This guarantees you that the namespace is completely configured and ready to use. This notification does not guarantee you that prog was started successfully. This is important to keep in mind mostly when you use 66-ns for a service supervision.

- -o ns_options: comma separated list of namespace options—see Namespace options.

- -e element : comma separated list of elements element to handle—see Element options. This options can be passed multiple times. This option precede the same element found at a rule file. element can be a directory, a file, a fifo, a symlink, a socket, a character special file or a block special file. The location of the element must be an absolute path.

- -r rule : name of the rule file rule to apply—see Rule file. This option can be passed multiple times.

## Namespace options

The -o option can be:

- flag= flag : where flag can be private, slave, unbindable and shared which corresponds to `MS_PRIVATE`, `MS_SLAVE`, `MS_UNBINDABLE` or `MS_SHARED` respectively—see mount(2). The special term all can also be passed to set all flag in one pass. If this option is not specified, `MS_SHARED` will be the default. This flag only controls the final mount point propagation of the file system of the namespace.

- unshare= flag : where flag can be pid, net, ipc, uts or cgroup which corresponds to `CLONE_NEWPID`, `CLONE_NEWNET`, `CLONE_NEWIPC`, `CLONE_NEWIPC`, `CLONE_NEWUTS`, `CLONE_NEWCGROUP` respectively—see unshare(2). The special term all can also be passed to set all flag in one pass. Also, The `CLONE_NEWNS` flag is implied by default.

- nonewprivileges : this ensures that the prog and all its children can never gain new privileges through execve(). So, the prog and its children can never elevate privileges by e.g setuid, setgid bits or filesystem capabilities.

- hostname= hostname : set the hostname namespace with hostname. This implies the unshare=uts option automatically.

## Element options

An element is set by a series of options separated by colon. The first option must be the name of the element to handle and must be an absolute path. Other options have no order of preference. let’s see an example before going forward:

        66-ns -e /etc:options=ro -e /etc/resolv.conf:type=clone:options=rw -e /dev:type=tmpfs:options=nosuid,strictatime,noexec,mode=755,size=4m,nr_inodes=64k -e /proc:type=proc -e /sys:type=recursive:options=ro sh -i

Options can be:

- element name : name of the element to handle. This option is mandatory. Again, this option must be set first and must be an absolute path.

- target= target : target of the element inside the namespace. If not set, the target is the same as the element path. target must be an absolute path.

- type= type : where type can be tmpfs, hidden, recursive, clone, proc, dev or sys—see Type specification. If type= is not set, the `MS_BIND`(bind) kernel flag is implied by default.

- options= options : comma separated list of filesystem-independent mount options—see mount(8). Note that the `remount` and `move` mount option is ignored.

- create yes|no : boolean argument. If no and the target of the element doesn’t exist, 66-ns stops the process and exit 111. If yes and the target of the element doesn’t exist, 66-ns tries to create it. If the creation of the target of the element fails, it exits 111. Default yes.

- ignore yes|no : boolean argument. If yes and element doesn’t exist, ignore the element and continue to setup the namespace. If no and element doesn’t exist, it exits 111. Default no.

### Type specifications

- tmpfs : mount element as type tmpfs . it ignores the MS_bind(bind) and MS_REC(rbind) kernel flags past at options= option. If element inside the namespace is already a mount point, it unmount first recursively the mount point. prog cannot see any files or directories beyond element.

- hidden : element will be made inaccessible for processes inside the namespace along with everything below them in the file system hierarchy. If element inside the namespace is already a mount point, it unmount first recursively the mount point. The final element will be mounted read-only. The `MS_BIND`(bind), `MS_RDONLY`(ro), `MS_NOSUID`(nosuid) and `MS_NODEV`(nodev) kernel flags is automatically added at the mount() command. It’s not possible to nest other element inside it. For example, setting /etc as element as type=hidden blocks a request to /etc/resolv.conf as element to handle.

- recursive : if element inside the namespace is already a mount point, it unmount first recursively the mount point and mount it with the options passed at options=. The `MS_BIND`(bind) and `MS_REC`(rbind) kernel flags is automatically added at the mount() command.

- clone : it tries to clone the element inside the namespace. For example if element is a symlink, it tries to create that symlink inside the namespace. It’s not possible to use this type if element is a mount point. The purpose of this type option is to create the element inside the namespace regarless if the element is a directory, a file, a fifo, a symlink, a socket, a character special file, or a block special file. The option options= is completely ignored.

- proc : mounts a new proc virtual file system inside the namespace instead of bind mount it from the host. If element inside the namespace is already a mount point, it unmount first recursively the mount point. This type can only by used for an element pointing to “/proc”. For example setting:

          66-ns -e /dev:type=proc sh -i

  produce an error whereas:

          66-ns -e /proc:type=proc dhcpcd

  setup a new proc virtual file system. It mounts the “/proc” mount point with the `MS_NOSUID`(nosuid), `MS_NODEV`(nodev) and `MS_NOEXEC`(noexec) kernel flag. The options= options are completely ignored.

- dev : mounts a new dev virtual file system inside the namespace instead of bind mount it from host. If element inside the namespace is already a mount point, it unmount first recursively the mount point. This type can only by used for an element pointing to “/dev”. It mounts the “/dev” mount point with `MS_NOSUID`(nosuid), `MS_STRICTATIME`(strictatime), `MS_NOEXEC`(noexec) and `MS_REC`(rbind) kernel flag and mode=755 mount option. The options= options is completely ignored. It only mounts the “/dev” mountpoint. That means that every sub-mount e.g. “/dev/pts” is not be handled.

- sys : mounts a new sys virtual file system inside the namespace instead of bind mount it from the host. If element inside the namespace is already a mount point, it unmount first recursively the mount point. This type can only by used for an element pointing to “/sys”. It mounts the “/sys” mount point with `MS_NOSUID`(nosuid), `MS_STRICTATIME`(strictatime), `MS_NOEXEC`(noexec). The options= options is completely ignored. It only mounts the “/sys” mountpoint. That means that every sub-mount e.g. “/sys/kernel/config” is not be handled.

## Rule file

It can be really tedious, repetitive and unreadable to setup a namespace uniquely with the -e option e.g:

        66-ns -e /dev:type=tmpfs:options=nosuid,strictatime,noexec,mode=755,size=4m,nr_inodes=64k -e /dev/pts:options=rw: -e /dev/shm:options=rw -e /dev/hugepages:options=rw -e /dev/mqueue:options=rw -e /dev/ptmx:type=clone sh -i

The rule file solves this.

### General rule file syntax

The rule file has the format INI. To reduce the complexity of the parse process, comments are not allowed where empty an line exists. You can set the rule file name as you want as long as the name is suffixed with .rule.

### Rule file invocation

The invocation of a rule file can be done by relative, absolute path or directly by its name. In case of direct name invocation, the file need to be place at %%ns_rule%% directory. The default path can be changed at compile time by passing the `--with-ns-rule=DIR` at `./configure`.

### Rule file syntax

The file is made of sections which can contain one or more `key value` pairs where the key name corresponds to the element option name and value to the option itself. The name of the section corresponds to the element name to handle.

The `examples/rule` subdirectory of the 66-tools package contains a set of rules, which is actually a working, valid set for setup a namespace.

A special section named `[include]` allows to include another rule file in that rule file e.g.:

    [include]
    system-strict.rule
    /usr/lib/66/script/ns/private-dev.rule

    [/etc/resolv.conf]
    options=rw

Each rule name set at `[include]` section is parsed and applied in conjunction to the definition of the `/etc/resolv.conf` element.

### Translation format between rule file and -e option

For example, this command line invocation:

        66-ns -e /dev:type=tmpfs:options=nosuid,strictatime,noexec,mode=755,size=4m,nr_inodes=64k -e /dev/pts:options=rw: -e /dev/shm:options=rw -e /dev/hugepages:options=rw -e /dev/mqueue:options=rw -e /dev/ptmx:type=clone sh -i

can be made by the following invocation:

        66-ns -r dev-example.rule sh -i

where dev-example.rule contain:

    [/dev]
    type=tmpfs
    options=nosuid,strictatime,noexec,mode=755,size=4m,nr_inodes=64k

    [/dev/pts]
    options=rw

    [/dev/shm]
    options=rw

    [/dev/hugepages]
    options=rw

    [/dev/mqueue]
    options=rw

    [/dev/ptmx]
    type=clone

## Usage examples

Mounts `/` as read-only file system:

        66-ns -e /:options=ro sh -i

Mounts `/` as read-only except `/dev` which is recursively mounted read-write:

        66-ns -e /:options=ro -e /dev:type=recursive sh -i

Avoid access to the `/usr/lib/modules` directory:

        66-ns -e /usr/lib/modules:type=hidden sh -i

Prog cannot gain new privileges, mounts a new “/proc” virtual file system and makes `/etc` read-only:

        66-ns -o nonewprivileges, -e /proc:type=proc -e /etc:options=ro sh -i

Mount `/etc` with `tmpfs` type and give access uniquely to /etc/resolv.conf file and /etc/ssh directory which is read-only:

        66-ns -e /etc:type=tmpfs -e /etc/resolv.conf:type=clone /etc/ssh:options=bind,ro sh -i

Invoke a rule file:

        66-ns -e -r myrule sh -i

Invoke 66-ns inside a frontend file to keep the control of dhcpcd which forks itself:

    [main]
    @type = longrun
    @version = 0.1.1
    @description = "Keep control on dhcpcd daemon which forks itself"
    @user = ( root )
    @options = ( log )

    [start]
    @execute = ( 66-ns -o unshare=pid dhcpcd )

Hide the all processes from the host and run a trivial PID1 inside the namespace(66-ns will be the PID1 and sh the PID2):

        66-ns -o unshare=pid -e /proc:type=proc sh -i