Users & Permissions

ACLs & Attributes

POSIX ACLs and extended attributes for permissions beyond owner/group/other.

The classic rwx bits express exactly three audiences: one owner, one group, everyone else. Real systems routinely need more nuance, such as “these two specific users may write, this third may only read, and no one else has access.” Cramming that into three triples forces ugly workarounds like extra groups or loosened permissions. POSIX ACLs, file capabilities, and file attributes fill the gaps: per-user grants, root’s power sliced into pieces, and protection even root cannot casually undo. This page shows when each is worth the added complexity. The acl and libcap2-bin tools installed in Verify The Environment are required; snapshot the lab first.

POSIX ACLs: getfacl And setfacl

An ACL attaches additional permission entries to a file beyond the standard owner/group/other. You read them with getfacl and set them with setfacl. A + at the end of an ls -l mode line signals a file has an ACL.

Create a file and grant one specific user write access without touching its group:

touch /tmp/shared.log
setfacl -m u:alice:rw /tmp/shared.log
ls -l /tmp/shared.log
getfacl /tmp/shared.log

Expected output:

-rw-rw-r--+ 1 ubuntu ubuntu 0 Jul  6 09:50 /tmp/shared.log
# file: tmp/shared.log
# owner: ubuntu
# group: ubuntu
user::rw-
user:alice:rw-
group::r--
mask::rw-
other::r--

The user:alice:rw- line is the extra grant; the + in -rw-rw-r--+ flags its presence. The mask entry caps the maximum effective permission for named users and the group, a subtlety worth knowing: if the mask is r--, an alice:rw- entry is effectively reduced to r--. Default ACLs on a directory (setfacl -d) are inherited by new files created inside, which is how you set up a shared drop directory. ACLs are worth the complexity when you have genuinely per-user access needs; if a single group would do, use a group, because ACLs are easy to overlook in an audit (always check for the +).

Extended Attributes And File Capabilities

Beneath ACLs sits a more general facility: extended attributes, key-value metadata stored on the file in namespaces like security, system, and user. The security-critical use of them is file capabilities, which split root’s monolithic power into discrete privileges. Instead of making a program setuid-root (all of root, from Setuid & Setgid), you grant it only the one capability it needs.

For example, binding to a port below 1024 normally requires root. With capabilities, a program can get cap_net_bind_service and nothing else. Inspect what already carries capabilities:

getcap -r /usr/bin 2>/dev/null

Expected output:

/usr/bin/ping cap_net_raw=ep

ping holds cap_net_raw (raw sockets) rather than being setuid-root, so a bug in ping yields raw-socket access, not full root. That is a much smaller blast radius. Grant a capability with setcap and confirm it:

sudo setcap cap_net_bind_service=+ep /tmp/whoisit
getcap /tmp/whoisit

Expected output:

/tmp/whoisit cap_net_bind_service=ep

Capabilities are worth reaching for whenever a program needs a single root power; they turn “all of root” into “just this,” which is the whole spirit of least privilege. The auditing caution mirrors setuid: enumerate capability-bearing files (getcap -r /) and flag anything unexpected, because a stray cap_setuid=ep on a scriptable binary is as good as root.

NeedReach forInstead of
Extra per-user access on a filePOSIX ACL (setfacl)A new group or looser bits
One root power for a programFile capability (setcap)setuid-root
Make a file tamper-resistantImmutable attribute (chattr +i)Read-only permissions

Immutable Files With chattr +i

File attributes (distinct from extended attributes, though related) are per-file flags managed with chattr and viewed with lsattr. The security star is the immutable flag, +i: a file marked immutable cannot be modified, deleted, renamed, or linked to, even by root, until the flag is removed. It is a strong protection for files that should never change, like a hardened config or an audit log.

echo "critical config" | sudo tee /tmp/locked.conf
sudo chattr +i /tmp/locked.conf
lsattr /tmp/locked.conf
sudo rm -f /tmp/locked.conf

Expected output:

critical config
----i---------e------- /tmp/locked.conf
rm: cannot remove '/tmp/locked.conf': Operation not permitted

The i in the lsattr output is the immutable flag, and even root’s rm fails. Note the catch that makes it a real control: only root (specifically CAP_LINUX_IMMUTABLE) can set or clear it, so an attacker who has merely compromised a service account cannot quietly rewrite the file. To edit it legitimately you clear the flag first with sudo chattr -i, change the file, then re-set it. Because it stops even root, immutability is also a foot-gun: document every file you lock, or a future admin will be baffled by a file that refuses to change.

These mechanisms extend the model from File Permissions and complement the delegation covered in Sudo and Setuid & Setgid. Capabilities in particular are the modern, least-privilege answer to many problems that once demanded setuid-root.

Practical Guidance

  1. Watch for the + on ls -l mode lines; an ACL grants access the standard bits do not show, and it is easy to miss.
  2. Use ACLs only when per-user access is genuinely needed; a shared group is simpler to reason about and audit.
  3. Mind the ACL mask; a restrictive mask silently reduces named-user and group entries below what they appear to grant.
  4. Prefer file capabilities over setuid-root, granting the single power a program needs and no more.
  5. Enumerate capability files with getcap -r / as part of any audit, and treat cap_setuid or cap_dac_override as near-root.
  6. Reserve chattr +i for files that truly must not change, and document each one so the immutability is not a future mystery.