containers all the way down —

Containerize all the things with Ubuntu Core 20

What if everything—and we mean everything—was a container?

You might draw a fairly similar schematic diagram to give someone a simplified idea of how a traditional Linux distribution is put together—but it wouldn't be as close to literal accuracy as this Ubuntu Core diagram is.
Enlarge / You might draw a fairly similar schematic diagram to give someone a simplified idea of how a traditional Linux distribution is put together—but it wouldn't be as close to literal accuracy as this Ubuntu Core diagram is.

Canonical released Ubuntu Core 20 today, and it is now available for download. If you're already familiar with Ubuntu Core, the standout new feature is added device security with secure boot, full-disk encryption, and secure device recovery baked in. If you're not familiar with Ubuntu Core yet... read on!

The key difference between regular Ubuntu and Ubuntu Core is the underlying architecture of the system. Traditional Linux distributions rely mostly on traditional package systems—deb, in Ubuntu's case—while Ubuntu Core relies almost entirely on Canonical's relatively new snap package format.

Ubuntu Core also gets a full 10 years of support from Canonical rather than the five years traditional Ubuntu LTS releases get. But it's a bit more difficult to get started with, since you need an Ubuntu SSO account to even log in to a new Ubuntu Core installation in the first place.

Before we talk about why getting started might be worth that extra roadblock, we need to do a little homework—so let's get up to speed on Ubuntu packaging systems first.

What’s an apt, and what’s a deb?

When using the apt package manager to install a piece of software—such as the audacity audio editing program—you need to fetch and install not only audacity itself, but also all of its dependencies. In Audacity's case, that would include libasound2, libavcodec57, and 31 other packages.

If you already have libasound2 and libavcodec57 installed, apt doesn't need or want to reinstall them, and it doesn't want to install separate copies just for audacity's sake—these libraries are all installed systemwide, and any .deb package which depends on them uses that system-wide installation. This cuts down on the amount of disk space used—since you won't need, for example, a hundred separate copies of libc6—and it also ensures that any vulnerability only needs to be patched once. If you update libc6 or libasound2, you don't just update them for audacity, you update them for all installed packages at once.

Once you've fetched all 34 of the deb packages comprising Audacity and its dependencies, they're extracted into lots of files and folders, which are distributed into the correct places around your machine's filesystem, in /usr, /var, /etc, and so forth.

Let’s talk about snaps

But Ubuntu also has a newer and decidedly different package management system available, called snap. If we were to install audacity from a snap, we'd only need to fetch a single file—because each snap is an entirely self-contained system. At its heart, a snap is a compressed, read-only squashfs filesystem; the snap contains both the actual package desired as well as all its dependencies, and they actually run directly from the squashfs file itself—unlike debs, snaps aren't expanded and distributed throughout the filesystem.

When you "install" a snap, you aren't actually doing a whole lot—the single file you downloaded gets dumped into /var/lib/snapd/snaps, and a system daemon automatically mounts those snaps—which, remember, are really squashfs filesystems—under /snap as needed. For example, if we sudo snap install audacity, we can see the following:

me@banshee:~$ ls -lh /var/lib/snapd/snaps | grep audacity
-rw------- 2 root root 118M Feb 2 14:46 audacity_748.snap

me@banshee:~$ mount | grep audacity
/var/lib/snapd/snaps/audacity_748.snap on /snap/audacity/748 type squashfs (ro,nodev,relatime,x-gdu.hide)
nsfs on /run/snapd/ns/audacity.mnt type nsfs (rw)

me@banshee:~$ ls -l /snap/audacity
total 0
drwxr-xr-x 9 root root 127 Dec 1 08:57 748
lrwxrwxrwx 1 root root 3 Feb 2 14:46 current -> 748

me@banshee:~$ ls /snap/audacity/current/
bin etc flavor-select lib meta snap usr var

me@banshee:~$ find /snap/audacity -name libasound2
/snap/audacity/748/usr/share/doc/libasound2

So the actual snap we downloaded is audacity_748.snap. It's mounted at /snap/audacity/748, and the contents of that mountpoint essentially look like a nearly entire miniature Linux filesystem. Within that filesystem-in-a-can, we can also see Audacity's dependencies, such as libasound2—with the obvious implication that a different snap which also depended on libasound2 would have its own individual copy rather than sharing Audacity's.

You may have also noticed the nsfs filesystem at /run/snapd/ns/audacity.mnt. This is a byproduct of the fact that snaps aren't just containerized in the sense of their individual squashfs filesystems—their code is also executed inside Linux containers, which minimizes their ability to interact (potentially harmfully) with other running processes they have no business touching.

Putting the Core in Ubuntu Core

Now that we're thoroughly grounded in deb and snap packaging, we can talk about what makes Ubuntu Core different from Ubuntu Desktop or Ubuntu Server. In traditional Ubuntu, the vast majority of the system consists of standard deb packages installed in the root filesystem; while snaps are available, they're a bit of an afterthought.

In Ubuntu Core, that relationship is turned on its head—to paraphrase the late, great Terry Pratchett, "it's snaps all the way down." Even the root filesystem itself is actually a snap and can be upgraded, sidegraded, or downgraded like any optional package would be on a traditional Linux distribution.

This approach brings some significant advantages—since everything's a snap, everything runs in a container, and (at least theoretically) maximally isolated from everything it has no business touching. Since the default is full containerization, devs have to go out of their way to breach the containers when they need to—in sharp contrast to the traditional Linux way, in which everything gets to touch everything else unless you add SELinux or AppArmor protection layers.

With every package on the system—and the root filesystem itself—in individual read-only squashfs packages, it becomes much more difficult to compromise the system without leaving an incredibly obvious trail of breadcrumbs behind you. You can't just overwrite the binary for a system library with a malicious version—you need to replace an actual package. And when you do replace it, it won't be properly signed by the correct vendor... which, itself, gets verified from another immutable snap, which should be signed by Canonical.

snapd also makes maintaining multiple versions of the same package on a system considerably easier. You probably noticed in the last section that audacity wasn't just mounted at /snap/audacity—it was actually mounted at /snap/audacity/748, with a symlink from /snap/audacity/current to /snap/audacity/748. If we wanted to test a different version of Audacity, we could install it without disturbing our existing version—and we could change which version we wanted to run in practice very simply, using snap channels and tracking.

Let's take a look at the info for our Audacity snap, which shows us its available channels:

me@banshee:~$ sudo snap info audacity
name: audacity
summary: Audio software for multi-track recording and editing
publisher: Daniel Llewellyn (diddledan)
store-url: https://snapcraft.io/audacity
contact: https://github.com/diddlesnaps/audacity/issues
license: GPL-2.0+
description: |
Audacity® is a free, easy-to-use, multi-track audio editor and recorder for Windows, Mac OS X,
GNU/Linux and other operating systems. The interface is translated into many languages.

You can use Audacity to:
* Record live audio
* Convert tapes and records into digital recordings or CDs
* Edit WAV, AIFF, FLAC, MP2, MP3 or Ogg Vorbis sound files
* Cut, copy, splice or mix sounds together
* Change the speed or pitch of a recording
* Apply a wide range of other effects to audio recordings

Upstream Project: https://www.audacityteam.org/
snapcraft.yaml Build Definition:
https://github.com/diddlesnaps/audacity/blob/master/snap/snapcraft.yaml
snap-id: KTe2wdAu5JKdRDUgYBuXXGjDXyzobvFI
channels:
latest/stable: 2.4.2 2020-12-10 (748) 123MB -
latest/candidate: 2.4.2 2020-12-13 (756) 123MB -
latest/beta: ↑
latest/edge: 2.4.2 2021-02-01 (779) 195MB -
installed: 2.4.2 (748) 123MB -

Since we didn't specify a channel when we installed Audacity, we installed the latest/stable channel by default—#748, which weighed in at 123MiB. What if we want to try out the latest/edge version instead?

me@banshee:~$ sudo snap refresh audacity --channel=latest/edge
audacity (edge) 2.4.2 from Daniel Llewellyn (diddledan) refreshed

That's all it takes—if you haven't already installed latest/edge, it's downloaded for you. If you want to switch back, a simple sudo snap refresh audacity --channel=latest/stable will do it for you—with no additional downloads needed, since the original version you downloaded is actually still there.

Taking this approach to every part of a Linux system means getting an unprecedented amount of modularity, with the ability to rapidly and reliably swap versions of every piece of the system back, forward, and sideways as necessary.

Channel Ars Technica