0bf9886164b4 — Alain Leufroy 3 years ago
README: add a readme file with typical use case.
1 files changed, 237 insertions(+), 0 deletions(-)

A => README.md
A => README.md +237 -0
@@ 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
+```
+