In part 1 we created a very small application. All it did was print to stdout. Such a program is very easy to sandbox. In fact, since we didn’t specify any permissions for it this application already runs in a very tight sandbox.
Here is what the sandbox does:
- No access to any host files except the runtime, the app, and ~/.var/app/org.test.Hello, and only the last of these is writable.
- /tmp is unique to the application instance.
- setuid functionallity is disabled.
- Processes are in a cgroup which they cannot get out of. This lets you know what application a process is running in, in an unfakable way. (This will be very important later for per-application preferences.)
- No access to the network.
- No access to any device nodes (apart from /dev/null, etc).
- Can’t see any processes outside the sandbox.
- A set of seccomp rules are loaded that limits what syscalls the app can do. For instance, it can’t use nonstandard network socket types, or ptrace other processes.
- Limited access to the session dbus instance, is only allowed to own its own name on the bus and can’t talk to anyone else.
- No access to host services like X, system dbus, or pulseaudio.
Of course, its very easy to secure a Hello world program, as it doesn’t need to do anything. There are designs (and experimental code) to allow applications to securely break out of the sandbox (using something called Portals). However, that is not stable for general use at this point. What is usable now though is punching minimal holes in the sandbox such that the applications can do what it needs.
We saw this already in part 2, where we used this command, granting X11 and network access:
xdg-app build-finish appdir2 --socket=x11 --share=network --command=gnome-dictionary
These arguments translate to these properties in the application metadata file:
[Application] name=org.gnome.Dictionary runtime=org.gnome.Platform/x86_64/3.20 sdk=org.gnome.Sdk/x86_64/3.20 command=gnome-dictionary [Context] shared=network; sockets=x11;
As we gave no access to the filesystem in the above the resulting app can’t see your files. We can verify this with a command like:
$ xdg-app run --command=ls org.gnome.Dictionary ~/ <nothing>
The permissions in the metadata file are only the default values though. You can override these in different ways. First of all, xdg-app run takes the same command line arguments as build-finish, which can override permissions for that instance. For example, this will let it see your home directory:
$ xdg-app run --filesystem=home --command=ls org.gnome.Dictionary ~/ <homedir listing...>
You can also permanently override permissions for an application:
$ xdg-app --user override --filesystem=home org.gnome.Dictionary $ xdg-app run --command=ls org.gnome.Dictionary ~/ <homedir listing...>
This can then again be overridden in a specific instance:
$ xdg-app run --nofilesystem=home --command=ls org.gnome.Dictionary ~/ <nothing>
There are quite a few options that control sandbox permissions. So, I’m going to list some useful examples of what can be done. (Note that all options have a negative version too, i.e. –unshare undoes –share.)
Grant access to some of your files:
--filesystem=host # All files --filesystem=home # Your homedirectory --filesystem=home:ro # Your homedirectory, read-only --filesystem=/some/dir --filesystem=~/other/dir # paths --filesystem=xdg-download # The XDG Download directory --nofilesystem=... # Undo some of the above
Allow the application to show windows using X11:
Note: –share=ipc means that the sandbox shares ipc namespace with the host. This is not necessarily required, but without it the X shared memory extension will not work, which is very bad for X performance.
Allow OpenGL rendering:
Allow the application to show windows using wayland:
Let the application play sounds using pulseaudio:
Let the application access the network:
Note: Giving network access also grants access to all host services listening on abstract unix sockets (due to how network namespaces work), and these have no permission checks. This unfortunately affects e.g. the X server and the session bus which listens to abstract sockets by default. A secure distribution should disable these and just use regular sockets.
Let the application talk to a named service on the session bus:
Let the application talk to a named service on the system bus:
Give the application unlimited access to all of dbus:
That was it for today. In the next part we’ll take a look at how to distribute your application.