Skip to main content
RunBook Academy

LinuxXXX · Linux Capabilities and PrivilegeCapabilities overview

Linux capabilities overview - the full list and categories

Intermediate⏱ ~12 mingetcapsetcapcapsh

What you'll learn

  • List capabilities by category
  • Use getcap and setcap
  • Understand capability bounding set
  • Recognise capability trade-offs

Prerequisites

Verified against Ubuntu 24.04 LTS · Debian 12 (Bookworm) · RHEL 9.x · Rocky Linux 9.x · AlmaLinux 9.x · Linux kernel 6.1 LTS / 6.6 LTS · systemd 255+ · OpenSSH 8.7p1 (RHEL 9) / 9.6p1 (Ubuntu 24.04) · nftables 1.0.x · chrony 4.x · Pacemaker 2.1.x · Corosync 3.1.x · 2026-08-09

Not yet marked complete on this device.

Linux has around 40 capabilities. This lesson covers the categories, the tools to manage them, and the trade-offs of using capabilities vs full root.

Capabilities by category

File and permissions

  • CAP_CHOWN: make arbitrary changes to file UIDs and GIDs (chown(2)). This is the capability that lets a process give a file away or take it over.
  • CAP_DAC_OVERRIDE: bypass file read, write, and execute permission checks.
  • CAP_DAC_READ_SEARCH: bypass file read permission checks and directory read/execute checks.
  • CAP_FOWNER: bypass the checks on operations that normally require the process’s filesystem UID to match the file’s UID (chmod, utime, setting inode flags), excluding what CAP_DAC_OVERRIDE and CAP_DAC_READ_SEARCH already cover.
  • CAP_FSETID: do not clear the set-UID and set-GID mode bits when a file is modified, and set the set-GID bit on a file whose GID does not match the caller’s. It has nothing to do with changing ownership - that is CAP_CHOWN.
  • CAP_SETFCAP: set file capabilities on any file. Treat this as root-equivalent: a process holding it can write a capability onto a binary it controls and re-acquire anything the bounding set still permits.
  • CAP_LINUX_IMMUTABLE: set the immutable and append-only inode flags.
  • CAP_LEASE: establish leases on files.
  • CAP_MKNOD: create device files with mknod(2).

Network

  • CAP_NET_ADMIN: network configuration - interfaces, routes, firewall rules, socket options. Broad; covers most network administration.
  • CAP_NET_BIND_SERVICE: bind to ports < 1024.
  • CAP_NET_BROADCAST: make socket broadcasts and listen to multicasts. Marked (Unused) in the man page - the kernel does not currently check it, so granting it buys nothing.
  • CAP_NET_RAW: use RAW and PACKET sockets, and bind to any address for transparent proxying.

Process

  • CAP_KILL: send signals to processes of other users.
  • CAP_SETPCAP: drop capabilities from the calling thread’s bounding set, and add capabilities to the inheritable set.
  • CAP_SYS_PTRACE: trace processes (gdb, strace).
  • CAP_SYS_CHROOT: use chroot.
  • CAP_SYS_NICE: set nice values, scheduling policy and CPU affinity arbitrarily.
  • CAP_SYS_RESOURCE: override resource limits.
  • CAP_SYS_TIME: set system clock.
  • CAP_SETUID: change UID.
  • CAP_SETGID: change GID.

System

  • CAP_SYS_ADMIN: extensive; mount, swapon, key management, namespace operations, and many more. Effectively the “new root” - avoid granting it.
  • CAP_SYS_BOOT: reboot.
  • CAP_SYS_MODULE: load/unload kernel modules.
  • CAP_SYS_RAWIO: raw I/O operations (/dev/mem, port I/O).
  • CAP_IPC_LOCK: lock memory (mlock, mlockall, shared memory) - what a database needs to keep pages out of swap.
  • CAP_MAC_ADMIN: change the MAC (Smack/SELinux) policy.
  • CAP_MAC_OVERRIDE: bypass MAC policy enforcement.
  • CAP_BPF: perform privileged BPF operations.
  • CAP_PERFMON: use perf and other performance-monitoring interfaces.
  • CAP_CHECKPOINT_RESTORE: checkpoint and restore processes (CRIU).
  • CAP_SYSLOG: read the kernel log; configure syslog.
  • CAP_AUDIT_WRITE: write to the audit log.
  • CAP_AUDIT_CONTROL: enable/disable auditing and change the audit rules.
  • CAP_AUDIT_READ: read the audit log.

The lists above are the ones you meet in production, not the whole set. Generate the authoritative list for the kernel you are actually running, and check each entry against man 7 capabilities before you grant it:

capsh --print | grep '^Bounding set' | tr ',' '\n'

Capabilities used by services

ServiceCapability
Web server (bind 80)CAP_NET_BIND_SERVICE
pingCAP_NET_RAW
tcpdumpCAP_NET_RAW
cronCAP_DAC_OVERRIDE (for /etc/cron.allow)
sshdCAP_AUDIT_WRITE, CAP_SYS_CHROOT
dhclientCAP_NET_ADMIN
mountCAP_SYS_ADMIN
ping6CAP_NET_RAW

Tools

# Read capabilities of a file
getcap /usr/local/bin/myservice
# /usr/local/bin/myservice cap_net_bind_service=ep

# Set capabilities on a file - a single-purpose binary only
sudo setcap cap_net_bind_service=+ep /usr/local/bin/myservice

# Remove
sudo setcap -r /usr/local/bin/myservice

# Decode a capability mask
capsh --decode=0000003fffffffff

# Drop capabilities for the current shell
sudo capsh --drop=cap_net_bind_service --print

Capability sets per process

For each process:

cat /proc/$$/status | grep Cap
# CapInh: 0000000000000000   # Inheritable
# CapPrm: 0000000000000000   # Permitted
# CapEff: 0000000000000000   # Effective
# CapBnd: 0000003fffffffff   # Bounding
# CapAmb: 0000000000000000   # Ambient

Decode the bounding set to see what the process can ever acquire.

Bounding set

The bounding set is a per-thread attribute, and has been since Linux 2.6.25. The old system-wide bounding set no longer exists. It is inherited at fork(), preserved across execve(), and it caps which capabilities a thread can ever gain from a file’s permitted set.

A thread holding CAP_SETPCAP drops capabilities from its own bounding set with prctl(PR_CAPBSET_DROP). The drop is one-way: a capability removed from a bounding set cannot be put back, and every descendant inherits the reduced set. Dropping from one process’s bounding set has no effect on unrelated processes on the host.

In a unit file the two are set together, because they defend different paths:

[Service]
User=www-data
CapabilityBoundingSet=CAP_NET_BIND_SERVICE   # nothing else is ever obtainable
AmbientCapabilities=CAP_NET_BIND_SERVICE     # grant it without setuid or file caps
NoNewPrivileges=true                         # separate control: no gain on execve

Verify what is actually in effect rather than trusting the file:

systemctl show -p CapabilityBoundingSet,AmbientCapabilities,NoNewPrivileges myservice
grep Cap /proc/$(systemctl show -p MainPID --value myservice)/status
capsh --decode=0000000000000400        # decode the CapBnd value you just read

systemd-analyze security myservice scores the whole sandbox and names the directives you have not set yet.

For containers, drop capabilities the container does not need before starting (docker run --cap-drop=ALL --cap-add=NET_BIND_SERVICE). That reduces the bounding set of the container’s init thread, and every process in the container inherits the reduced set.

File capabilities

A binary can have capabilities stored in its extended attributes (xattrs). When the binary is exec’d, the capabilities are added to the process.

sudo setcap cap_net_bind_service=+ep /usr/local/bin/myservice
getcap /usr/local/bin/myservice
# /usr/local/bin/myservice cap_net_bind_service=ep

The +ep means: in the Effective and Permitted sets after exec.

The target is a single-purpose binary. Do not put file capabilities on an interpreter, a shell, or a packaged binary that a package upgrade will silently replace - an upgrade rewrites the inode and drops the xattr, so the service starts failing at the next patch window with no change record to explain it.

Grant capabilities to the service, not the file

For anything managed by systemd, AmbientCapabilities= is the better tool. It grants the capability to that service instance only, so it survives package upgrades and gives nothing to the users who can execute the same binary:

# /etc/systemd/system/myservice.service.d/override.conf
[Service]
User=myservice
AmbientCapabilities=CAP_NET_BIND_SERVICE
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
NoNewPrivileges=true

AmbientCapabilities= requires a non-empty CapabilityBoundingSet= containing the same capability; systemd raises the ambient set for the service, and the bounding set caps what it can ever hold.

If the only requirement is a low port, skip capabilities altogether and lower the privileged-port threshold:

sudo sysctl -w net.ipv4.ip_unprivileged_port_start=80
# Persist in /etc/sysctl.d/99-unprivileged-ports.conf

setuid vs file capabilities

setuidFile capability
Privilege grantedFull rootSpecific capability
Use caseLegacy compatModern services
Blast radiusAll of rootJust the capability
AuditEasy (setuid bit)Requires getcap

Replace setuid with file capabilities where possible. A service that previously required setuid root can run as a non-root user with just the capabilities it needs.

Knowledge check

Knowledge check · 4 questions

  1. Q1. Which capability allows binding to port 80?

  2. Q2. setuid binaries should be preferred over file capabilities.

  3. Q3. Which of the following are valid capabilities? Select all that apply.

  4. Q4. A change ticket says "hardened: CAP_SYS_ADMIN and CAP_SYS_MODULE can no longer be acquired by the service". The diff adds only NoNewPrivileges=true to the unit. What is the state of the service after the deploy?

Passing score: 75%. Answers are checked in this browser.