pflask is a simple tool for creating Linux namespace containers. It can be used for running a command or even booting an OS inside an isolated container, created with the help of Linux namespaces. It is similar in functionality to chroot(8), although pflask provides better isolation thanks to the use of namespaces.
Compared to LXC, pflask is easier to use since it doesn't require any pre-configuration (all the options can be passed via the command-line). pflask is mostly intended for testing, building and experimenting, whereas LXC is a more complete solution, better suited for production environments.
Compared to systemd-nspawn, pflask doesn't require the use of systemd on the host system and provides additional options for manipulating mount points and network interfaces inside the container. On the other hand, systemd-nspawn is better integrated in the systemd ecosystem.
Additionally, while most other containerization solutions (LXC, systemd-nspawn, ...) are mostly targeted at containing whole systems, pflask can also be used to contain single programs, without the need to create ad-hoc chroots.
When the host system allows it, pflask creates a new user namespace inside the container, and automatically maps the user running pflask to the root user inside the container. This means that a user could create and have full root privileges inside a container, while having none on the host system.
Note that this has been the cause of security vulnerabilities in the past, so that most OS vendors (reasonably) decided to either disable user namespace support altogether, or restrict the functionality to root.
pflask can disable the relevant functionality when it detects that support for user namespaces is not available.
By default, pflask creates a new mount namespace inside the container, so that filesystems mounted inside it won't affect the host system. pflask can also be told to create new mount points before the execution of the supplied command, by using the --mount option. Supported mount point types are:
- bind -- bind mount a directory/file to another directory/file
- bind-ro -- like bind, but read-only
- overlay -- stack a directory on top of another directory using AuFS or OVL
- tmp -- mount a tmpfs on a directory
When supplied the --netif option, pflask will create a new network namespace and move/rename the supplied network interface inside the container.
By default, pflask creates new PID, IPC and UTS namespaces inside the container, in order to isolate processes, IPC resources and the node/domain name of the container from the host system.
$ pflask --user=$USER --mount=tmp,$HOME chromium --disable-setuid-sandbox
This command does not require a pre-generated chroot (it will use the current root) and will mount a tmpfs on $HOME so that the application (chromium in the example) won't be able to access your precious files. Any change will be also discarded once the process terminates. A bind mount can be used to retain the modifications:
$ pflask --user=$USER --mount=bind,/tmp/trash,$HOME chromium --disable-setuid-sandbox
All filesystem changes applied by the command will be available in /tmp/trash.
Both commands can be run without root privileges as long as user namespaces are supported by the host system, and available to non-privileged users.
For example, on Debian, user namespaces are enabled, but are restricted to root only. To enable them for unprivileged users run:
$ sudo sysctl kernel.unprivileged_userns_clone=1
$ pflask --user=$USER --detach /bin/bash
To reattach run pflask with the --attach option:
$ pidof pflask
29076
$ pflask --attach=29076
Where 29076 is the PID of the detached pflask process. Once reattached, one can detach again by pressing ^@ (Ctrl + @).
First create a base Debian system using debootstrap(8):
$ sudo debootstrap --include=systemd unstable /path/to/container
It is recommended to use systemd as init system inside the guest, since it can detect whether it is run inside a container or not, and disable not needed services accordingly.
Then create the container:
$ sudo pflask --chroot=/path/to/container /lib/systemd/systemd
This will simply execute the init system (systemd) inside the container. Replace /lib/systemd/systemd with /sbin/init if you have a different init (but note that there's no guarantee that it'll work).
By using the --volatile option it's possible to tell pflask to discard any change applied to the / directory once the container exits:
$ sudo pflask --chroot=/path/to/container --volatile /lib/systemd/systemd
The above will overlay a tmpfs on top of the chroot /path/to/container directory, so that the root filesystem will be writable from inside the container, but any change will be disacarded once the container terminates.
This can be useful as a build environment, where dependencies can be installed at every build run on a clean chroot, without the need to recreate the chroot at every run.
$ sudo pflask --chroot=/path/to/container --netif /lib/systemd/systemd
Using the --netif option without any argument creates a new network namespace inside the container without adding any new interface, therefore leaving the _lo_ interface as the only one inside the container and disabling network access to the outside world while at the same time leaving the network on the host system working.
First, let's create the new network interface thet will be used inside the container:
$ sudo ip link add name pflask-vlan0 link eth0 type macvlan
This will create a new interface, pflask-vlan0, of type macvlan using the eth0 interface on the host as master. macvlan interfaces can be used to give a second MAC address to a network adapter (in this case eth0) and make it look like a completely different device.
Finally, create the container:
$ sudo pflask --chroot=/path/to/container --netif=pflask-vlan0,eth0 /lib/systemd/systemd
This will take the pflask-vlan0 interface previously created, move it inside the container and rename it to eth0. The container will thus have what it looks like a private eth0 network interface that can be configured independently from the host eth0. Once the container terminates, its network interface will be destroyed as well.
Note that macvlan is just one of the possibilities. One could create a pair of veth interfaces, move one of them inside the container and connect the other to a bridge (e.g. an Open VSwitch bridge). Alternatively one could create a vxlan interface and connect the container to a VXLAN network, etc...
$ sudo pflask --chroot=/path/to/container \
--mount=overlay,/tmp/overlay/root,/path/to/container,/tmp/overlay/work \
/lib/systemd/systemd
This will mount a copy-on-write filesystem on the / of the container. Any change to files and directories will be saved in /tmp/overlay so that the container root directory (/path/to/container) will be unaffected.
Note that this requires support for either AuFS or OverlayFS on the host system.
First, create the base Debian system:
$ sudo mkdir -p /var/cache/pflask
$ DIST=sid pflask-debuild --create
Then retrieve the source package we want to build:
$ apt-get source somepackage
$ cd somepackage-XYX
Where _somepackage_ is the desired package, and _XYZ_ is the package version.
Finally build the package:
$ DIST=sid pflask-debuild
The script will take care of creating a new container, installing all the required dependncies (inside the container), building and signing the package.
A copy-on-write filesystem is also mounted on the / of the container, so that the same clean chroot can be re-used to build other packages.
Note that the pflask-debuild tool is far from perfect, and may not work in all situations.
See the man page for more information.
pflask is distributed as source code. Build with:
$ ./bootstrap.py
$ ./waf configure
$ ./waf build
Copyright (C) 2013 Alessandro Ghedini <[email protected]>
See COPYING for the license.