The magic behind snap interfaces
by Igor Ljubuncic on 4 June 2021
Snaps are confined, self-contained applications, designed with portability and security in mind. By default, strictly confined snaps run in isolation, with minimal access to system resources. For instance, they cannot access home, network, audio, or display. To make their snaps usable, developers and publishers can declare a set of interfaces, which allow granular, per-resource access to the underlying system in a secure, controlled manner.
In today’s article, we want to talk a bit more about the mechanics behind the interface. What happens when a developer declares an interface plug in snapcraft.yaml, and when that snap gets installed on a user’s machine?
Interfaces consist of two components – slots and plugs. The slot of an interface is a provider of functionality (and access) to a system resource. The plug of an interface is a component of a built snap, which can then connect to the relevant slot, and thus gain access to the underlying resource. Multiple plugs can connect to a specific slot, e.g.: multiple snaps with the home plug can connect to the home slot, and thus gain access to the user’s home directory.
For security reasons, some interfaces can be auto-connected – on snap installation on a client’s machine, the declared plugs will be able to connect to the slots without any manual intervention. In other scenarios, this will not be possible by default. The user will need to manually connect their snap, or request an override from the Snap Store team. In some cases, this functionality may be granted, especially if it’s part of the necessary application functionality. Typical examples would be network control or audio recording capabilities.
In some cases, the snaps will notify you that a specific connection is required. In others, you will need to determine this yourself, perhaps if you encounter a functionality problem. You can also manually choose to connect or disconnect interfaces as you see fit, but be aware that this may alter the application’s expected behavior.
snap connections vlc
Interface Plug Slot Notes
audio-playback vlc:audio-playback :audio-playback -
audio-record vlc:audio-record - -
avahi-control vlc:avahi-control - -
camera vlc:camera - -
desktop vlc:desktop :desktop -
desktop-legacy vlc:desktop-legacy :desktop-legacy -
dvb vlc:dvb - -
home vlc:home :home -
jack1 vlc:jack1 - -
mount-observe vlc:mount-observe - -
mpris - vlc:mpris -
network vlc:network :network -
In the snippet above, the VLC media player has access to a wide range of typical resources required for its functionality. The camera isn’t auto-connected, though. For example, if you need VLC to capture a video stream using the camera device, you will need this interface to be connected.
sudo snap connect vlc:camera
Now, in a typical snapcraft.yaml file, a plug section declaration may look like this:
If you’re interested in a broader overview of interfaces and specific use cases, we have already covered this in detail in a blog post a while back.
During the installation, if you install a snap on the command line and you pay attention to the output in your terminal window, you may see a number of messages from the snapd service about mounts, connections, etc. The installation of the snap includes two principal steps:
- Unpacking of the snap (a compressed Squashfs archive).
- Configuration of the security profiles.
Once the snap is installed, you can inspect the system configuration. The snapd AppArmor profiles are stored under:
Depending on how complex your snap is, there may be one or more profiles, e.g.:
-rw-r--r-- 1 root root 4706 Jun 3 12:32 snap-update-ns.segfexample
-rw-r--r-- 1 root root 23379 Jun 3 12:32 snap.segfexample.segfexample
Provided you have the right permissions, you can now open these profiles in a text editor, and examine the contents. You need some familiarity with the AppArmor syntax, but in essence, you will see the capabilities declared in your snapcraft.yaml plug section listed and expanded here
# Description: Can access the network as a client.
/etc/mdns.allow r, # not yet included in the mdns abstraction
network netlink dgram, # not yet included in the nameservice abstraction
If you want to know more about how a particular interface works, you can also look at the snapd source on GitHub. For instance, the home interface. The code declares the slot specifications, including read and auto-connect attributes, and the AppArmor profile details that will be applied on installation. In this example, the profile allows read/write access to all files in the user’s home directory, except for the snap application path and top-level hidden directories, allows creation of new files, allows access to GVFS mounts for files owned by the user, and disallows writes to the bin subdirectory.
const homeBaseDeclarationSlots = `
Snap interfaces may look a bit confusing at first, especially since their behavior diverges from the classic Linux model. However, they can be useful in making sure applications only use the necessary system resources. This can improve both security and predictability, and minimize long-term cruft in the system.
If you want to understand exactly what will happen when you declare a plug in your snapcraft.yaml, you can check the snapd source code and/or the AppArmor profiles for the installed snap to get a clearer view on the specific implementation of the security confinement logic. Hopefully, this article makes things simpler, and reveals the “magician’s trick”. Let us know if you have any questions or suggestions on this topic by joining the Snapcraft forum.