An example confined user shell

Mircade snap: An example confined user shell

Mircade demonstrates how it is possible to take a collection of applications, a user shell and Snap technology and deliver a portable, secure package to multiple Linux platforms including Ubuntu Core, Ubuntu Desktop and many other distros.

Security: Snap confinement

What does ‘confinement’ mean?

When you run an application on a computer you are giving it, and by extension, the developers of that application access to your computer. Unless you take precautions, it gets access to everything you can access.

Historically, there’s been a high cost of entry to application development and distribution meaning that developers have had to establish a reputation and trust. While some have suspicions of what, say, Microsoft Office or Chromium does, there’s no realistic fear that it will steal from you or hold information on your computer for ransom.

But the barrier to entry has become low, writing an app and getting it into the app store has never been easier and, as a result, application development is no longer the preserve of a few well-known organizations. The basis for trust that used to exist has been eroded.

At the same time, computers are being trusted with more and more sensitive information. We carry pocket computers with us everywhere and trust them to hold personal information including access to bank accounts, credit cards and medical details.

When the computer has access to your bank accounts, running code from developers that are essentially unknown to you beyond a picture of their app on the app store is risky.

Taking precautions to mitigate the risk posed by untrusted code is where app confinement comes into play. By confining the app at the operating system level it is possible to restrict its access to your computer to only those things that are needed for it to work.

How does ‘app confinement’ work?

As developers, we all know that something that sounds simple in the user domain can involve some serious work in the solution domain. App confinement is no exception: we need to consider what the operating system needs to do to confine an app; how that can be controlled; how the user can review and configure the confinement; and how to write and package applications so they work with restricted access to the system.

The discussion that follows talks about some specific Linux technologies for app confinement. That’s for the convenience of having concrete examples that I’m familiar with, but the principles involved can be, and have been, applied with other technologies and on other operating systems.

Kernel and userspace

The code running on a computer can be divided into ‘kernel’ and ‘userspace’. The kernel is that part of the operating system that mediates all interaction with hardware and between processes. The userspace is everything that runs within a normal app. (I know this isn’t the whole story, but software development is about useful abstractions and this separation is useful for this article.)

If we write a “hello world” application, the code we write runs in userspace. And so does the output function from the library we use (maybe operator<<() , or printf() or …) but at some point it writes to the console and at that point the kernel takes over and, eventually (there may well be further userspace and kernel code executed), some pixels are lit on the screen.

While code can run without invoking the kernel it cannot produce significant effects without doing so. It can’t access your files, it can’t access the internet, it can’t access your keyboard, mouse, touchpad, interact with other processes, etc.

That makes the interface between userspace and kernel a useful place to restrict the activities of a program.

Snaps and AppArmor

AppArmor is an implementation detail of ‘snap confinement’, which is a component of Canonical’s ‘Snap’ packaging format. Snaps make use of lists of AppArmor rules called ‘interfaces’, each of which covers identifiable capabilities. These interfaces are reviewed by the Snap developers and can be enabled (or disabled) by the end user.

These are the permissions used by Mircade:

$ snap connections mircade
Interface              Plug                           Slot             Notes
audio-playback         mircade:audio-playback         :audio-playback  -
hardware-observe       mircade:hardware-observe       -                -
login-session-control  mircade:login-session-control  -                -
network-bind           mircade:network-bind           :network-bind    -
opengl                 mircade:opengl                 :opengl          -
wayland                mircade:wayland                :wayland         manual
x11                    mircade:x11                    :x11             -

A user shell

A user shell is a program that allows the user to interact with the computer. It could be as simple as a command-line shell or as complex as a full desktop environment.

For this example, Mircade, I use a fork of the egmde example Mir shell I’ve presented in other writings. This version of egmde allows the user to select one of a number of programs and run it all within the Snap confined environment.

Obviously, you don’t have to use egmde, or even a Mir based shell, but doing so ensures there are no unexpected issues to resolve.

The applications

I’ve taken a bunch of games from the Ubuntu archive and bundled them in. It isn’t necessary to choose games, other programs from the archive, or ones you write yourself can be bundled this way.

In this example, most of the applications use SDL2 and all use Wayland. That makes things simple, but with Mir shells is also possible to bundle applications based on other toolkits and X11.

The target platforms

Snaps can be built for any of the architectures that Ubuntu supports. In addition to that I wanted to show how a snap like this can run seamlessly across Ubuntu Core, and X11 and Wayland based “Classic” desktops.

As has been discussed elsewhere, connecting snaps to Wayland involves a little “dance”, this is all automated for Mircade by the shell scripts illustrated below.

Running on Ubuntu Core

On Ubuntu Core you need a Wayland server (such as mir-kiosk) running. Then when you install (or start) Mircade it runs a script to detect and connect to the Wayland server:

#!/bin/sh

if [ -z "${WAYLAND_DISPLAY}" ]
then WAYLAND_DISPLAY=wayland-0
fi

# "Someone else" provides the real Wayland display, we use that as a host
real_wayland=$(dirname "$XDG_RUNTIME_DIR")/${WAYLAND_DISPLAY}
if [ ! -O "${real_wayland}" ]; then echo "Waiting for Wayland socket";  sleep 4;  fi
if [ ! -O "${real_wayland}" ]; then echo "Waiting for Wayland socket";  sleep 16; fi

if [ ! -O "${real_wayland}" ]
then
  echo "Stopping: No host Wayland socket (do you need to install mir-kiosk, or connect the wayland interface?)"
  snapctl stop $SNAP_NAME.daemon
  exit 0
else
  ln -sf "${real_wayland}" "$XDG_RUNTIME_DIR"

  export MIR_SERVER_WAYLAND_HOST=${WAYLAND_DISPLAY}
  unset WAYLAND_DISPLAY

  exec "$@"
fi

Running on Classic Linux

On Ubuntu Classic there are three ways that Mircade can run.

  1. Connecting to a Wayland compositor
  2. Connecting to an X11 compositor
  3. Running directly on the hardware using logind

For the last of these to work you first need to manually connect the login-session-controlinterface:

snap connect mircade:login-session-control

After that, the rest is automated by a wayland-if-possible script:

#!/bin/sh

real_wayland=$(dirname "$XDG_RUNTIME_DIR")/${WAYLAND_DISPLAY:-wayland-0}

if [ -O "${real_wayland}" ] && [ -e $SNAP_COMMON/wayland.connected ]
then
  mkdir -p ${XDG_RUNTIME_DIR}
  ln -sf "${real_wayland}" "$XDG_RUNTIME_DIR"
  export MIR_SERVER_WAYLAND_HOST=${WAYLAND_DISPLAY:-wayland-0}
  unset WAYLAND_DISPLAY
fi

exec "$@"

This has obvious similarities to the script used when running on Ubuntu Core, but doesn’t give up (or wait) if there’s no Wayland socket to “host” Mircade.

Conclusion

The Mircade snap is a proof-of-concept and illustrates a confined shell environment that works:

  • On Ubuntu Core
  • On “classic” Linux (where snapd is supported)
  • A variety of applications included

Including a bespoke set of applications in a new snap is the simplest way to customize this. maybe this will inspire you to find a more useful set of apps.


Notes

Further reading

There’s a more detailed discussion of snap confinement in A Secure Environment for Running Apps?

The code

All the code for Mircade is available on GitHub:

https://github.com/AlanGriffiths/mircade/

The snap

mircade

2 Likes