Flatpak – a technical walk-through

Alexander Larsson, Red Hat

What is Flatpak?

“apps” for the Desktop Distribute your app Run it anywhere Build in anywhere Run it sandboxed How is this different from containers? Non-privileged user

● Must not require root permissions ● Must not grant root permissions ● No root-only features Desktop integration

● Icon shows up in desktop ● Automatic setup of X11/Wayland/Pulseaudio ● DBus integration ● OpenGL / DRI support ● Uses freedesktop specs ● Interactive permissions system Building blocks

● OSTree ● Bubblewrap OSTree

“its like git for operating systems” Repo layout app $ cat app/README ├── bin This is some example content │ └── hello.sh └── README Repo layout app $ cat app/README ├── bin This is some example content │ └── hello.sh └── README $ ostree --repo=repo init $ ostree --repo=repo commit \ --subject="Foobar" \ --branch=master app 8528ed0cee

$ ostree --repo=repo show master commit 8528ed0cee Date: 2018-08-22 14:18:11 +0000 Foobar Repo layout app $ cat app/README ├── bin This is some example content │ └── hello.sh └── README $ cat repo/refs/heads/master repo/ 8528ed0cee ├── config $ cat repo/objects/80/04dd449f.file ├── objects This is some example content │ ├── 94/b60b8c7a.dirtree │ ├── f2/13a94e40.dirtree │ ├── 30/d1d158ec.file │ ├── 80/04dd449f.file │ ├── 85/28ed0cee.commit │ └── d9/f4b64256.dirmeta └── refs/heads/master Repo layout app $ cat app/README ├── bin This is some example content │ └── hello.sh └── README $ cat repo/refs/heads/master repo/ 8528ed0cee ├── config $ cat repo/objects/80/04dd449f.file ├── objects This is some example content │ ├── 94/b60b8c7a.dirtree │ ├── f2/13a94e40.dirtree │ ├── 30/d1d158ec.file │ ├── 80/04dd449f.file │ ├── 85/28ed0cee.commit $ echo -n "..." >> app/README │ └── d9/f4b64256.dirmeta $ ostree --repo=repo commit \ └── refs/heads/master --subject=”Changed stuff” --branch=master app 2e9472ca3f Repo layout app $ cat app/README ├── bin This is some example content │ └── hello.sh └── README $ cat repo/refs/heads/master repo/ 2e9472ca3f ├── config $ cat repo/objects/80/04dd449f.file ├── objects This is some example content │ ├── 94/b60b8c7a.dirtree │ ├── c6/171320dd.dirtree $ cat repo/objects/67/a1a77c36.file │ ├── f2/13a94e40.dirtree This is some example content... │ ├── 30/d1d158ec.file │ ├── 80/04dd449f.file │ ├── 67/a1a77c36.file │ ├── 85/28ed0cee.commit │ ├── 2e/9472ca3f.commit │ └── d9/f4b64256.dirmeta └── refs/heads/master Checkout repo/ ├── config ├── objects │ ├── 94/b60b8c7a.dirtree │ ├── c6/171320dd.dirtree │ ├── f2/13a94e40.dirtree │ ├── 30/d1d158ec.file │ ├── 80/04dd449f.file │ ├── 67/a1a77c36.file │ ├── 85/28ed0cee.commit │ ├── 2e/9472ca3f.commit │ └── d9/f4b64256.dirmeta └── refs/heads/master Checkout repo/ $ ostree --repo=repo checkout \ ├── config master new ├── objects │ ├── 94/b60b8c7a.dirtree │ ├── c6/171320dd.dirtree │ ├── f2/13a94e40.dirtree │ ├── 30/d1d158ec.file │ ├── 80/04dd449f.file │ ├── 67/a1a77c36.file │ ├── 85/28ed0cee.commit │ ├── 2e/9472ca3f.commit │ └── d9/f4b64256.dirmeta └── refs/heads/master Checkout repo/ $ ls -lR new ├── config new: ├── objects drwxr-xr-x. 2 alex alex 60 bin │ ├── 94/b60b8c7a.dirtree -rw-r--r--. 2 alex alex 19 README │ ├── c6/171320dd.dirtree new/bin: │ ├── f2/13a94e40.dirtree -rw-r--r--. 2 alex alex 38 hello.sh │ ├── 30/d1d158ec.file $ cat app/README │ ├── 80/04dd449f.file This is some example content... │ ├── 67/a1a77c36.file │ ├── 85/28ed0cee.commit │ ├── 2e/9472ca3f.commit │ └── d9/f4b64256.dirmeta └── refs/heads/master new ├── bin │ └── hello.sh └── README Checkout repo/ $ ls -lR new ├── config new: ├── objects drwxr-xr-x. 2 alex alex 60 bin │ ├── 94/b60b8c7a.dirtree -rw-r--r--. 2 alex alex 19 README │ ├── c6/171320dd.dirtree new/bin: │ ├── f2/13a94e40.dirtree -rw-r--r--. 2 alex alex 38 hello.sh │ ├── 30/d1d158ec.file $ cat new/README │ ├── 80/04dd449f.file This is some example content... │ ├── 67/a1a77c36.file │ ├── 85/28ed0cee.commit │ ├── 2e/9472ca3f.commit │ └── d9/f4b64256.dirmeta $ stat new/README | grep Inode └── refs/heads/master Device: 2fh/47d Inode: 276193 Links: 2 new $ stat repo/objects/67/a1a77c36.file \ | grep Inode ├── bin Device: 2fh/47d Inode: 276193 Links: 2 │ └── hello.sh └── README Hosting a repo

● Repo mode “archive-z2”

├── config ├── objects │ ├── 35/b2a1ffa5.dirtree │ ├── 67/a1a77c36.filez │ ├── 80/04dd449f.filez │ ├── c6/171320dd.dirtree │ ├── d9/f4b64256.dirmeta │ ├── db/92a318a1.commit │ └── db/92a318a1.commitmeta ├── refs/heads/master ├── summary.sig └── summary ● Host on HTTP server ● GPG sign commits ● Static deltas OSTree advantages

● Automatic deduplication ● Disk ● RAM ● Efficient updates ● Atomic updates ● No privileges needed ● No kernel requirements bubblewrap

“unprivileged chroot on steroids” The virtual filesystem

A a1 a1 FS Root Mount point B b1 A / / a2 c1 b1 B / /a1 a2 C a3 C / /a1/b1 foo a3 c1 C /c1 /a1/b2 c2 b2 c foo b3 b2 c3 c2

foo c3

b3 chroot

A a1 FS Root Mount point B b1 A / / b1 c1 a2 C B / /a1 C / /a1/b1 foo a3 c1 c2 b2 c C /c1 /a1/b2 foo b3 Process root b2 c3 c2 /a1 foo c3

b3 namespaces

A a1 FS Root Mount point B b1 A / / b1 c1 a2 C B / /a1 C / /a1/b1 foo a3 c1 c2 b2 c C /c1 /a1/b2 foo b3 Process root b2 c3 c2 /a1 foo c3 Process mount table b3 What is wrong with chroot?

● Must be root ● Why? Unprivileged user namespaces

● prctl(PR_SET_NO_NEW_PRIVS) ● User mappings bubblewrap

● Simple tool to use user namespaces ● Starts with / as empty tmpfs ● Add bindmounts and dirs/files/symlinks

$ bwrap --dir /tmp --ro-bind /usr /usr --symlink usr/lib64 /lib64 /usr/bin/ls / lib64 tmp usr Other features

● Namspaces: net, ipc, pid, uts, cgroup ● Filesystems: tmpfs, proc, dev ● Pid 1 handler ● Setuid version

● For distros that disable user namespaces Flatpak – 10000 feet view

● Each installation has an OSTree repo ● Apps and runtimes are branches – app/org.gnome.gedit/x86_64/stable – runtime/org.gnome.Platform/x86_64/3.28 ● Checked out (deployed) next to repo ● Run with bubblewrap bwrap --ro-bind $app /app --ro-bind $runtime /usr ... Deployed app $ cd /var/lib/flatpak $ ls -l app/org.gnome.gedit/x86_64/stable drwxr-xr-x. 4 alex wheel 63 849ce9102a... lrwxrwxrwx. 1 alex wheel 64 active -> 849ce9102a.. $ ls -l app/org.gnome.gedit/x86_64/stable/active/ -rw-r--r--. 2 alex alex 966 metadata drwxr-xr-x. 6 alex alex 89 files drwxr-xr-x. 4 alex alex 30 export -rw-r--r--. 1 alex alex 134 deploy $ ls -la app/org.gnome.gedit/x86_64/stable/active/files/ drwxr-xr-x. 2 alex alex 56 bin drwxr-xr-x. 7 alex alex 4096 lib drwxr-xr-x. 17 alex alex 238 share -rw-r--r--. 1 alex alex 0 .ref flatpak install flathub org.gnome.gedit

● ostree pull flathub $REF ● COMMIT=$(ostree rev-parse flathub:$REF) ● ostree checkout --branch $REF $REF/$COMMIT ● create $REF/$COMMIT/{deploy, files/.ref} ● syncfs() ● ln -sf $COMMIT $REF/active ● process $REF/$COMMIT/export into exports/ flatpak update org.gnome.gedit

● Same, but after: – mv $REF/$OLDCOMMIT .removed – for D in .removed/*; do if flock --exclusive --nonblock $D/.ref; then rm -rf $D; fi done Flatpak sandbox

● Namespaces: user, fs, pid, ● Optional namespaces: net, ipc ● Seccomp – Disable weird network types – no ptrace, perf, personality, multiarch, keyring, weird syscalls – No recursive namespaces ● Per-app /tmp ● Runs in transient --user scope ● Filtering DBus proxy ● Writable per-app storage in ~/.var/app/$APPID Questions

● Links: – https://flatpak.org/ – https://github.com/flatpak/flatpak/ – https://github.com/ostreedev/ostree – https://github.com/projectatomic/bubblewrap/

● Reaching us – [email protected][email protected] – #flatpak on freenode