Access Control Lists
The classic permission model grants exactly three sets of rwx bits per file: one for the owning user, one for the owning group, one for everyone else. That covers most of a server, until the day two different groups need different access to the same directory tree, or one contractor needs read access without being added to the group that owns everything. The traditional model has no entry to express "this one extra user" — your only escape is to invent another group and re-chown files into it.
Access control lists (ACLs) remove that ceiling. They attach any number of named-user and named-group entries to a file or directory, each with its own rwx, layered on top of the base mode the kernel already checks. ACLs are a POSIX-draft extension stored in the filesystem's extended attributes, supported on ext4, XFS, and Btrfs and mounted with ACL support by default on Debian and Ubuntu. The operational consequence is subtler than the feature list suggests: the moment a file carries an ACL, ls -l appends a + to the mode and the group bits stop meaning what they appear to mean — they become a mask, and misreading that mask is the most common way an ACL silently grants less than you intended.
Limits of the Three-Triad Model
Standard permissions answer a fixed question with a fixed shape: one user, one group, everyone else. Picture a finance directory that the finance team writes to, the audit team must read but never write, and a single external accountant needs read access to one file. Three triads cannot encode three distinct positive grants plus a default-deny. The usual workaround — making an audit group, an accountant group, and shuffling group ownership per file — turns a permissions problem into a group-membership sprawl that nobody can reason about six months later.
ACLs solve exactly this case and only this case. When the access pattern genuinely needs per-principal rights — distinct users or groups each with their own rwx on the same object — an ACL says so directly. When the pattern is "everyone on this team gets the same access," a single group is simpler, faster for the kernel to evaluate, and far easier to audit. The discipline is to reach for ACLs for the irregular case, not as a default for ordinary shared storage.
getfacl and setfacl
Every ACL contains three base entries that mirror the classic triad: user:: (the owner), group:: (the owning group), and other::. To these you add user:name: entries for named users and group:name: entries for named groups — the entries the old model could not express. getfacl prints the full list, including the named entries that ls -l collapses into a single +; setfacl -m modifies an entry and setfacl -x removes one. Both ship in the acl package — priority optional on Debian and Ubuntu, so a minimal server may lack it and need apt install acl before either command exists.
# grant user bob read+write, audit group read-only setfacl -m u:bob:rw report.csv setfacl -m g:audit:r report.csv # ls only signals that an ACL exists — the trailing + ls -l report.csv -rw-rw----+ 1 alice finance 8214 report.csv # getfacl shows the real entries getfacl report.csv # file: report.csv # owner: alice # group: finance user::rw- user:bob:rw- group::rw- group:audit:r-- mask::rw- other::---
To remove the whole ACL and return a file to plain bits, use setfacl -b. To copy one file's ACL onto another, pipe them: getfacl source | setfacl --set-file=- target. There is no chmod equivalent that wipes named entries by accident — but chmod does interact with the mask, which is where the surprises start.
The Mask and Effective Permissions
Whenever a file has at least one named-user or named-group entry, the ACL also carries a mask:: entry. The mask is a ceiling: the effective permission of every named entry and of the owning group is the bitwise AND of that entry and the mask. If user:bob:rwx meets mask::r--, bob's effective permission is r--, no matter what his own entry says. getfacl prints this explicitly with an #effective: comment when an entry is being clipped, which is the single most useful thing to look for when an ACL "isn't working."
The trap is that chmod rewrites the mask. On an ACL-bearing file the middle group of bits in ls -l no longer shows the owning group's permission — it shows the mask. Run chmod 640 on a file where bob has an ACL entry and you have just set the mask to r--, silently downgrading every named entry to read-only while the entries themselves still read rw-. setfacl recalculates the mask to cover the entries it sets unless you pass -n, so prefer setfacl over chmod on any file you have given an ACL.
| Entry | ACL value | Mask | Effective |
|---|---|---|---|
user:bob | rwx | r-x | r-x |
group:audit | rw- | r-x | r-- |
user:: (owner) | rwx | — | rwx |
The owner entry (user::) and other:: are never masked — only named entries and the owning group are. That is why the file owner can still write a file whose mask reads r--: the mask was never in their path.
Default ACLs and Inheritance
A directory can carry a second, parallel ACL called the default ACL, set with setfacl -d. It grants nothing itself — instead it is the template that every new file and subdirectory created inside inherits as its access ACL. This is the only inheritance mechanism in POSIX ACLs: a regular ACL on a directory governs access to the directory, while the default ACL seeds children. Set a default of g:finance:rwx on a shared tree and every file dropped in by any team member is automatically group-writable, without anyone re-running setfacl.
# shared dir: finance read+write, audit read-only, for all new files setfacl -d -m g:finance:rwx /srv/finance setfacl -d -m g:audit:rx /srv/finance # apply to the directory itself AND its current contents setfacl -R -m g:finance:rwx /srv/finance
Two caveats bite here. Default ACLs only seed files created after they are set — -R fixes existing contents, the -d template does not reach backward. And inheritance is copy-on-create, not a live link: change the directory's default later and existing files keep the ACL they were born with. For a shared directory this still beats the setgid-bit approach, which can only propagate the owning group, not arbitrary per-principal rights.
Standard permissions — three triads: owner, owning group, other. Cheap for the kernel to evaluate, trivial to read in ls -l, and visible to every admin without extra tooling. Choose them whenever access splits along a single group boundary — which is most of the time.
POSIX ACLs — arbitrary named-user and named-group entries plus a mask, stored in extended attributes. Choose them only when access genuinely needs per-principal grants the triads cannot express, and you have weighed that against simply creating one more group. The complexity is justified for an irregular access matrix; it is overhead for "the whole team gets read-write."
- Running
chmodon an ACL-bearing file and silently shrinking every named entry —chmod 640rewrites the mask tor--, so a user whose ACL saysrw-can no longer write, with nothing inls -lto explain why. Usesetfaclinstead. - Reading the middle group of bits in
ls -las the owning group's permission. On a file with named entries that field is the mask, not the group entry —getfaclis the only honest view. - Setting a default ACL with
setfacl -dand expecting existing files to change. The default only seeds files created afterward; existing contents need a separatesetfacl -Rpass. - Copying or backing up with tools that drop ACLs. Plain
cploses them;cp -aorcp --preserve=allkeeps them, andrsyncneeds-A(and-Xfor other xattrs) or the destination silently reverts to base permissions. - Assuming ACLs work on any mount. They need a filesystem that supports them mounted with ACL support — default on ext4/XFS on Debian and Ubuntu, but a hand-rolled
noaclmount option or an unusual filesystem makes everysetfaclfail with "Operation not supported." - Reaching for ACLs where a group would be clearer — scattering per-user entries across a tree that the whole team shares, producing a permission layout no one can audit when a single supplementary group would have done the job.
- Prefer a supplementary group over an ACL whenever access splits along one boundary; reach for ACLs only when distinct principals genuinely need distinct rights on the same object.
- Edit ACL-bearing files with
setfacl, neverchmod—setfaclrecalculates the mask to cover your entries, whilechmodclobbers it. - Set a default ACL with
setfacl -don any shared directory so new files inherit the right access automatically, and follow it withsetfacl -Rto fix what is already there. - Run
getfaclbefore trusting an ACL and watch for the#effective:comment — it tells you exactly when the mask is clipping an entry. - Preserve ACLs in every backup and copy path:
cp -a,rsync -AX, andtar --acls, or restored data quietly loses its access rules. - Audit ACL usage with
getfacl -Ron shared trees during reviews, and collapse stray per-user entries back into groups whenever the access pattern has regularized.
chmod +a, layered over the same Unix base bitsKnowledge Check
You run chmod 640 on a file where user:bob has an ACL entry of rw-. Bob can no longer write. Why?
chmodrewrote the ACL mask tor--, and a named entry's effective permission is its own bits ANDed with the maskchmoddeleted bob's named ACL entry entirely, so his access falls back to theother::permission, which is read-only- The base mode now overrides the ACL entirely, because standard permissions always take precedence over any named entries
- Bob was removed from the file's owning group, so the group triad in the base mode no longer applies to him at all
When is reaching for an ACL the better choice than the standard permission model?
- When distinct users or groups each need their own
rwxon the same object — a grant the three triads cannot express - Whenever a directory is shared by more than one person, since a single supplementary group can never cover a directory used by a whole team
- When you need the permission check to apply faster, because the kernel evaluates the ACL entries before it ever consults the base bits
- Whenever a file must be readable by root, since the base owner-group-other permissions have no way to grant root its own access
You set a default ACL on /srv/finance with setfacl -d -m g:finance:rwx. What happens to the files already in that directory?
- Nothing — the default ACL only seeds files created afterward; existing files need a separate
setfacl -Rpass - They immediately gain the
finance:rwxentry, because the default ACL applies to the whole tree at once - They lose all of their existing ACLs, since setting a default entry replaces every access ACL on the files underneath it
- They inherit the
finance:rwxentry only the next time someone opens each of them for writing
A nightly rsync backup restores files but every ACL is gone. What is the likely cause?
- The
rsynccommand omits-A, so it never transfers the ACL extended attributes — they must be requested explicitly - ACLs cannot survive any copy across filesystems and must always be re-applied by hand after a restore
rsyncsilently strips every ACL whenever the destination directory uses a different owning group than the source- The ACL mask was clipped to
---during the transfer, hiding all the named entries from view until achmodresets it
You got correct