I’ve been working on GUI snaps, with IoT devices in mind. We’ve written up a series of tutorials on how to get it working:
However the implementation we recommend in that tutorial has a flaw that I think need to be rectified. I want to go into the problem here, and share what I think is a better - but not perfect - solution.
The Wayland socket
Communication between a Wayland client (GUI app) and Wayland server (e.g. mir-kiosk) happens through a “socket” - a special file on the filesystem. By convention, this file is called “wayland-0” (that number can change) and is located in $XDG_RUNTIME_DIR.
In the desktop/Snap Classic world, $XDG_RUNTIME_DIR is a single directory shared between all applications, so both server and client can access the socket.
Not so simple in the Ubuntu Core world…
Sharing the Wayland socket between snaps
In Ubuntu Core, confined snaps each have their own $XDG_RUNTIME_DIR - to prevent leaking data between different snaps. But that makes sharing the Wayland socket problematic.
So we need mir-kiosk to provide the Wayland socket at a location that an application GUI snap can access and connect to it.
Snapd doesn’t provide any convenience to automatically put the socket in the application snap, so we need to engineer it ourselves.
Using the Content Interface
The content interface seemed to be the ideal solution to this - it allows one to share a directory from one snap with another. This is the approach I started with, and it did succeed.
My initial approach was this: mir-kiosk snap provides a “wayland-socket-dir” content interface slot, which essentially shared its $XDG_RUNTIME_DIR with any other snap that plugs into it. Clients could then point $XDG_RUNTIME_DIR environment variable to the shared directory and Wayland would work!
However with the benefit of hindsight, this approach has problems:
- We just want to share a socket, not an entire directory.
- Sockets require full read-write access, and content interface only sets permissions on the directory, meaning the directory needed full read-write permissions. This opens up a data leaking channel between 2 GUI snaps!
- Client snaps overriding $XDG_RUNTIME_DIR made it especially easy for accidental data leaking to happen, as it is a commonly used default storage location for many libraries.
The existing “wayland” interface
But wait, isn’t there an existing wayland interface in snapd? Yes, it was formed for graphical snaps to run on Ubuntu Classic with Wayland. It has its own approach to sharing the Wayland socket which I find clunky, but also works:
The convention here is to place the Wayland socket in /run/user/$UID/. Outside the snap world, this is exactly where $XDG_RUNTIME_DIR points to. But inside a snap, $XDG_RUNTIME_DIR is a randomly generated subdirectory of the form: /run/user/$UID/XXXXXX - and AppArmor rules are used to confine snaps inside this directory.
The wayland interface has an exception to allow a snap to access /run/user/$UID/wayland-0 - and nothing else in that directory. So it is secure.
But that puts the responsibility on the application snap to check if that file exists, and link it into their own $XDG_RUNTIME_DIR.
Those using the “desktop-helpers” project have this done automatically for them, otherwise the snap author needs to be aware of this.
The future plan
The Content Interface approach is too insecure to continue using, we are better off adopting the existing wayland interface approach, and ensure the details are dealt with transparently in a helper.
This comes with the benefit that GUI snaps can be tested on both Classic and Core too.
I’m in the process of doing this transition, and I will update all our documentation to reflect the new approach when its ready. I’ll post here when it’s done.
I hope this does not inconvenience anyone, but let me know if it will and we’ll establish a transition path for you.