The flatpak security model – part 2: Who needs sandboxing anyway?

The ability to run an application sandboxed is a very important  feature of flatpak. However, its is not the only reason you might want to use flatpak. In fact, since currently very few applications work in a fully sandboxed environment, most of the apps you’d run are not sandboxed.

In the previous part we learned that by default the application sandbox is very limiting. If we want to run a normal application we need to open things up a bit.

Every flatpak application contains a manifest, called metadata. This file describes the details of the application, like its identity (app-id) and what runtime it uses. It also lists the permissions that the application requires.

By default, once installed, an application gets all the permissions that it requested. However, you can override the permissions each time you call flatpak run or globally on a per-application basis by using flatpak override (see manpages for flatpak-run and flatpak-override for details). The handling of application permissions are currently somewhat hidden in the interface, but the long term plan is to show permissions during installation and make it easier to override them.

So, what kind of permissions are there?

First apps need to be able to produce output and get input. To do this we have permissions that allow access to PulseAudio for sound and X11 and/or Wayland for graphical output and input. The way this works is that we just mount the unix domain socket for the corresponding service into the sandbox.

It should be noted that X11 is not very safe when used like this, you can easily use the X11 protocol to do lots of malicious things. PulseAudio is also not very secure, but work is in progress on making it better. Wayland however was designed from the start to isolate clients from each other, so it is pretty secure in a sandbox.

But, secure or not, almost all Linux desktop applications currently in existence use X11, so it is important that we are able to use it.

Another way for application to integrate with the system is to use DBus. Flatpak has a filtering dbus proxy, which lets it define rules for what the application is allowed to do on the bus. By default an application is allowed to own its app-id and subnames of it (i.e. org.gnome.gedit and org.gnome.gedit.*) on the session bus. This means other clients can talk to the application, but it can only talk to the bus itself, not any other clients.

Its interesting to note this connection between the app-id and the dbus name. In fact, valid flatpak app-ids are defined to be the same form as valid dbus names, and when applications export files to the host (such as desktop files, icons and dbus service files), we only allow exporting files that start with the app-id. This ties very neatly into modern desktop app activation were the desktop and dbus service files also have to be named by the dbus name. This rule ensures that applications can’t accidentally conflict with each other, but also that applications can’t attack the system by exporting a file that would be triggered by the user outside the sandbox.

There are also permissions for filesystem access. Flatpak always uses a filesystem namespace, because /usr and /app are never from the host, but other directories from the host can be exposed to the sandbox. The permission here is quite fine grained, starting with access to all host files, to your home-directory only or to individual directories. The directories can also be exposed read-only.

The default sandbox only has a loopback network interface and thius has no connection to the network, but if you grant network access then the app will get full network access. There are no partial access for network access however. For instance one would like to be able to set up a per-application firewall configuration. Unfortunately, it is quite complex and risky to set up networking so we can’t expose it in a safe way for unprivileged use.

There are also a few more specialized permissions, like various levels of hardware device access and some other details. See man flatpak-metadata for the available settings.

All this lets us open up exactly what is needed for each application, which means we can run current Linux desktop applications without modifications. However, the long term goal is to introduce features so that applications can run without opening the sandbox. We’ll get to this plan in the next part.

Until then, happy flatpaking.

The flatpak security model – part 1: The basics

This is the first part of a series talking about the approach flatpak takes to security and sandboxing.

First of all, a lot of people think of container technology like docker, rkt or systemd-nspawn when they think of linux sandboxing. However, flatpak is fundamentally different to these in that it is unprivileged.

What I mean is that all the above run as root, and to use them you either have to be root, or your access to it is equivalent to root. For instance, if you have access to the docker socket then you can get a full root shell with a command like:

docker run -t -i --privileged -v /:/host fedora chroot /host

Flatpak instead runs everything as the regular user.  To do this it uses a project called bubblewrap which is like a super-powered version of chroot, only you don’t have to be root to run it.

Bubblewrap can do more than just change the root, it lets you construct a custom filesystem mount tree for the process. Additionally it lets you create namespaces to further isolate things from the host. For instance if use –unshare-pid then your process will not see any processes from outside the sandbox.

Now, chroot is a root-only operation. How can it be that bubblewrap lets you do the same thing but doesn’t require root privileges? The answer is that it uses unprivileged user namespaces.

Inside such a user namespace you get a lot of capabilities that you don’t have outside it, such as creating new bind mounts or calling chroot. However, in order to be allowed to use this you have to set up a few process limits. In particular you need to set a process flag called PR_SET_NO_NEW_PRIVS. This causes all forms of privilege escalation (like setuid) to be disabled, which means the normal ways to escape a chroot jail don’t work.

Actually, I lied a bit above. We do use unprivileged user namespaces if we can, but many distributions disable them. The reason is that user namespaces open up a whole new attack surface against the kernel, allowing an unprivileged user access to lots of things that may not be perfectly adapted user access. For instance CVE-2016-3135 was a local root exploit which used a memory corruption in an iptables call. This is normally only accessible by root, but user namespaces made it user exploitable.

If user namespaces are disabled, bubblewrap can be built as a setuid helper instead. This still only lets you use the same features as before, and in many ways it is actually safer this way, because only a limited subset of the full functionality is exposed. For instance you cannot use bubblewrap to exploit the iptable bug above because it doesn’t set up iptable (and if it did it wouldn’t pass untrusted data to it).

Long story short, flatpak uses bubblewrap to create a filesystem namespace for the sandbox. This starts out with a tmpfs as the root filesystem, and in this we bind-mount read-only copies of the runtime on /usr and the application data on /app. Then we mount various system things like a minimal /dev, our own instance of /proc and symlinks into /usr from /lib and /bin. We also enable all the available namespaces so that the sandbox cannot see other processes/users or access the network.

On top of this we use seccomp to filter out syscalls that are risky. For instance ptrace, perf, and recursive use of namespaces, as well as weird network families like DECnet.

In order for the application to be able to write data anywhere we bind mount $HOME/.var/app/$APPID/ into the sandbox, but this is the only persistent writable location.

In this sandbox we then spawn the application (after having dropped all increased permissions). This is a very limited environment, and there isn’t much the application can do. In the next part of this series we’ll start looking into how things can be opened up to allow the app to do more.

A stable base for Flatpak: 0.8

Earlier this week I released Flatpak 0.8.0. The version change is meant to signal the start of a new long-term supported branch. The 0.8.x series will be strictly bugfixes, and all new
features will happen in 0.9.x.

The release has a few changes, such as a streamlined command line interface and OCI support, but it also has several additions that make Flatpak more future-proof. For instance, we added versioning to all file formats, and a minimal-flatpak-version-required field for applications.

My goal is to get the 0.8 series into the Debian 9 release, and as many other distributions as possible, so that people who create flatpaks can consider the features it supports as a reliable baseline.

Sandboxing has always been one of the pillars of Flatpak, but even more important to me is cross-distro application distribution, even if not sandboxed. This is important because it gives upstream developers a way to directly interact with their users, without having an intermediate distributor. With 0.8 I think we have reached a level where the support for this is solid. So, if you ever thought about experimenting with Flatpak, now is the time!

I leave you with a small screencast showing the new streamlined way to install an application om the command line (on an otherwise empty system):

For information on how to get flatpak, see flatpak.org. Version 0.8.0 is already in the Ubuntu PPA and in  Fedora. Other distributions hopefully will get it soon.

Distributing spotify as a flatpak

One of the features in the recent flatpak relase is described as:

Applications can now list a set of URIs that will be downloaded with the application

This seems a bit weird. Downloading an application already means your loading data from a URI. What is the usecase for this?

Normally all the data that is needed for your application will be bundled with it and this feature is not needed. However, in some cases applications are freely downloadable, but not redistributable. This only affects non-free software, but Flatpak wants to support both free and non-free software for pragmatic reasons.

Common examples of this are Spotify and Skype. I hope that they eventually will be available as native flatpaks, but in order to bootstrap the flatpak ecosystem we want to make it possible to distribute these right now.

So, how does this work? Lets take Spotify as an example. It is available as a binary debian package. I’ve created a wrapper application for it which contains all the dependencies it needs. It also specifies the URL of the debian package and its sha256 checksum, plus a script to unpack it.

When the user downloads the application it also downloads the deb, and then (in a sandbox) it runs the unpack script which extracts it and puts the files in the right place. Only then is the installation considered done, and from thereon it is used read-only.

I put up a build of the spotify app on S3, so to install spotify all you need to do is:

flatpak install --from https://s3.amazonaws.com/alexlarsson/spotify-repo/spotify.flatpakref

(Note: This requires flatpak 0.6.13 and the remote with the freedesktop runtime configured)

Here is an example of installing spotify from scratch:

New flatpak command line

Today I released version 0.6.13 of flatpak which has a lot of nice new features. One that I’d like to talk a bit about is the new command line argument format.

The flatpak command line was always a bit low-level and hard to use. Partly this was because of lack of focus on this, and partly due to the fact that the expected UI for flatpak for most people would be a graphical user interface like gnome-software. However, with this new release this changed, and flatpak is now much nicer to use from the commandline.

So, what is new?

Flatpakrepo files

Before you can really use flatpak you have to configure it so it can find the applications and runtimes. This means setting up one or more remotes. Historically you did this by manually specifying all the options for the remote as arguments to the flatpak remote-add command. To make this easier we added a file format (.flatpakrepo) to describe a remote, and made it easy to use it.

The new canonical example to configure the gnome stable repositories is:

$ flatpak remote-add --from gnome \
   https://sdk.gnome.org/gnome.flatpakrepo
$ flatpak remote-add --from gnome-apps \
   https://sdk.gnome.org/gnome-apps.flatpakrepo

Alternatively you can just click on the above links and they should open in gnome-software (if you have new enough versions installed).

Multiple arguments to install/update/uninstall

Another weakness of the command line has been that commands like install, uninstall and update only accepted one application name argument (and optionally a branch name). This made it hard to install multiple apps in one go, and the separate branch name made it hard to cut-and-paste from output of e.g. flatpak list.

Instead of the separate branch name all the commands now take multiple “partial refs” as arguments. These are partial versions of the OSTree ref format that flatpak uses internally. So, for an internal reference like app/org.gnome.gedit/x86_64/stable, one can now specify one of these:

org.gnome.gedit
org.gnome.gedit//stable
org.gnome.gedit/x86_64
org.gnome.gedit/x86_64/stable

And flatpak will automatically fill in the missing part in a natural way, and give a detailed error if you need to specify more details:

$ flatpak install gnome org.gnome.Platform
error: Multiple branches available for org.gnome.Platform, you must specify one of: org.gnome.Platform//3.20, org.gnome.Platform//3.22, org.gnome.Platform//3.16, org.gnome.Platform//3.18

Automatic dependencies

The other problem with the CLI has been that it is not aware of runtime dependencies. To run an app you generally had to know what runtime it used and install that yourself. The idea here was that the commandline should be simple, non-interactive and safe. If you instead use the graphical frontend it will install dependencies interactively so you can control where things get installed from.

However, this just made the CLI a pain to use, and you could easily end up in situations where things didn’t work. For instance, if you updated gedit from 3.20 to 3.22 it suddenly depended on a new runtime version, and if you weren’t aware of this it probably just stopped working.

Of course, we still can’t just install dependencies from wherever we find them, because that would be a security issue (any configured remote can supply a runtime for any applications). So, the solution here is for flatpak to become interactive:

$ flatpak update org.gnome.gedit
Looking for updates...
Required runtime for org.gnome.gedit/x86_64/stable (org.gnome.Platform/x86_64/3.22) is not installed, searching...
Found in remote gnome, do you want to install it? [y/n]: y
Installing: org.gnome.Platform/x86_64/3.22 from gnome
Installing: org.gnome.Platform.Locale/x86_64/3.22 from gnome
Updating: org.gnome.gedit/x86_64/stable from gnome-apps
Updating: org.gnome.gedit.Locale/x86_64/stable from gnome-apps

If you have remotes you never want to install dependencies from, you can install them with --no-use-for-deps, and they will not be used. Flatpakrepo files for app-only repositories should set NoDeps=true.

Note that this is not a package-system-like dependency solver that can solve sudoku. It is still a very simple two-way split.

Flatpakref files

The primary way Flatpak is meant to be used is that you configure a few remotes that has most of the applications that you use, then you install from these either on the command line, or via a graphical installer. However, sometimes it is nice to have a single link you can put on a website to install your application. Flatpak now supports that via .flatpakref files. These are very similar to flatpakrepo files, in that they describe a repository, but they additionally contain a particular application in that repository which will be installed.

Such files can be installed by just clicking on them in your web-browser (which will open them for installation in gnome-software) or on the command line:

flatpak install --from https://sdk.gnome.org/gedit.flatpakref

This will try to install the required runtime, so you first need to add the remote with the runtimes.

Launching runtimes

During development and testing it is often common to launch commands in a sandbox to experiment with how the runtime works. This was always possible by running a custom command in an application that used the runtime, like so:

$ flatpak run --command=sh org.gnome.gedit
sh-4.3$ ls /app
bin lib libexec manifest.json share
sh-4.3$

You can even specify a custom runtime with --runtime. However, there really should be no need to have an application installed to do this, so the new version allows you to directly run a runtime:

$ flatpak run org.gnome.Platform//3.22
sh-4.3$ ls /app
sh-4.3$

Using bubblewrap in xdg-app

At the core of xdg-app is a small helper binary that uses Linux features like namespaces to set up sandbox for the application. The main difference between this helper and a full-blown container system is that it runs entirely as the user. It does not require root privileges, and can never allow you to get access to things you would not otherwise have.

This is obviously very useful for desktop application sandboxing, but has all sort of other uses. For instance, you can sandbox your builds to avoid them being polluted from the host, or you can run a nonprivileged service with even less privileges.

The current helper was a bit too tied to xdg-app, so as part of Project Atomic we decided to create a separate project based on this code, but more generic and minimal. Thus Bubblewrap was born.

Bubblewrap is a wrapper tool, similar to sudo or chroot. You pass it an executable and its argument on the command line. However, the executable is run in a custom namespace, which starts out completely empty, with just a tmpfs mounted as the root, and nothing else. You can then use commandline arguments to build up the sandbox.

For example, a very simple use of bubblewrap to run a binary with access to everything, but readonly:

$ bwrap --ro-bind / / touch ~/foo
touch: cannot touch ‘/home/alex/foo’: Read-only file system

Or you can use bubblewrap as an regular chroot that doesn’t require you to be root to use it:

$ bwrap --bind /some/chroot / /bin/sh

Here is a more complicated example with a read-only host /usr (and no other host files), a private pid namespace, and no network access:

$ bwrap --ro-bind /usr /usr \
      --dir /tmp \
      --proc /proc \
      --dev /dev \
      --ro-bind /etc/resolv.conf /etc/resolv.conf \
      --symlink usr/lib /lib \
      --symlink usr/lib64 /lib64 \
      --symlink usr/bin /bin \
      --chdir / \
      --unshare-pid \
      --unshare-net \
      /bin/sh

See the bubblewrap manpage for more details.

Today I changed xdg-app to use bubblewrap instead of its own helper, which means if you start an app with xdg-app it is using bubblewrap underneath.

For now it is using its own copy of the code (using git submodules), but as bubblewrap starts to get deployed more widely we can start using the system installed version of it.

xdg-app builds for gnome 3.20 are out

Finally there are now stable builds of the Gnome 3.20 runtime. I updated the Runtime wiki page  with details on how to install it.

This has been a long time coming, because this new runtime as well as the freedesktop.org runtime that it is based is built in a new way. It used to be built with a custom script, but now it uses the new xdg-app-builder tool. This makes it a lot cleaner and easier to maintain.

Also worth mentioning is the base freedesktop.org 1.4 runtime. This has been updated, modernized and cleaned up to make a great base both for applications that don’t need a lot of runtime support (such as games) and as a base for other runtimes. In fact, Alexei is using this as the base for the KDE runtimes too.

I have also set up a gnome-apps repository with stable builds of some Gnome 3.20 applications. This lets you test Gnome 3.20 applications on older distributions.

Documentation on how to install these can be found on Runtime wiki page , but as a teaser i give you the commands to test gedit if you have zero xdg-app setup:

$ wget https://people.gnome.org/~alexl/keys/gnome-sdk.gpg
$ xdg-app remote-add --user --gpg-import=gnome-sdk.gpg gnome http://sdk.gnome.org/repo/
$ xdg-app --user install gnome org.gnome.Platform 3.20
$ xdg-app --user remote-add --gpg-key=gnome-sdk.gpg gnome-apps http://sdk.gnome.org/repo-apps/
$ xdg-app --user install gnome-apps org.gnome.gedit stable
$ xdg-app run org.gnome.gedit

These runtimes will be a great base if you want to experiment with building xdg-apps, and going forward we will start looking at integrating xdg-app better into the Gnome development and release schedule.

Also, Endless Mobile are sponsoring Gnome with ARM hardware and people working on that, so I hope we can soon have official Gnome runtimes for ARM too.

xdg-app 0.5.0 released

When I set out to create xdg-app I had two goals:

  • Make it possible for 3rd parties to create and distribute applications that works on multiple distributions.
  • Run applications with as little access as possible to the host. (For example access to the network or the users files.)

Yesterday I released version 0.5.0 of xdg-app, and which I now finally consider feature complete for the first of these goals. It now has tooling that makes it easy to build apps, it has multiple graphical frontends, and it is now pretty robust and featureful.

0.5.0 is already built in Fedora 23, and is available for other distributions here.

No software is ever finished obviously, so I will continue working on xdg-app. However, going forward the majority of my work will now be focusing on the sandboxing and portal aspects of xdg-app.