Skip to content

Snap Execution Environment

Zygmunt Krynicki edited this page Feb 6, 2018 · 7 revisions

Snap applications and hooks execute in a specially crafted environment. The word environment does not merely refer to environment variables but to the set of observable properties of the system.

Starting applications

When a snap application is started it is typically done so by executing one of the programs in the /snap/bin/ directory. Curious observer will notice that all such applications are symbolic links to /usr/bin/snap. The snap executable detects this and behaves as if snap run had been invoked. This locates the correct snap and application name and proceeds to exec the first helper program, snap-confine.

The purpose of snap-confine is twofold: As the name implies it is responsible for confining the started process by establishing the security sandbox. In addition it also sets up the mount namespace of the process in a way that will be described below.

Confined and in a modified mount namespace, snap-confine proceeds to run (no longer with elevated permissions) the last of the helper programs called snap-exec. The purpose of that program is to parse the snap.yaml file belonging to the application that is being started and execute the command listed there.

All of the transitions here, from the /snap/bin/foo -> /usr/bin/snap, to snap-confine and snap-exec are done with the exec system call. At all times there is only one process going through this transition.

Applications built with snapcraft usually have one more step. The actual command that was spelled out in the snapcraft.yaml file is moved to a shell wrapper script command-SNAP-APP-wrapper. The wrapper sets PATH, LD_LIBRARY_PATH and executes the real command.

Applying confinement

There are two main parts of the confinement that are applied to each process: apparmor and seccomp.

snap-confine ensures that upon exec an apparmor label is associated with the process. The label is always derived from the snap and application name (snapd code often refers to this as the security tag). A program like hello-world has the apparmor label of snap.hello-world.hello-world (the snap name and application name are always separate in the security tag).

The apparmor profile is stored directly in the kernel memory. The profiles as they exist on disk can be found in /var/lib/snapd/apparmor/profiles. They are loaded by snapd when they are generated or are loaded on boot by one of the apparmor systemd units (TBD: clarify which).

The apparmor profile defines most of the policy. It specifies which files can be read, which can be written, it specifies details of what is allowed by various IPCs (including DBus) and more. Typically when something is not allowed by the policy, apparmor returns a permission denied error (EACCES) at the lowest level of the system.

Apparmor profiles use a standardised syntax that is documented extensively by the apparmor project. If you want to learn more about that please visit http://apparmor.net/

The seccomp profile is stored on disk and is loaded and parsed each time. The profiles are in /var/lib/snapd/seccomp/bpf/*.src (formerly /var/lib/snapd/seccomp/profiles) and are named the same way as their apparmor brethren. Seccomp profiles mainly define which system calls are allowed. In some cases system calls are also filtered by the particular arguments.

The syntax for expressing seccomp profiles is unique to snapd. You can find more about this in the snapd source code.

Setting up the mount namespace

The mount namespace defines what a process "sees" in the file system. Two process running on the same kernel can see different root file system and set of mount points thanks to this feature. Snappy uses this feature extensively to construct a consistent look of the file system that is not strongly affected by how the host distribution arranges the file system.

The details are somewhat complicated but the result is that:

  • The application sees the core snap as the root of the file system. You can think of this as a chroot of sort.

  • Certain directories from the host file system are mapped (bind-mounted) to the mount namespace (see below).

  • The mount event propagation is restricted so that anything that is mounted in the mount namespace is only visible there

  • As an exception the /media directory shares mount events (e.g. udisks2 snap can mount globally visible things there)

  • so does /run/netns, it is a location used by the ip tool to manage network namespaces.

  • The mount profile of the snap is applied (e.g. content sharing uses this)

  • /dev

  • /etc

  • /home

  • /root

  • /proc

  • /sys

  • /tmp

  • /var/snap

  • /var/lib/snapd

  • /var/tmp

  • /run

  • /lib/modules

  • /usr/src

  • /var/log

  • /run/media or /media (depending on the distribution)

  • /run/netns

Keep in mind that the core snap which now acts as the root file system is read only. This is not a limitation of the security sandbox but an inherent property of the squashfs file system that snaps rely on.

Preserving the mount namespace

snap-confine prepares the mount namespace and then preserves it for subsequent runs of applications in the same snap. The prepared namespace is saved as a bind mounted nsfs (namespace file system) file in /run/snapd/ns/$SNAP_NAME.mnt. As with all files in /run the mount namespace is not preserved across system reboots.