The problem is not isolation or lack of it. The problem is that app require complex set of permissions for both users files and other apps.
App might want to send notification to notification daemon. But app should not be able to pretend to be another app, whether by name or icon. And good luck trying to stop malicious app from just making same/similar enough icon and spelling Firefox with some fancy UTF characters to go around it.
And that's pretty simple case! And already very hard on kernel/OS level to solve. Now look at files.
You might want to allow graphical editor to open any graphical file, regardless of location.
You might want to allow that same editor to only edit some of them.
But for browser, you might want to allow saving new files, but not editing/rewriting existing ones, because it is not an editor, and should have no business editing the files.
Or, allow browser tab browsing certain URL (say, web image editor) to modify the files, but not the image sharing webpage that only needs to read the file.
Now we not only have insanely granular permissions per app, the different actions from "app" (web browser is basically container for multiple applications at that point) also need different permissions.
It has nothing to do with "unix bad", or "unix wrong", to actually separate the applications without hardships on the user (like fucking with permissions every time one app needs to touch files of another app) is just very very hard
More likely, you want to temporarily give them permission to specific files you indicate. A graphical editor doesn’t have reason to read any file that the user didn’t explicitly picked for editing/viewing.
That’s how Mac OS works nowadays (possibly except for the ‘temporarily’; I don’t know the details): applications can only open files that the user selected in the system file open dialog. That runs in a separate process and opens up an app’s sandbox to allow access to the file the user selected.
> The FileChooser portal allows sandboxed applications to ask the user for access to files outside the sandbox. The portal backend will present the user with a file chooser dialog.
> The selected files will be made accessible to the application via the document portal, and the returned URI will point into the document portal fuse filesystem in /run/user/$UID/doc/.
[1]: https://docs.flatpak.org/en/latest/portal-api-reference.html...
In practice I don't have the time to debug every shitty little app armor integration for weeks. I lost days to libvirt-manager because its app armor support was enforced and not even half assed. Some configuration paths would automatically get whitelisted in its auto generated app armor profiles, others would just get you a file not found until you whitelisted them manually. The process responsible for generating these profiles would also silently kill itself if it encountered a path that was on its internal ban list, have fun debugging that when you do things like using an alternative bios rom, which by default are all stored in a blocked path.
Apparmor feels like security through obscurity, unless you already know that you are dealing with app armor fuckery there is no chance in hell that you will be able to run your application and not being able to run anything is the holy grail of security.
I used libvirt with apparmor and was pretty satisfied with it
because programs often to need to have part of the capabilities of the user which started them, just a very well controlled subset of them, something which the UNIX model can't properly represent (through you can hack it on-top of it)
There is also the problem of having by default "owner-user owner-group other" as permission sets for files and executable. This works if others is "other humans" (assuming it does work, security issues on shared systems based on that where not uncommon). But this works much less if you want to protect users from rogue programs because then "other" tends to be far to permissive.
Picture: local DB client, remote DB server. Server can stream a file to the client for the client to write to disk. “On the same machine, as a different user” is just the trivial case of “over the network.”
Next problem will be sharing data between programs that legitimately need to do so; if I had an _emacs user that owned my source code, how do I make it non-painful for the _gcc user to read the source and write the resulting executables (which would end up in a directory owned by _emacs)? What about git, various preprocessors/generators, formatters, linters?
You'd have to step out of the traditional UNIX authn/authz model to effectively implement that. It's what various security-focussed OS's have been doing for a while anyway; e.g. OpenBSD implements unveil, which "hides" entire branches of the VFS tree. For example, if git has no business reading or writing files outside of the currently operated on repository, it can restrict itself very early in the process life - before proceeding to perform any of the "tricky" operations that are the common sources of security bugs.
I think one problem with the UNIX design is that UIDs/GIDs are a flat namespace, and commonly only 32-bits in size (even on 64-bit systems), when what is really needed to meet contemporary requirements is a hierarchy, either with an unlimited number of levels, or at least generous limits. Allow a user to create sub-uids (such as one per an application) and even sub-sub-uids (a web browser might create a sub-sub-uid for each website the user visits).
I think the Windows design of variable-length SIDs is in principle superior to the POSIX approach.
(Although, not necessarily in practice - it isn’t uncommon for Windows to make design decisions which in theory are superior to those of UNIX, but the practical implementation of them is full of warts, backward compatibility hacks, arbitrary limitations, and undocumented black boxes, which end up canceling out a lot of the theoretical advantage.)
From what I understand, Linux user namespaces require you to reserve a UID range for each namespace to be mapped to its parent. Since you only have 32-bits to play with, you are forced to map multiple UIDs in the child namespace to the same UID in the parent, while many security decisions are based on the root user namespace UID only. So this is actually a lot more limiting and inflexible than Windows-style variable length UIDs would be.
The problem with this approach is that, of course, it really doesn’t scale easily. Eventually you need multi tenant, and eventually we ended just pushing everything into the database, using row level security and tenant IDs. It worked great but felt more fragile (eg, you can disable RLS)
I’m not an OS expert by any means, but I think ultimately the problem is that we’re using one operating system model for two orthogonal use cases.
I feel like need a well-defined client model - “one user with multiple apps” and a well-defined server model - “one app with multiple users”. But it’s not clear to me how the OS can help with the latter, since it’s going to be domain specific. Maybe Postgres’ model is the right answer after all.
I tried to use Apparmor and SELinux, but how policies work is beyond me. Snap's sandboxing seems to be the closest thing to user-friendly sandboxing, but it's still not that user-friendly.
I mean, the correct approach would be to have groups even for specific network protocols because capabilities are not enough to sandbox a binary correctly, and the network group is pretty much pointless.
And then there's icmp, which brings us to the ping binary which on lazy distributions still has an SUID flag set, as well as glibc which still allows LD_PRELOAD by default because it is intended functionality from the perspective of its developers.
Most of these privilege escalation exploits can be mitigated, if users and groups and capabilities are managed correctly.
In practice I probably would recommend to use the systemd seccomp sandboxes because most of these quirks have been abstracted away there and are configurable in the service files - like file/folder access, user/group randomization, chrooting, capabilities etc.