🎁 Lightweight process containers for Linux
C Python
Latest commit 30d3b94 Feb 16, 2017 @ghedo retab

README.rst

pflask

https://travis-ci.org/ghedo/pflask.png

pflask is a simple tool for creating process containers on LInux. It can be used for running single commands or even booting a whole operating system inside an isolated environment, where the filesystem hierarchy, networking, process tree, IPC subsystems and host/domain name can be insulated from the host system and other containers.

Getting Started

pflask doesn't need any configuration and can be run without any arguments as follows:

$ sudo pflask

By default a new container will be created and a bash shell will be started, but a custom command can also be specified:

$ sudo pflask -- id
uid=0(root) gid=0(root) gruppi=0(root)

The container can also be run inside a private root directory by using the --chroot option:

$ sudo pflask --chroot=/path/to/rootfs -- id
uid=0(root) gid=0(root) gruppi=0(root)

This can be used, for example, as a replacement for the chroot(8) command. It's even possible to invoke the init binary and boot the whole operating system inside the container:

$ sudo pflask --chroot=/path/to/rootfs -- /sbin/init

Note that pflask doesn't provide any support for creating the rootfs, but can piggyback on existing tools. For example the debootstrap(8) command can be used for creating a Debian rootfs as follows:

$ sudo debootstrap sid /path/to/rootfs http://httpredir.debian.org/debian

For more information on pflask usage, have a look at the man page.

Networking

Using the --netif option the networking of the container will be disconnected from the host system and all network interfaces will be made unavailable to the container:

$ sudo pflask --netif -- ip link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00

The --netif option can also be used to create private network interfaces:

$ sudo pflask --netif=macvlan:eth0:net0 -- ip link
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
5: net0@if2: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default
    link/ether 92:e4:c2:9b:a4:75 brd ff:ff:ff:ff:ff:ff link-netnsid 0

Interfaces created inside the container will be automatically destroyed once the container terminates.

The command above will create a new macvlan interface called net0, from the eth0 host interface. macvlan interfaces can be used to give an additional MAC address to a network adapter and make it look like a completely different device.

pflask can also create other types of network interfaces, have a look at the manpage for more information.

Filesystem

By default a new mount namespace is created for the container, so that filesystems mounted inside it won't affect the host system. The --mount option can then be used to create new mount points before the execution of the supplied command.

$ sudo pflask --chroot=/path/to/rootfs --mount=bind:/tmp:/tmp

The command above will bind mount the host's /tmp directory into the container's /tmp, so that files can be exchanged between them.

pflask can also create other types of mount points, have a look at the manpage for more information.

Additionally, using the --ephemeral option it's possible to tell pflask to discard any change applied to the root filesystem once the container terminates:

$ sudo pflask --chroot=/path/to/rootfs --ephemeral -- /sbin/init

This can be used for example for a build environment, where dependencies can be installed at every run on a clean rootfs, without the need to recreate the rootfs every time.

Unprivileged containers

All the commands above have been executed with root privileges, but pflask can be invoked, with some limitations, by unprivileged users as well, as long as user namespaces are supported by the host system.

$ pflask --user=$USER -- id
uid=1000(ghedo) gid=1000(ghedo) gruppi=1000(ghedo)

For example, on recent Debian versions user namespaces are enabled, but are restricted to the root user only. To enable them for unprivileged users run:

$ sudo sysctl kernel.unprivileged_userns_clone=1

This functionality can be used to run every-day user applications such as a web browser inside a container:

$ pflask --user=$USER --mount=tmp:$HOME -- chromium --disable-setuid-sandbox

The command above uses the --mount option to create a tmpfs mount point on the $HOME directory, so that the application (chromium in the example) won't be able to access the user's private files, and any modification to the home directory will be discarded once the container terminates.

The --chroot option can be used with unprivileged containers as well, but requires some additional configuration.

The first step is assigning a set of additional UIDs and GIDs to the current user ($USER). These will be used by pflask inside the container:

$ sudo usermod --add-subuids 100000-165535 $USER
$ sudo usermod --add-subgids 100000-165535 $USER

Note that the commands above require root privileges, but have to be run only once.

Then any time an unprivileged chroot(8) is needed, the following command can be run:

$ pflask --user-map=0:100000:65536 --chroot=/path/to/rootfs

Note that the newuidmap(1) and newgidmap(1) commands need to be installed for any of this to work: on Debian/Ubuntu systems they are provided by the uidmap package.

Background containers

Containers can be detached from the current terminal as soon as they are created by using the --detach option:

$ sudo pflask --chroot=/path/to/rootfs --detach

and then later reattached (even to a different terminal) with the --attach option:

$ pidof pflask
29076
$ pflask --attach=29076

Where 29076 is the PID of the detached pflask process. Once reattached, it can be detached again by pressing ^@ (Ctrl + @).

machined integration

Containers created with pflask are automatically registered with the machined daemon, if installed and running. The machinectl(1) command can then be used to list and manipulate running containers.

Let's create one container as follows:

$ sudo pflask --chroot=/path/to/rootfs -- /sbin/init

Running containers can be listed using the list command:

$ machinectl --no-pager list
MACHINE      CLASS     SERVICE
pflask-19170 container pflask

1 machines listed.

and information regarding a single container can be retrieved with the show command:

$ machinectl --no-pager show pflask-19170
Name=pflask-19170
Id=00000000000000000000000000000000
Timestamp=gio 2015-06-25 20:28:34 CEST
TimestampMonotonic=8860409172
Service=pflask
Unit=machine-pflask\x5cx2d19170.scope
Leader=19170
Class=container
RootDirectory=/home/ghedo/local/debian
State=running

Additionally, the status command will show more information regarding the status of the container:

$ machinectl --no-pager status pflask-19170
pflask-19170
        Since: gio 2015-06-25 20:28:34 CEST; 1min 21s ago
       Leader: 19170 (systemd)
      Service: pflask; class container
         Root: /home/ghedo/local/debian
           OS: Debian GNU/Linux stretch/sid
         Unit: machine-pflask\x2d19170.scope
               ├─19170 /lib/systemd/systemd
               └─system.slice
                 ├─systemd-journald.service
                 │ └─19184 /lib/systemd/systemd-journald
                 └─console-getty.service
                   └─19216 /sbin/agetty --noclear --keep-baud console 115200 3...

giu 25 20:28:34 kronk systemd[1]: Started Container pflask-19170.
giu 25 20:28:34 kronk systemd[1]: Starting Container pflask-19170.

One can even log into the container using the login command (note that the dbus daemon needs to be running inside the container for this to work):

$ sudo machinectl login pflask-19170
Connected to machine pflask-19170. Press ^] three times within 1s to exit session.

Debian GNU/Linux stretch/sid kronk pts/0

kronk login:

And finally the container can be terminated using either the poweroff or terminate commands:

$ sudo machinectl poweroff pflask-19170

Building

pflask is distributed as source code. Build with:

$ ./bootstrap.py
$ ./waf configure
$ ./waf build

Copyright

Copyright (C) 2013 Alessandro Ghedini <alessandro@ghedini.me>

See COPYING for the license.