Containerize all the things with Ubuntu Core 20
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
libavcodec57, and 31 other packages.
If you already have
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
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
/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
/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
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
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
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/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
summary: Audio software for multi-track recording and editing
publisher: Daniel Llewellyn (diddledan)
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:
latest/stable: 2.4.2 2020-12-10 (748) 123MB -
latest/candidate: 2.4.2 2020-12-13 (756) 123MB -
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.