--- title: Tier list of Linux security mechanisms date: 2024-06-23 tags: [code, linux, security] description: Linux has quite some security mechanisms. So let's look at some of them and rate them by usability and power. --- Linux has quite some security mechanisms. So let's look at some of them and rate them by usability and power. ## File permissions Every file has read/write/execute permissions for its owner, group, and others. This is usually expressed in octal: 755 means everyone can read and execute, but only the owner can write. 600 means everyone is blocked but the owner can read and write. This system is pretty old and has served us well. For example, home directories are typically 700 to block access from other users. And system services can run as separate users so they cannot change the system or access users' home directories. File permissions are simple and powerful. They are a foundational tool for any Linux user. However, they lack some of the flexibility of other items on the list. A Tier ## Capabilities The root user has a lot of special permissions. For example, they can access any file or bind ports below 1024. At some point, the Linux community decided to divide this role into [Capabilities](https://www.man7.org/linux/man-pages/man7/capabilities.7.html) to allow more granular access control. However, the list of Capabilities is long and complicated. Regular users will hardly be able to understand which capabilities a process needs. Some Capabilities even allow [privilege escalations](https://book.hacktricks.xyz/linux-hardening/privilege-escalation/linux-capabilities). Docker by default runs containers as root, but [drops most capabilities](https://docs.docker.com/engine/security/#linux-kernel-capabilities). Running with a limited set of capabilities is better than running as root. But in practice, you rarely even need any capabilities. Just running as a regular user will work 99% of the time. B Tier ## seccomp [seccomp](https://www.kernel.org/doc/html/latest/userspace-api/seccomp_filter.html) allows to filter which syscalls a process has access to. For me, this has much the same pros and cons as Capabilities: The list of syscalls is just too long and complicated. On top of that, creating a seccomp filter is also complicated. bwrap wants a [compiled cBPF program](https://www.man7.org/linux/man-pages/man3/seccomp_export_bpf.3.html). Docker will take a [JSON seccomp profile](https://docs.docker.com/engine/security/seccomp/). Systemd has probably the most usable interface by providing presets like `@system-service`. B Tier ## No new privileges [`PR_SET_NO_NEW_PRIVS`](https://docs.kernel.org/userspace-api/no_new_privs.html) blocks setuid (e.g. sudo) and a bunch of other privilege escalations. Highly recommended. bwrap uses it unconditionally, in systemd it is [implied by a bunch of settings](https://www.freedesktop.org/software/systemd/man/latest/systemd.exec.html#NoNewPrivileges=), and Docker has an [option to enable it](https://docs.docker.com/reference/cli/docker/container/run/#security-opt). S Tier ## AppArmor / SELinux These are effectively file permissions on steroids. Where file permissions only allow you to assign permissions for owner and group, these allow you to assign permissions for every binary individually. This means that every application can come with a specific AppArmor / SELinux profile that exactly lists the files it needs access to. My impression is that very few applications come with AppArmor or SELinux profiles. Writing them is cumbersome, especially if they are not maintained along with the application itself. I don't think these mechanisms are actively harmful. Maybe I am too harsh, but given the alternatives we will discuss later, I don't see any reason to use them. C Tier ## cgroups So far we mainly looked at mechanisms to restrict file access and syscalls. [Control Groups](https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html) allow to restrict system resources (mostly CPU and memory). This is what resource control in systemd and Docker is build upon. cgroups are a useful mechanism for servers, especially if you rent out computing time to others. For single users systems, they are much less relevant. B Tier ## Namespaces [Namespaces](https://www.man7.org/linux/man-pages/man7/namespaces.7.html) have fueled the container revolution of the past years, including Docker and Flatpak. Network and PID namespaces are useful, but the real star is mount namespaces, which allows to construct a separate file hierarchy for each process. You can use this mechanism a lot like AppArmor / SELinux, but instead of blocking access to a file, you just don't include it in the hierarchy. In that case you still have to maintain the list of files that should be made available, which is quite complex to get right. The other option is to use a completely separate file hierarchy that only shares some select data folders with the host system. This is easier, but also results in many redundant and potentially unpatched directory trees. Despite the downsides, I really like how powerful this mechanism is while also being quite intuitive. A Tier ## Landlock Landlock is yet another way to limit access to files, and was only added to the kernel in 5.13. While it could be used to sandbox applications, I think we already have more than enough mechanisms for that (AppArmor, SELinux, mount namespaces). However, it could be interesting as a mechanism for processes to restrict themselves. Landlock was actually modelled after a BSD mechanism called `pledge`, as in "I pledge to not ever access those files". C Tier ## polkit A trend in recent years is to have services that can perform privileged actions, so that applications can talk to those services over dbus instead of having to perform the privileged actions themselves. On its own, this is just a security bypass. But the services in turn ask polkit whether an action should be allowed. polkit will then consult its configuration and either allow the request, deny the request, or ask the user to authorize the request by entering their password. Polkit gives instant privilege escalation while having a voluptuous attack surface. That doesn't mean that it is insecure. But if I wanted to attack a Linux desktop system, this is where I would start. Proponents of polkit argue that it gives much more flexibility. But polkit rules decide requests mainly based on group membership. I cannot see how polkit should make nuanced decisions based on this limited information. The main benefit or polkit is that it allows to get the user into the loop. There is a good idea somewhere in here. But the current implementation is not that. Unfortunately, polkit is a central part of most modern Linux desktops. I wish we had something else, but for now we are stuck with it. D Tier ## xdg-dbus-proxy The Flatpak project realized that polkit is not sufficient and came up with an additional mechanism: They build [xdg-dbus-proxy](https://github.com/flatpak/xdg-dbus-proxy), a dbus proxy that filters which services are available. They then mount the proxy instead of the original socket in their mount namespace. (Aside: This would not be necessary if dbus would use a separate socket for each service. Then you could use mount namespaces directly without a need for a proxy.) As far as I understand, they do not do much work on this project because they want to [move this functionality into dbus itself](https://gitlab.freedesktop.org/dbus/dbus/-/issues/171). However, the ticket was created in 2017 and there has not been much progress. So I am not really sure about the status. C Tier ## Summary There you have it, this is my ranking of linux security mechanisms: - S Tier: No new privileges (great!) - A Tier: File permissions, Namespaces (tools for everyday use) - B Tier: Capabilities, seccomp, cgroups (only for select occasions) - C Tier: AppArmor / SELinux, Landlock, xdg-dbus-proxy (better options are available) - D Tier: polkit (I would like to see this one replaced) - F Tier: -