---
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 of 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: -
