(gdb) break *0x972

Debugging, GNU± Linux and WebHosting and ... and ...

Starting systemd-nspawn Container from ArchLinux

I recently disovered systemd-nspawn utility ("chroot on steroids"), and I wanted to share how I set it up on Archlinux. I plan to use it as a sandbox environment, although it's the the recommended usage.

I mainly want to prevent from script-kidding (and accidental) destruction of my environment: instead of running code from the Internet directly on my user session (or event sometimes as root, I must admit), I'll run it inside the container. Hence, the possibilities of an untargeted attack is, I believe, quite limited.

This tutorial is completely inspired from ArchWiki systemd-nspawn.

Also, keep in mind that this is not a script, don't copy and paste it directly, run it step by step and pay attention to what you validate ;-)

Step one: setup a container filesystem

sudo mkdir $CONTAINER -p

On Archlinux, pacstrap can bootstrap a new filesystem architecture. debbootstrap on Debian and dnf on Fedora can certainly do the same.

sudo pacman -S arch-install-scripts

BASE_PACKAGES="base firefox gdb vim sudo"
PKG_TO_EXCLUDE=linux # see the archwiki
sudo pacstrap -i -c -d $CONTAINER $BASE_PACKAGES --ignore $PKG_TO_EXCLUDE

And that's it for a minimal system !

Step two: prepare the container environment

Let's boot it, configure it, then we'll come back on some more host-side configuration.

sudo systemd-nspawn --boot --directory $CONTAINER --network-veth

# systemd boots the system ...
# login as root ...

# get these values on the HOST
UID=$(echo $UID from HOST)
GID=$(echo $GID from HOST)
USER=$(echo $USER from HOST)

# add and configure the container user:
groupadd --gid $GID $USER
useradd --uid $UID --create-home --gid $GID $USER
passwd $USER # ...
visudo # add something like '$USER ALL=(ALL) ALL'

# setup the network DHCP (through the virtal-nic)
systemctl enable systemd-networkd
systemctl start systemd-networkd
networkctl # check that everything is prepared:
# host0            ether              routable    configur[ing|ed]

# setup the DNS resolver
systemctl enable systemd-resolved.service
systemctl start systemd-resolved.service

rm -f /etc/resolv.conf
ln -sf /run/systemd/resolve/resolv.conf /etc/resolv.conf

# and quit (the *right* machine ;-)

Step three: finish the host configuration

We're back on the host for some more configuration:

# just in case
sudo systemctl enable systemd-networkd
sudo systemctl start systemd-networkd
# or
sudo systemctl restart systemd-networkd

# then check:
networkctl status ve-sandbox
# if State: no-carrier (unmanaged)
cp /usr/lib/systemd/network/80-container-ve.network /etc/systemd/network/80-container-ve.network
# and comment: Driver=veth
# elif State: routable (configur[ing|ed])
# we're good !

# enable Linux port forwarding
sudo sysctl -w net.ipv4.ip_forward=1
# and make it permanent across reboots
echo net.ipv4.ip_forward = 1 | sudo tee /etc/sysctl.d/40-portforwarding.conf

We're almost done for the configuration! Now we need to setup the container start and access:

sudo systemctl enable machines.target
sudo systemctl enable systemd-nspawn@$MACHINE.service
sudo systemctl status systemd-nspawn@$MACHINE.service
# it should be up and running

# force nic reconfiguration
# (do it every time you boot a container)
sudo systemctl restart systemd-networkd

Step four: easy access to the container

And now your container is easy to access:

sudo machinectl
sudo machinectl login $MACHINE
sudo machinectl shell $USER@$MACHINE
sudo machinectl status $MACHINE
sudo machinectl poweroff $MACHINE

Bonus step: convenient access to the container

Sharing Xorg

sudo cp /usr/lib/systemd/system/systemd-nspawn@.service /etc/systemd/system/machines.target.wants/systemd-nspawn@$MACHINE.service
# then add --bind /tmp/.X11-unix to the container option
# then (in the container)
echo export DISPLAY=:0 >> ~/.profile

That should be enough to be able to run X application on the sandbox. Keep in mind that sharing Xorg between the two environment flaws the sandboxing, Xorg is old and buggy ...

If you want to share anything else, the option syntax is:

--bind /path/on/host[:/path/on/container]

Passwordless connection

# add to /etc/suders.d/$USER something like
$USER ALL= NOPASSWD: /usr/bin/systemctl restart systemd-networkd
$USER ALL= NOPASSWD: /usr/bin/machinectl shell $USER@$MACHINE*