The third permission slot
You have a project directory shared between three developers. Two are in the dev group. One contractor is not. You need the contractor to read files but not write them. Changing the group breaks the other two. chmod o+r exposes everything to the world. You need a third permission slot. That is what Access Control Lists provide.
ACLs let you grant specific permissions to named users or groups without altering the file owner or the primary group. They sit on top of standard Unix permissions and give you granular control when the owner/group/other model is too rigid.
How ACLs extend Unix permissions
Standard permissions are a lock with three keys: owner, group, and other. ACLs turn that lock into a keycard system. You can issue specific cards to specific people. The kernel checks the ACL entries before falling back to the standard permission bits.
The ACL model adds two concepts that change how you think about access:
- Named entries: You can add
user:alice:rw-orgroup:contractors:r--directly to the file metadata. - The mask: A ceiling that limits the effective permissions for all named entries and the group owner. The mask is where most ACL confusion happens. If the mask is
r-x, a named user withrwxin the ACL only getsr-xeffective access.
ACLs do not replace standard permissions. They extend them. The owner entry in the ACL maps to the owner bits. The group entry maps to the group bits. The other entry maps to the other bits. Named entries and the mask are the new additions.
Verify filesystem support
Fedora ships with the acl package providing setfacl and getfacl. Most filesystems used on Fedora support ACLs natively. ext4 and XFS enable ACLs by default on modern kernels. You do not need to edit /etc/fstab to add acl unless you are running an ancient configuration or a custom kernel.
Install the tools and verify support in one step.
sudo dnf install acl # install setfacl and getfacl utilities
getfacl /tmp # test if the filesystem supports ACLs
# If this returns permission entries, ACLs are supported.
# If you see "Operation not supported", the filesystem lacks ACL support.
If getfacl fails with Operation not supported, check your mount options. For ext4, verify the filesystem feature set.
tune2fs -l /dev/sdXN | grep -i acl # check ext4 filesystem features
# Look for "acl" in the list of mounted features.
# If missing, the filesystem was created without ACL support.
# Recreating the filesystem is the only fix for this rare case.
Run getfacl on a test path first. If it works, you are ready. Do not waste time checking mount output for the acl flag. Modern kernels often hide the flag even when ACLs are active.
Set permissions with setfacl
Use setfacl to modify ACLs. The -m flag modifies existing entries or adds new ones. The syntax is u:user:perms for users and g:group:perms for groups.
Grant a user read and execute access to a directory.
sudo setfacl -m u:alice:rx /var/www/html # add alice with read and execute
# -m modifies the ACL without removing other entries.
# u:alice:rx targets the named user alice.
# rx allows reading directory contents and traversing into it.
Verify the change immediately. ls -l shows a + sign when ACLs are present, but it hides the details. Always use getfacl to read the full state.
getfacl /var/www/html # display the complete ACL
# Look for user:alice:r-x in the output.
# The + in ls -l confirms ACLs exist but gives no detail.
# getfacl is the only reliable way to audit permissions.
Apply permissions recursively to existing files and subdirectories. Use -R with caution on large trees. Recursive operations can take time and generate significant I/O.
sudo setfacl -R -m u:alice:rx /var/www/html # apply to all files and dirs
# -R processes the directory tree recursively.
# This updates every file and subdirectory under the path.
# Large directories may cause noticeable latency during execution.
Remove a specific entry with -x. Remove all extended ACLs with -b.
sudo setfacl -x u:alice /var/www/html # remove alice's named entry
# -x deletes the specified entry only.
# Standard permissions and other ACL entries remain intact.
sudo setfacl -b /var/www/html # strip all extended ACLs
# -b removes all named entries and the mask.
# The file reverts to standard owner/group/other permissions.
Check getfacl output after every change. The + sign in ls -l is a warning, not a report.
The mask controls effective access
The mask entry limits the maximum effective permission for named users, named groups, and the owning group. The owner entry is exempt from the mask. The other entry is exempt from the mask. Everything else is clipped by the mask.
If you set u:alice:rwx but the mask is r-x, alice gets r-x effective access. The ACL entry shows rwx, but the effective permission is r-x. This is intentional. The mask protects against accidental over-permissioning when multiple entries are added.
Inspect the mask in getfacl output.
# file: var/www/html
# owner: apache
# group: apache
user::rwx
user:alice:rwx
group::r-x
mask::r-x
other::r-x
In this output, alice has rwx in the ACL, but the mask is r-x. Alice's effective permission is r-x. The getfacl output often shows the effective permission in parentheses, like user:alice:rwx (r-x).
Reset the mask to allow full permissions if needed.
sudo setfacl -m m::rwx /var/www/html # set mask to full permissions
# m::rwx updates the mask entry.
# This allows named entries to use their full defined permissions.
# Be careful: widening the mask grants access to all named entries.
Use setfacl -m m::rwx when you want named users to have the permissions you explicitly assigned. Use a restrictive mask when you want to cap access for all named entries at once.
Run getfacl and check the effective permissions in parentheses. If the effective permission is lower than expected, the mask is the culprit.
Default ACLs for inheritance
Access ACLs apply to the file or directory you modify. They do not automatically apply to new files created inside a directory. Default ACLs solve this. Default ACLs are stored on directories and copied to new files and subdirectories created within them.
Set a default ACL so new files inherit permissions.
sudo setfacl -d -m u:alice:rx /var/www/html # set default ACL for alice
# -d sets a default ACL on the directory.
# New files created here will inherit u:alice:rx.
# Default ACLs only exist on directories, not files.
Default ACLs also define the default mask and default group entry for new objects. If you set a default ACL for a user, the system creates a default mask automatically based on the union of all default named entries.
Verify default ACLs with getfacl. Default entries are prefixed with default:.
getfacl /var/www/html # check for default: entries
# Look for default:user:alice:r-x.
# The default mask controls inheritance for new files.
# Files inherit the default ACL as their access ACL.
Combine recursive and default ACLs to secure a directory tree. Apply access ACLs to existing files and default ACLs to directories.
sudo setfacl -R -m u:alice:rx /var/www/html # set access ACLs recursively
sudo setfacl -d -m u:alice:rx /var/www/html # set default ACL on dirs
# The first command fixes existing files.
# The second command ensures future files get the right permissions.
# Default ACLs only apply to directories in the tree.
Default ACLs are essential for shared directories. Without them, users create files with restrictive permissions that break the sharing model.
SELinux and ACLs work together
ACLs are Discretionary Access Control. SELinux is Mandatory Access Control. The kernel checks DAC first. If the ACL denies access, the request fails immediately. If the ACL allows access, the kernel checks SELinux. If SELinux denies access, the request fails.
ACLs do not bypass SELinux. You can grant alice full read/write via ACL, but if SELinux policy forbids alice's domain from writing to that context, the write fails.
Check the SELinux context alongside ACLs.
ls -Z /var/www/html # show SELinux context
# Look for context like system_u:object_r:httpd_sys_content_t:s0.
# The context determines which SELinux policies apply.
# ACLs and SELinux are independent layers. Both must allow access.
If access fails despite correct ACLs, check for SELinux denials.
sudo journalctl -t setroubleshoot | tail -20 # check for SELinux denials
# setroubleshoot logs one-line summaries of denials.
# Look for messages mentioning the user and the file path.
# SELinux denials appear even if ACLs are correct.
Adjust SELinux booleans or file contexts if needed. Do not disable SELinux to fix ACL issues. Fix the policy instead.
sudo semanage fcontext -a -t httpd_sys_rw_content_t '/var/www/html(/.*)?' # set context
sudo restorecon -Rv /var/www/html # apply context recursively
# semanage defines the correct context for the path.
# restorecon applies the context to existing files.
# This allows services to write to the directory under SELinux.
SELinux and ACLs are friends. One checks identity and ownership. The other checks process policy and file type. Both must say yes.
Common pitfalls
- The mask clips permissions. You set
rwxbut the user getsr-x. Check the mask. Reset it withsetfacl -m m::rwxif needed. ls -llies. The+sign hides details. Permissions shown inls -lare the intersection of the group entry and the mask. Usegetfaclfor truth.- Default ACLs only apply to directories. Setting
-don a file does nothing. Default ACLs must be on the parent directory to affect new files. - Recursive operations are slow.
setfacl -Ron millions of files takes time. Run it during maintenance windows. - Network filesystems vary. NFS and CIFS support ACLs differently. Ensure the server and client both support POSIX ACLs. Check mount options for
aclon network shares. - Backups may strip ACLs. Some backup tools do not preserve ACLs. Verify your backup solution supports
--aclor equivalent flags.
Run getfacl before and after changes. Trust the output, not the intuition.
When to use ACLs vs alternatives
Use ACLs when you need per-user permissions on a shared directory without creating a new group. Use standard groups when access is binary and applies to a whole team. Use SELinux when you need to restrict a service account to specific paths regardless of file ownership. Use ACLs when you need fine-grained read/write splits for individuals on the same file. Use default ACLs when you want new files to inherit permissions automatically in a shared workspace.
ACLs fill the gap between coarse group permissions and complex policy engines. They are the right tool for shared directories, contractor access, and multi-user collaboration.
Check the mask before you blame the user. The mask is the silent killer of ACL permissions.