 I'm Alex, I work at Red Hat in the desktop group, meaning usually I'm a known kind of guy. For the last couple of years I've been working on something called Flatpak. And this talk is about Flatpak. Title says it's about portal, which is our way to do dynamic permissions. But to explain that, I also have to explain some of what Flatpak is and how it works. And here's what it is. How many people have ever heard of Flatpak? Most people? How many people used it? Some people. All right. So it's an application, distribution and deployment mechanism for desktop applications on Linux. On Linux because we use kernel stuff that don't really exist anywhere else. And for desktop applications, meaning Xapp, Wayland apps, things that are run on the desktop. So the idea is that you build your thing on your machine, put it somewhere on the internet and people can download it and run it on their systems. And we have two major goals. One is cross distribution, deployment, meaning I should be able to run whatever distro I want at whatever version, build an app, ship it and whoever runs it should be able to run whatever distro he wants and be able to upgrade the version and whatnot and the app will keep running. And the second goal is that we want to have some level of sandboxing between apps and the system, protect your user file from the apps, protect apps against each other. Now the second goal is kind of optional. You really need to modify apps to make them really work in a sandbox fashion. So initially, many of the apps we currently use as Flatpaks are not really sandbox. All the games are for instance, because they're easy to sandbox. The other apps are harder. So we'll try slowly moving towards more and more things being sandboxed, but the first goal is always met. The second one is kind of a goal. And then we're talking about apps, and this is clearly like a reference to iOS and Android style apps, not necessarily like web style apps. And those are fundamentally different than containers. This container implies a server something. Usually a server container is a network-facing service or like support for a network service like a database or something. And that's fundamentally a different context than what an desktop app runs in. Like they're probably running on a machine that doesn't have graphical output. It has a SUS admin that manages it. Maybe it's a cluster. There's like a logging framework. Someone gets paged when it goes down. That is very dissimilar to the kind of ad hoc system that an app runs in. It gets launched when the user launches it and he clicks on the close button and it goes away. So it's a very different kind of situation. It relies on all these services that we expect to be in a Unix graphical session, the X server or Wayland or whatever, Pulse Audio, D-Bus, all these things that are very specific to the desktop. And everything runs as the user UID. Not only does anything run, like nothing ever runs as root, we cannot really require root in any way. People who run these things are not system admins, so they should never be able to run something as root, not something generic as root. Some system use multiple UIDs like Android does for app isolations. But we can't really do that and still be traditional Unix. You log in your session, we can't create multiple users for you. There's also issues with style ownership and being able to have UIDs across machines and stuff. Containers typically use some kind of overlay file system or a copy and write system where they can just write to user share. The app can just write in the system wherever, install a new RPM. But desktop apps typically, like if you install an RPM of the GIMP, it cannot write to user share. Everything always goes in your user's home directory. Which is very different from how containers work, but actually it's how all currently existing Unix desktop app works already, so it's not a problem. We don't do the overlay system, instead we have a very simple split. The runtime supplies slash user and the app supplies something called slash app which has the bundles for the app. This way we can get, so the runtime is kind of like the base layer in a Docker or whatever container system. But they're like two separate images that get updated automatically on, like even if you didn't update the app, if the runtime changes because there's a G-Libsie update or whatever, the app will automatically inherit it. In Docker you would need to rebase on the base layer or whatever. And then it uses something called OSTree. I think OSTree is really cool. I don't really have time to go into the details of it. But it's kind of like as you get repository where every branch in the repository is an app. And then we check out the apps, their hard length so it doesn't actually use extra space. But in the end, each app and each runtime is just a tree of files, like regular files. And it has a subdirectory called files, which actually has the files. And then there's a key value file called metadata that gives extra information about what's the application name, what architecture was built for, what environment variables do you want to set, what command do we launch by default. And then there is a directory called exports. And the exports are the way that the installed app integrate with the system, right? So if anyone is used to Linux desktop stuff, these are not weird. These are the typical ways that the free desktop standards for how apps integrate with the session, basically, with your desktop. So we take these files. Desktop files is how you appear in the menus. Keep the service files. If your app is a service, you can add mine types. Seeing I handle these kind of extension. If you see one of these, open with me. We expose these. From all the currently installed apps, we collect all these in a single directory that we hand off to the desktop so that it can see these apps as regular apps in the menus. So you wouldn't notice anything different between these apps. They used to appear in your desktop and you click on the icon. But it spawns and it just works. There are some limitations. We know about these file types. We can rewrite them to be safe so you can't expose the DBS service files that executes some weird commands. Instead of everything that you launch will be sandboxed and flatpack and stuff. And then when you run it, we always have some kind of sandbox. Fundamentally, it's based on file system names so we can make things look like it's not the whole system. There's always some level of sandboxing. It uses a project called bubble wrap. Bubble wrap was originally part of flatpack way back when it was called XDD app. It was like the XDD app helper that was later extracted because a lot of people were interested in using bubble wrap by itself. Bubble wrap is a command line wrapper for the kernel container system. It's like namespaces, setcomp, all these things. Which is, you know, in many ways not super dissimilar from run C or something like that. But the major interesting part of bubble wrap is that the features it exposes are only those that we can implement using unprivileged user namespaces. So in this machine running Fedora with user namespaces in the kernel, bubble wrap is a regular app that doesn't read any extra permissions. And yet it can create sandboxes for me. It's very useful for testing or debugging. You can give this to any user and they can create their own sandboxes for doing development or whatever. So a lot of people are using it to sandbox stuff in the desktop in general. But it also has a mode where it's set UID and since it's only ever exposing like a real minor subset of what unprivileged user namespaces is, it is generally considered something safe that you can ship to users even though it's set UID. It uses these basic privileges. No new privileges how we make user namespaces safe. We basically unshare all those namespaces and we create a complete from scratch file system hierarchy based on the root being a tempFS where we build up like directory like slash temp is just a directory on the tempFS. But slash user and slash app are bind mounts to the actual data, like read only bind mount to the data from the app. There's some minimal like they have on prokensys. And then there is a directory in your home directory, which is per app, dot var app, app ID, which is the only place you could write to that persists by default. And then we set up the environment like there's this x to g directory specification, set these up to point in there. So most apps just automatically write there. We use secomp. Secomp is very low level. So it's hard to make a generic secomp profile that is useful for all apps. But we do have some use of it. Like we don't allow recursive containers. We don't by default give access to ptrace and perf and some low level things. We also use systemd dash dash user. If you have a user systemd session, we use it to set up a scope, which is a way to get a cgroup for our processes. We can't currently actually set any cgroup properties like memory limits or anything that currently requires root privileges. But apparently maybe with the new cgroup v2, we can maybe do limitations to, but that's eventually. To make things work well for desktop apps, we punch some holes in it by default. So debus is a very common way for desktop apps to talk to each other. And by debus here, I mean the session debus, not the global one. But we always let apps talk to the bus, but we do so via a filtering proxy. So they don't really talk to the bus and they're not really allowed to do much. What they can do is own their own application ID as a name on the bus and they can reply to other people that can talk to you. We also expose some files from the host, like the fonts and the icons, which are very useful because if you say inherit the default font in your app, you better be able to read the font file or nothing works. There's some configuration options, configuration files that we need to resolve com of local time. There's a journal socket there so you can log stuff. Sys and Etsy machine ID are visible, so we're not trying to anonymize the host. Like, it's really hard to do that because apps will need to look for PCI IDs to load the right OpenGL driver and things like that. So we're not anonymizing the machine, we're just giving it, this is what this, we used to hide the user stuff from it. And then there's this Fuse amount that's always to get there, which is from something called the document portal, which I get into more detail later. But on top of this, applications can request more access, like punish more holes than this thing. And it's something that the app lists to install, I will need to have access to these things. And the user can then decide to not install it or you can actually override them, but chances are if you do, things don't work, but you can if you want. And these are the things, basically you can grant access to a particular directory, like read only access to my photos, the XG photo directory, or you can use granted access to all my home directory or the entire system. You can grant it network access, IPC access. These are basically un-chaired namespaces or chaired rather. You can ask for more device nodes. Common thing is you want to have access to USB devices because you will need to do some kind of a user space, USB driver, like a joystick or some weird thing. You can ask for sockets, and by sockets I mean Unix domain sockets. They just get mounted into your file system so you can talk to the X server, Wayland Compositor, Pulse Audio, Demon, and you can ask for actually raw D-Bus access if you want. You can also ask for loosening up the rules for Secom, like if you're a debugger or something, you might want to have P-Trace access, or Perf access. You can ask for access to more D-Bus names. I want to talk to this particular D-Bus service. I want to be able to implement this other D-Bus name. We don't actually have a UI for the asking about user permissions right now. All the back-end stuff is implemented, but since most apps so far are basically not very sandboxed, it hasn't yet been a priority to have the UI because it's really complicated to translate really low-level system permissions to something that a regular user can understand. What does it mean that you have Pulse Audio access? It's just very hard. So we have to come up with some way to describe these in easy-to-understand ways. But static permissions are fundamentally lame. It's like the old-school Android permission system where you ask for this long list of things. Do you want to do this? How would you know if you want or not? You haven't even run the app yet. You don't know if it's any good. Sometimes it's unavoidable. Many things are fundamentally set up when you launch the container, and it's really hard to avoid setting them up ahead of time. But when possible, we should avoid to use static permissions and instead use something called portals. Portal is a word we made up a bunch of years ago at a hackfest here in Berlin, actually. It is a name for a service that runs on the host, so it's not sandboxed, but there is a hole in all the sandboxes allowing them to talk to the portal. And the interface you use, the API you expose on the portal, is designed to be safe to handle this. And safe is a very vague word, but basically it's been decided by whoever wrote the portal that this is something we deem safe, and mostly this is done by having things be interactive. So when you request or you do an operation on a portal, almost always that will be a dialogue shown to the user or some kind of conversation with the user. And that way the sandboxed app can't really misuse this in a way that the user isn't aware of. If unexpectedly there's a billion dialogues popping up, you know that the app is doing something wrong. And if you did, or even if there's just one, but you didn't expect it, you wouldn't be typing sensitive information in it. In fact, you would probably close the window, which is another important part of being a portal. If you're cancelable, you can always abort and see, I didn't want this, I'm going to close this, and the app has to accept that. And in third, we want to have a way that the portal can, in a trusted way, know what's doing this call. Like, can I give an ID for this thing, so I can store things like permissions? If it asks for permissions, maybe I want to remember the permission for the duration of the session or something. So it doesn't have to keep asking all the time. So that's basically how portals are supposed to work. I want to do an example here. So here, I'm running a gnomeRecipes, but instead of running gnomeRecipes, I just run a shell in it. So it looks like I have my home directory here. Oh, whatever. It's just nothing there, it just looks like it. So in general, we try to expose a subset of what's on the host in the sandbox. We never make up paths that don't exist outside the sandbox. So it has the same name, but it's nothing there. There is the more app directory we can store persistent data. And if I run the app, oh, it got up here. It works. This is an app that is useful on its own, but it's also kind of an experimental thing where we can play with sandboxing. So this is a snap that runs fully sandboxed and you can do recipes and stuff on it, but you can also create your own recipes. And if you want to have a photo for it, oh, shit. That's really high DPI for the win. Anyway, it looks like I can see my home directory here and I can load a file and I can make cats or whatever. It didn't look like anything special. Like you clicked on the open thing and things just used to expect there to be a dialogue. So it goes, when it works, it's the natural flow. It works really, really well. Basically what happened was that the file chooser was running outside the sandbox, but choosing it and clicking okay implicitly gave permission to access just this one file and nothing else. And the app itself was not in any way in control of what was happening in the sandbox. So what was happening here? There's a portal called the XTG desktop portal. It's running as a service on the host and by host I actually mean it's a service on the session bus running as the user in the side of the session. So it's not a rude thing. It's just a regular thing running in your session. And it gets the request. In this case, it's a DBS call and it looks at the request and it gets the peer application ID and it calls out to the backend. In this case, you decay. If you were running a KD system, it would spawn the KD file chooser. And if you want, you can plug in your other back and set us non-interactive things if you want or whatever. The backend did all the UI work and sent back the final file name that the user selected or whether it was canceled or so. And then we take another portal called the document portal and we call it and say, give me a document ID for this particular file name and also let this application ID give read access to it. And then we pass the document ID back to the app and now it can read the file. This way we get the application ID is kind of weird right now. We look in this file, prop.root.flatpack.info and flatpack make sure that all sandboxes you create have that file as read-only. The app cannot modify it. It cannot use recursive namespaces so it cannot fool itself in some other ways. It looks like there's a PID race here but actually the PID is the DBS proxy PID which lives outside the sandbox so it's out of control and will live as long as the any file in the sandbox. However, it's really shitty to have this proxy DEMON and this proc stuff. So we're actually working upstream with Docker DEMON to have a proper sandbox ID and list of permissions in DBS DEMON itself. The document portal is basically like a tiny database where you can create unique IDs for a patent name and then on the side there's a fusive file system where you can access all those files. So in this case, there's a fuse amount and XTG Render that has all the... Sir, point on this thing, whatever. So all the unique IDs are listed as directories and underneath them there's the actual file. So you can get the file in them, you can get access to the file and if you have right permissions, you can replace it. But there's also a separate sub directory called byapp and beneath that, there are all the apps that have been using this and they have subsets of the entire three in them. So they're filtered by the permissions they have and when we run the app in the flat pack like the recipes subject here will be mounted at the top so it will only see its own files and will only have the right read write permissions it has. So there's basically multiple views of the entire database and you only get to see yours. You can modify files if you have access rights but all modifications are turned into atomic updates on the host or from the point of view of a different app so you can replace the file in its entirely. You cannot write in the middle of a thing that someone else has open. Also the document portal is accessible by apps even though they're sandbox. Because the way you register a file name you actually open a file descriptor like an opath file descriptor and pass it to the app so you can prove that you have access to this file then you can create a document ID for it. So this is also a way for a sandbox app to talk to a different sandbox app and grant it permission to its files which is quite useful. The Debus API for the portal itself is quite easy. So there's a single call open file and pass a bunch of properties. The first one is basically the X ID for the window so that you want to parent to and then the dialogue title and a bunch of file chooser options. And then it returns the reference handle to the request object. So it doesn't wait until it's done. It immediately returns a request object which is kind of a reference to this long running dialogue because in case your app is terminating or the window is closing or something you can terminate the request and close the dialogue. And you can wait for the signal to get the user's actual response. We have a bunch of portals. The file chooser portal is what I talked about but we have something called the open URI portal where you can basically hand over a URI and then there would be some kind of UI saying what app do you want to open this in and when you choose that you can open it in that and actually I think with these days we don't actually ask about HTTP URIs we used to open them in your, at least if you only have a single browser installed it just directly opens them. But this turns out to be a very important portal because almost all apps have like an about page where you open up the browser to their homepage or online documentations. But there's also an open file portal which is basically the same but instead of passing a URI pass a open file descriptor because we don't want to allow open URI to do file, colon URIs because then you can easily basically tell it to open things with yourself with any path name you want. So it's doing the same thing but you have to open the file descriptor first and then pass it over. We have an email portal. Basically it opens a pre-populated email client window where you can give it the subject, the address and you can start typing and send. Or if you didn't want to send it you can use close to thing. We have a printer portal. Means that the entire printing dialogue is running outside the control of your app. The app doesn't see the list of printers on your system. It doesn't see the configuration of the printer. It doesn't actually talk to the printers like what happens is that the dialogue lets you configure the details of the print and you return only the information required needed to format your page or whatever. And then you feed it a PDF and then the portal itself does the spooling to the printer. So we never talk to the printer from directly from the app. In fact, you could print to a network printer even though the app doesn't have network access. There's a screenshot portal which basically gives you a screenshot, a window with a screenshot and you can crop it and whatnot and you click on okay and it returns the results back to the app. There's an account portal which lets you basically, it's a standardized way of something called GNOME account which is access to your OAuth authentication. You can give your Google account access or Twitter account access. Inhibit and notifications are also basically wrappers around the system. Thanks for inhibiting screensaver, inhibiting suspend, doing notifications. But the important thing is that we're going through the portal so we have a point where we can decide what the user listed these apps as allowed to do notification and if you're not allowed you can use silence and it doesn't appear. So on GNOME, for instance, if you go to the control center, there's a list of all the apps and you can say these are allowed to do notifications and the rest can use, you know, disappears. Network monitor and proxy resolver are a bit different. They don't actually do UI. I think they just look at the properties of the remote, of the app that talks to them and if it has network access, it just allows them to do this. The network monitor does state changes with your own line, with your offline. These are possible to do in the sandbox but it also talks to things like network manager to tell whether you're under a captive portal or gives extra information that's harder to do if you don't have real host access. The proxy resolver is basically a way to do proxy configuration without having to shove the configuration into the sandbox. We also have an iBus portal. iBus is an input method framework for complicated scripts like Chinese, Japanese. It's a plug-in framework where there's a debus API that the client uses to talk to and it turns out that the debus API is very, very wide and you can have it load plugins and pretend to be an input method mechanism and read everyone's key presses and stuff. So we have a portal which is only exposing the minimal API required for a sandbox app to itself to input in Chinese or whatever. Other than the pure portals, there's also services that act like portals. Wayland is like the original portal. It allows you to do input and output but it doesn't give you any way to talk to other clients like where X is completely open about everything and you can easily use X messages to find your root terminal and send it keyboard input. In Wayland, a single client cannot even mention the other client's windows. There's just no way in the protocol to even refer to them. Pipewire is a project that we're working on which is basically taking pulse audio but making it work for video. And by default or initially it was all about webcams and things like that. But it's also been used for screen casting. So if you wanna get a screen grab or a video of the screen so you can do screen casting or similar tools, then you can use Pipewire to get a file descriptor that gives you basically a stream of video for the frames. And it's useful also if you wanna have a something like a webcam that you want multiple apps to access to. Because right now what you do is you open the device physically and then you have a lock on it and nobody else can access it. Whereas Pipewire is a central point that can distribute to many users. Geoclue is our geopositioning API like GPS and Wi-Fi based location. It isn't currently a portal, it's using a, it's actually like a service running as root on the system bus. But it has some kind of cold back framework where it detects that you're a flat pack and then it asks the desktop to show you some permissions dialog. It's kind of weird, but it does work. Although I think we eventually wanna move that towards like a standard portal model. Pulse Audio actually is the reverse of a portal because it's super open and you can basically poke on any stream from any app and you can upload or you can tell the demon to load DLLs and stuff. But there's work in progress to tying it down having some kind of security framework. And eventually hopefully it will be possible to make it safe. We have a bunch of work to do. One of the main thing is to make apps use portals. We try our best to make the APIs, for instance, in GTK and GLib and QT and the Akiti libraries to make them automatically use this. Or expose new APIs that underneath just uses this. And maybe it uses them when they run in a sandbox, but use something else when they're outside. Although the portals work perfectly fine even if you're an old sandbox. So they might be useful in general. We need to design, decide how to expose static permissions and do some kind of user interface study on how to best expose all of that. I wanna work with the debus upstream to make the debus proxy go away for performance reason and just like general clean, cleaniness. PipeWire is a new project, so its goal is to be run in Flatpak and to be very secure, but it's not actually right now implemented. Like the integration is not implemented. So there's some work needed there. The apps often, like this app often need access to the certificates on the host. Basically, if you have a site-wide certificate for your company or something, it's typically loaded on older machines, but the way sandboxing and containers work, you don't really have access to all the certificate files. We ship our own certificates store or see a certificate in the runtime. And there's really no sane way to expose the files because they're in between different distros, they are in a completely different ways, different file names, different formats. So P11 kit has this demon for exposing PQC S11 modules over debus or at least some kind of unix domain sockets. So I wanna look into making that exposed like a subset of your information, like a read-only access to all your CS certificates. It would be nice because then we could just give the container access to that and they could pick up any certificates automatically. Deconf is the GNOME JTK preference storage system. We have actually initial work on making it sandbox so that you get basically it's a tree of configuration option. It would be cool if you got your own tree that you could see and you couldn't see anything else but you always got access to your own tree. So I think that's blocking on the debus work so both of those are related. I guess I kinda run out of slides so if you have any questions. What do you use to send a big file through a portal for example to print a PDF? Hmm? How do you send a file through a portal if you need to print a PDF with a... Oh, you stream it to a debus to the portal and the portal uses cops or whatever. Stream it to the printer. So all the data goes through debus, I guess? Yeah, I don't actually, Matthias might know better. How do you send the actual PDF? I'm not sure actually. I mean we could just pass a file descriptor or something but I don't know for sure right now how we do it but you don't necessarily have to write all the bytes. You could pass a file descriptor or something. But I don't, at this moment, remember exactly how it works. And so does the sandboxing allow you to do things like if you have an Ethernet controller that you want to shift so that it's inside the Flatpak application but it's not visible outside? Come again? So if you have a network interface that's currently sort of like EATH one. Yeah, on the host. Yes, and you want to move that so that it's visible inside the Flatpak application but not on the outside. Yeah, so we can, we only have two stages for network. Either we share the network entirely by sharing the same network namespace and then everything will look as if you're on the outside or we do our own network namespace and then set up only loopback and you have no Ethernet connection at all. And this is kind of unfortunate but there's no way to do anything better without also having root access because you have to set up like bridges and it is really complicated and there is no way to do that without permissions. There's actually like work upstream on the EPPF networking stuff that could possibly be used but that's root only at the moment. According to Leonard, there is no real reason for it to not be usable as a user so maybe in the future we could use that but then we can basically do per app, you know, whatever kind of filtering only access this IP address or whatever. But right now the only thing we can do is all or nothing. All right, no more questions but we're out of time anyway so thanks.