Flatpak’s permissions can be confusing. Some are technical and need knowledge on how they work, and others are self-explanatory. Some are added before the app starts, known as static permissions, and some are requested when the user runs the app, known as dynamic permissions. Many may also criticize Flatpak for lacking Android-style permissions while being unaware of the existence of XDG Desktop Portals.
In this article, I’m going to explain:
Keep in mind that I won’t be going into low level details in this article. This is a simplified overview of the Flatpak permissions system for the less technical.
Static permissions are permissions that come with the app by default and cannot be changed while the app is running. Suppose an app has the permission to read and write to
$HOME/Downloads (the Downloads directory). If you revoke that permission, the app keeps the permission until you completely close it and reopen it. Once you reopen it, you’ll notice that it can’t access the Downloads directory anymore, whether it is through the file picker or by dragging and dropping – assuming it does not make use of dynamic permissions.
Dynamic permissions are permissions that can be changed while the app is running. In other words, resources are only accessed when the user allows it on demand, and can be revoked while the app is running. This is analogous to the Android-style permission model, where a prompt appears on-screen and asks you whether you want to allow the app to access certain resources (a file, hardware, etc.) or deny it.
Most well known desktop environments, like GNOME and KDE Plasma, support these permissions, and will actively suggest that apps use them.
Decoder, for example, uses dynamic permissions to access the user’s camera:
Dynamic permissions are the opposite of static permissions. Static permissions are often viewed as a hack, in other words a workaround, whereas dynamic permissions as a solution. Dynamic permissions are meant to replace static permissions and address their issues.
Static permissions are implicit, because they come with the app by default. They need to be learned when using Flatpak to some extent, as some apps can come with unsuitable permissions, e.g. not enough permissions. Dynamic permissions are explicit, because the user allows them on demand. They’re mostly abstracted away from the user, similarly to Android.
There are several issues with static permissions. As mentioned previously, static permissions are implicit, need to be learned and are considered hacks; however, there are many more issues associated with them.
Since static permissions are implicit and need to be learned, if an app comes with unsuitable permissions, then it can be unusable or inconvenient by default – assuming it doesn’t use dynamic permissions. For example, if a text editor does not come with the
filesystem=host permission, i.e. read-write access to the host system, including user and external devices’ directories, then the app would be effectively useless, because it can’t access any of your files, let alone write.
To work around this inconvenient default, the user needs to manually set additional permissions, to make it useful. In this case, they’d manually have to add
filesystem=host. Another workaround would have the packager add this permission, but the actual solution would be to use dynamic permissions to be able to read and write files anywhere and anytime, as long as the user allows it.
There are several reasons why static permissions are insecure, namely the inability to filter resources properly, and insecure permissions being shipped by default.
Static permissions have no proper method to filter resources. The model’s philosophy is “punch holes in the sandbox whenever needed”, which means that you are effectively making the sandbox weaker with each additional permission you give it. For example, if an app has read-write access to the Downloads directory (or any directory), then it can view and write all files located in it at any time. In contrast, dynamic permissions are designed to be selective, so you only access the files you absolutely need.
For example, Upscaler uses dynamic permissions to retrieve files by either dragging and dropping or selecting one from the file picker:1
The illusion here is Upscaler comes with read and write capabilities to all directories and files by default. This is untrue. Instead, dynamic permissions automatically retrieve the data the user provides and act accordingly. In the previous example, opening a file from the Pictures directory exported the file to a private location that the sandbox can access (
/run/user/1000/doc normally). This means that only the files I provided can be interacted with by Upscaler, and nothing else. If there was a
test3.jpg file in the same directory, then Upscaler won’t be able to access it, as I never provided it.
With static permissions, you get a pre-configured set of permissions that the app can access once installed (unless manually changed).
This means, apps can come with enough permissions by default to read and write the user directory, or even worse, it can access external devices, such as USBs, webcams, microphones, etc. This is by nature insecure and confusing as the default permissions are inconsistent and vary per app. Some apps come with little to no permissions, whereas others come with many.
Dynamic permissions, on the other hand, come with no permissions by default. Once needed, a prompt appears and asks the user for explicit permissions. Refer to the Decoder and Upscaler examples.
XDG Desktop Portals, shortened to Portals, are a collection of APIs that implement dynamic permissions; they allow sandboxed environments to conveniently and securely access resources by using host tools. Each portal is specific to the use case – there is a portal for accessing the camera; a portal for accessing files and directories using the host file picker; or to assist dragging and dropping; and many more.
Using Decoder’s example again, it was specifically using the Camera portal. In Upscaler’s example, when I dragged and dropped
test.png (at 0:06), it was using the FileTransfer portal. When I opened
test2.jpg (at 0:13), it was using the FileChooser portal.
However, Portals do more than just implement dynamic permissions; they’re also designed to integrate cleanly with the desktop. A famous problem with apps used to be the file picker problem, where an app that used a toolkit would use their corresponding file picker. For example, a Qt app would use the Qt file picker and a GTK app would open a GTK file picker. Frameworks like Electron would only use the GTK file picker, even on Plasma, because there wasn’t a convenient method to use the system’s file picker.
This was solved by the FileChooser portal, which uses the system’s file picker, or a generic GTK file picker implementation as a fallback. That is because Portals are interfaces that systems can integrate and interact with.
To explain it simply, a portal only provides basic information to the system. The system grabs this information and displays it to the user however it sees fit. Firefox, for example, was one of those apps that only used the GTK file picker. Nowadays, if you use Firefox on GNOME, it will continue to use the GTK file picker. However, if you use Firefox on Plasma, then it will use the Qt file picker instead. Should System76 create their own file picker for COSMIC in the future, Firefox will open COSMIC’s file picker on COSMIC.
In short, it’s thanks to Portals that we have dynamic permissions in the first place. Additionally, Portals helped apps use host resources instead of whatever the toolkit or framework provides. This means that apps can better integrate with the system, and do so easily.
Unfortunately, Portals aren’t perfect. While it is the closest to the idealistic user experience, there are a few advantages that static permissions have over dynamic permissions: no implementation is required and less functional limitations.
With dynamic permissions, toolkits and frameworks need to implement each portal. At the time of writing this article, GTK supports the FileChooser and FileTransfer Portals. Qt, Firefox, Chromium, and Electron support the FileChooser portal, but not the FileTransfer portal. This means that dragging and dropping files from outside of the sandbox will not work. What’s worse, wxWidgets doesn’t support FileChooser or FileTransfer Portals, which means that we need to resort to static permissions for file access, and only the GTK file picker will be used.
Portals may have technical limitations. Bottles, an app that allows you to run Windows software on Linux, is one of the apps that is affected by Portals’ limitations, specifically the FileChooser and FileTransfer Portals. Many Windows apps require some additional files next to the executable file (e.g. libraries), for example the Windows version of Mindustry. If a user downloads and runs the Mindustry executable from Bottles strictly with dynamic permissions, then it won’t work, because Mindustry can’t access the files it needs; the sandbox will only have read and write access to the Mindustry executable, and not the additional files. As a workaround, Bottles comes with the static permission
xdg-download, i.e. read-write access to the Downloads directory, where most users store downloaded content. However, this doesn’t fix the issue entirely, because it won’t work outside of the Downloads directory (unless manually changed).
These limitations could go as far as making Portals unsuitable for certain apps, or make it inconvenient for developers and users.2
When I explain the premise of static permissions, I like to explain it like Xwayland and Wayland. Xwayland acts as a drop-in replacement for X11, for use in Wayland, but it comes with the design flaws of X11. Similarly, static permissions are used as a drop-in replacement for traditional system access for use in Flatpak. Static permissions are intended to be a short term workaround, as dynamic permissions are meant to be convenient, especially as more toolkits and frameworks implement them. However, XDG Desktop Portals are a relatively new technology, so it needs time to mature and adopt.
To summarize everything, here are some important details in the permission models:
Unfortunately, transitioning from one model or paradigm to another in the realm of technology isn’t easy. Oftentimes, transitioning to the tech is more difficult than developing the tech itself. We currently rely on static permissions, because Portals require to be implemented and does not cover all use cases. We’ve seen a lot of progress in recent years with Portals being adopted in toolkits and frameworks. Hopefully, Portals will only get better in the future as more people use them.
If you want to experiment with XDG Desktop Portals, feel free to take a look at ASHPD Demo. Please do note that the app may misbehave if your desktop environment does not support the portal or does so improperly.
If you want to learn more about the static permissions, feel free to take a look at Flatseal’s documentation. For the offline version, you can install Flatseal, click the hamburger menu (
☰) and click “Documentation”.
If you want a technical overview of Flatpak’s permission models, feel free to take a look at Flatpak High-Level Overview.
At the time of writing this article, it’s not available on the stable version of Upscaler. ↩
Luckily, this issue is being discussed with “neighboring files” in this discussion.
xdg-download won’t be needed anymore once it has been addressed. ↩
Source file: 2023-05-11-overview-of-flatpaks-permission-models.md