Linux capabilities are a kernel mechanism that divides the all-or-nothing power of root into distinct privileges, each of which can be granted to or removed from a process independently. In Linux security, they let a program bind a low network port or change file ownership without receiving full superuser rights, which directly applies the principle of least privilege.
Traditional UNIX checks asked only one question: is the effective user ID zero. Capabilities replace that single test with about forty separate checks, such as CAP_NET_BIND_SERVICE or CAP_SYS_ADMIN, so the kernel verifies the specific privilege an operation needs.
How Linux capabilities work in the kernel
Each thread carries several capability sets, which are bit masks of privileges. When a thread attempts a privileged operation, such as loading a kernel module, the kernel checks whether the corresponding capability is present in the thread’s effective set. The user ID is no longer the deciding factor for these operations.
The sets interact through precise rules, especially when a program is started with execve. The table describes each per-thread set and its role.
| Set | Meaning | Practical effect |
|---|---|---|
| Permitted | Upper bound of usable capabilities | Can be raised into effective |
| Effective | Capabilities checked by the kernel now | Determines what succeeds |
| Inheritable | Preserved across execve under conditions | Rarely sufficient alone |
| Bounding | Limit on what can ever be gained | Dropped entries are gone for good |
| Ambient | Kept across execve of unprivileged programs | Lets services pass capabilities on |
Executable files can also carry file capabilities, stored in the security.capability extended attribute. When such a file runs, the kernel combines the file sets with the thread sets, so a binary can gain one specific privilege without the setuid-root bit.
Important capabilities and their risk
Capabilities are not equally dangerous. Some grant a narrow right, while others are close to full root because they allow changes that can be turned into arbitrary code execution in the kernel or control over other processes.
| Capability | What it permits | Risk level |
|---|---|---|
| CAP_NET_BIND_SERVICE | Bind to ports below 1024 | Low |
| CAP_NET_RAW | Raw and packet sockets, as used by ping | Moderate |
| CAP_CHOWN | Change file owners arbitrarily | Moderate |
| CAP_DAC_OVERRIDE | Bypass file permission checks | High |
| CAP_NET_ADMIN | Configure interfaces, routes and firewalls | High |
| CAP_SYS_PTRACE | Trace and inspect other processes | High |
| CAP_SYS_ADMIN | Mounts, namespaces and many other operations | Near root |
CAP_SYS_ADMIN is often called the new root because it covers dozens of unrelated operations. Granting it to a container or service removes most of the benefit of using capabilities, so it should be treated as a last resort.
Managing capabilities in practice
Administrators manage capabilities through file attributes, service units and container settings. The table summarizes the main tools and their typical use.
| Tool or setting | Purpose | Example |
|---|---|---|
| setcap | Assign file capabilities | setcap cap_net_bind_service=+ep /usr/bin/app |
| getcap | Show file capabilities | getcap -r /usr/bin |
| capsh | Inspect or launch with given sets | capsh --print |
| /proc/PID/status | Show sets of a running process | CapEff and CapBnd fields |
| CapabilityBoundingSet | systemd limit for a service | CapabilityBoundingSet=CAP_NET_BIND_SERVICE |
| AmbientCapabilities | systemd grant to a non-root service | AmbientCapabilities=CAP_NET_BIND_SERVICE |
A common pattern is to run a service as an unprivileged user with one ambient capability, combined with NoNewPrivileges=yes. Container runtimes apply a similar approach: they start with a reduced default set and allow options such as --cap-drop=ALL with selected additions.
Capabilities compared with other Linux security controls
Capabilities answer which privileged operations a process may perform, but they do not restrict which files it may read as an ordinary user or which system calls it may make. Other controls in Linux security fill those gaps.
| Control | Question it answers | Granularity |
|---|---|---|
| Linux capabilities | Which root privileges are held | About forty privilege bits |
| Seccomp | Which system calls are allowed | Per system call and argument |
| SELinux | What labeled subjects may do to objects | Type enforcement policy |
| AppArmor | Which paths and resources a program may use | Path-based profiles |
| PAM | How users authenticate and start sessions | Per service login stack |
These controls are complementary. A hardened service typically drops capabilities, applies a seccomp filter and runs under a mandatory access control profile, so a single weakness does not give an attacker unrestricted control.
Pitfalls and misconceptions
Capabilities are easy to misuse because their semantics across execve are subtle. The following points summarize the errors that appear most often in audits.
- Root is still powerful without capabilities, because it owns many files and configuration paths that matter to security.
- File capabilities are lost when a binary is replaced by a package update or copied without extended attributes.
- Interpreted scripts cannot hold file capabilities effectively; setting them on the interpreter grants them to every script.
- Several capabilities allow escalation to full root, including CAP_SYS_ADMIN, CAP_SYS_MODULE and CAP_DAC_OVERRIDE.
- Inheritable alone does not pass capabilities to unprivileged child programs; ambient capabilities exist for that purpose.
A plain-language summary
On a traditional Linux system, the administrator account holds a single master key that opens every door. Linux capabilities cut that master key into many small keys. One key opens only the network door, another only the clock, another only the file ownership office.
A program receives only the keys it actually needs. If someone takes over that program, they get those few keys rather than the whole building. That simple idea greatly limits the damage of a security mistake.
An example from practice
A team deploys a small web service that must listen on port 443, which is below 1024 and normally requires root. Running the whole service as root would expose the system if the application had a vulnerability.
The team creates a dedicated service account and writes a systemd unit with User=webapp, AmbientCapabilities=CAP_NET_BIND_SERVICE, CapabilityBoundingSet=CAP_NET_BIND_SERVICE and NoNewPrivileges=yes. Checking /proc/PID/status confirms that only one capability bit is set.
The service binds port 443 normally, but it cannot change file ownership, load modules or reconfigure the network. A later penetration test finds a file upload flaw, yet the attacker gains only an unprivileged account with one narrow right.