# HG changeset patch # User Alain Leufroy # Date 1615063278 -3600 # Sat Mar 06 21:41:18 2021 +0100 # Node ID 0bf9886164b4faf447897d027f67c95c20e180e2 # Parent 2af5c44319f69f3f698483b6455261d43fd0f1f0 README: add a readme file with typical use case. diff --git a/README.md b/README.md new file mode 100644 --- /dev/null +++ b/README.md @@ -0,0 +1,237 @@ +# OverlayCtl + +## Overview + +Manage images as OverlayFS point for machinectl using systemd units. + +OverlayFS is a is a union mount filesystem. It combines multiple +different underlying mount points into one, resulting in single +directory structure that contains underlying files and sub-directories +from all sources. + +The idea behind OverlayCtl is quite similar to as +https://github.com/LEW21/siren-sh. + + + +For each overlay it creates new var-lib-machines-{imagename}.mount and +var-lib-machines-{imagename}.automount systemd units, and starts the +second one. Systemd automatically mounts overlayfs at +/var/lib/machines/{imagename} when somebody tries to access it, for +example by starting the container. + +To get around OverlayFS limitation +(https://lists.ubuntu.com/archives/kernel-team/2013-March/025775.html) +we stack the upper dir of lowered overlays instead of their mount point. + +## Use case + +Let's see how we can use it. +systemd-journal-remote +First of all, you'll need a base root point containing a fresh new +system root. Let's say that we want a testing debian to start with. + +```sh +host$ debootstrap --include=systemd-container --include=dbus-user-session --include=systemd-journal-remote --include=libpam-systemd testing /var/lib/machines/debian +``` + +Note: `--include=systemd-container` will help you about host <-> + container communication. + + Due to systemd-nspawn limitations, I recommand to not use `.`, `-`, + `_` in the layer names. + + +The root login with password is disabled by default in Debian, as I'm +writting this documentation. So, we'll create a first layer that allows +us to automatically login as root. + +```sh +host$ overlayctl create autologin +``` + +Don't be afraid, overlayctl will help us fixing this security hole later. + +To enable automatic login as root, create the following file at +`/var/lib/overlayctl/upper/var-lib-machines-autologin/etc/systemd/system/console-getty.service.d/override.conf` + +```sh +host$ mkdir -p /var/lib/overlayctl/upper/var-lib-machines-autologin2/etc/systemd/system/console-getty.service.d/ +host$ cat > /var/lib/overlayctl/upper/var-lib-machines-autologin2/etc/systemd/system/console-getty.service.d/override.conf << OEF +[Service] +ExecStart=-/sbin/agetty --noclear --autologin root --keep-baud console 115200,38400,9600 $TERM +EOF +``` + +Ok, we now will be able to connect to the container system directly as root. + +Now, we'll need networking, in order to install more stuff. We'll +create a layer for that. Note that you may need to use +systemd-networkd and systemd-resolved on your host to apply the following method. + +```sh +host$ overlayctl create network autologin debian +host$ overlayctl start -s network +host$ systemd-nspawn -M network -b -n +network# systemctl enable --now systemd-networkd +network# systemctl enable --now systemd-resolved +network# resolvectl query linuxfoundation.org +network# systemctl halt +``` + +Continue with a layer containing build-essentials allowing us to compile things. + +```sh +host$ overlayctl create buildessential network +host$ overlayctl start -s buildessential +host$ systemd-nspawn -M buildessential -b -n +buildessential# apt install -y build-essential cargo vim +buildessential# systemctl halt +``` + +We now have everything to build our application container. Let say +that our project is a simple rust file in `/home/myproject/hello.rs` +on the host. + +```rust +fn main() { + println!("Hello world!"); +} +``` + +We create a container for our project: + +```sh +host$ overlayctl create myproject buildessential +host$ overlayctl start -s myproject +host$ systemd-nspawn -M myproject -b -n --bind-ro=/home/myproject:/app +myproject# hostnamectl set-hostname myproject +myproject# cd /app +myproject# rustc --out-dir=/usr/local/bin hello.rs +myproject# hello +Hello world! +``` + +Then create a service file to execute our code: + +```sh +myproject# vim /etc/systemd/system/myproject.service + +[Unit] +Description=myproject +[Service] +ExecStart=/usr/local/bin/hello +# Hardening: http://0pointer.de/blog/projects/security.html +ProtectSystem=strict +[Install] +WantedBy=multi-user.target + +myproject# systemctl start myproject.service +myproject# journalctl -e -u myproject.service +App 1 00:00:00 myproject systemd[1]: Started myproject. +App 1 00:00:00 myproject hello[113]: Hello world! +App 1 00:00:00 myproject systemd[1]: myproject.service: Succeeded. +myproject# systemctl halt +``` + +Everything works. We can clean up things for security reason. We do not +need networking, neigher autologin, neither build tools. + +In fact, looking for our overlay, it looks like this: + +```sh +host$ overlayctl list myproject +myproject 🖴 → buildessentials → network, autologin, debian +``` + +So, lets remove buildessential as a lower layer and put debian directly + +``` +host$ overlayctl edit --delete buildessential myproject +host$ overlayctl edit --append debian myproject +host$ overlayctl list myproject +myproject 🖴 → debian +``` + +By this way, root is not logged by default, there are not network nor compilers. + +For convenience, we create a unit file in the host to easily start our +container in `/etc/systemd/nspawn/myproject.nspawn` containing: + +```ini +[Exec] +Boot=True +[Network] +Private=True +``` + +We can now start the container with: + +```sh +host$ systemctl start systemd-nspawn@myproject +``` + +And execute our code: + +```sh +host$ systemctl -M myproject start myproject +host$ journalctl -M myproject -u myproject +App 1 00:00:00 myproject systemd[1]: Started myproject. +App 1 00:00:00 myproject hello[113]: Hello world! +App 1 00:00:00 myproject systemd[1]: myproject.service: Succeeded. +App 1 00:02:00 myproject systemd[1]: Started myproject. +App 1 00:02:00 myproject hello[113]: Hello world! +App 1 00:02:00 myproject systemd[1]: myproject.service: Succeeded. +``` + +Now let say we need to update our project code. We need the rst +compiler. But we do not need networking anymore. We can cleanup +overlay dependencies: + +```sh +host$ overlayctl list +autologin → +network → autologin, debian +buildessential → networking ⇢ autologin, debian +myproject → debian +host$ overlayctl edit networking -d autologin -d debian +host$ overlayctl edit buildessential -d network +host$ overlayctl list +autologin → +network → +buildessential → +myproject → debian +``` + +We now "activate" things independently. + +```sh +host$ systemctl stop systemd-nspawn@myproject +host$ overlayctl edit myproject +# -*- encoding: utf-8 -*- +# Note: Leave unchanged or empty to ignore changes +buildessential +autologin # <- Remove this line +network # <- Remove this line +debian +host$ overlayctl list myproject +myproject → buildessential, debian +``` + +And update the project: + +```sh +host$ systemd-nspawn -M myproject -b --bind-ro=/home/myproject:/app +myproject# cd /app +myproject# rustc --out-dir=/usr/local/bin hello.rs +myproject# systemctl halt +``` + +Then clean up the overlay and restart the container. + +``` +host$ overlayctl edit -d autologin -d +host$ systemctl start systemd-nspawn@myproject +host$ systemctl -M myproject start myproject +``` +