In the previous article, we discussed how file and directory access permissions work in Linux, what the chmod and chown commands are used for, and how to manage read, write, and execute permissions. In most cases, the standard model - where each file has an owner, a group, and a set of permissions for other users - is sufficient.
But what if multiple users are working with a single directory and need different levels of access? For example, a developer should be able to edit website files, a backup system should only be able to read them, and other users shouldn’t be able to see the directory’s contents at all. Solving this problem using the standard Linux tools described in the previous article can be quite difficult - or, more accurately, inconvenient - especially if changing the owner or group composition is undesirable for one reason or another.
For situations like this, Linux provides ACLs (Access Control Lists). They allow you to assign individual permissions to users and groups while preserving the existing owners of files and directories.
In this article, we’ll walk through how to configure ACLs and set up permission inheritance for new files, as well as explore how to verify exactly which permissions a user has actually been granted.
What Are ACLs and How Do They Differ from chmod
Standard permissions for files and directories in Linux use the designations r (read), w (write), and x (execute). These are assigned to the file owner, its group, and other users.
For example, if we run the following command, specifying any directory:
ls -ld /var/www/project
, we’ll see a result similar to this:
drwxr-x--- 2 www-data www-data 4096 Oct 20 12:00 /var/www/project
In this example, we can see that the directory is owned by the system user www-data. This user has full access to the directory, the group can read the directory’s contents, and other users have no access at all.
Now let’s assume that a developer with the username developer needs to be granted access to edit the files in this project. Of course, we could add them to the www-data group, but then they would also receive group permissions for other objects where that group is used.
ACLs, however, allow us to grant permissions directly to the user developer and directly on the target directory or file.
Below, we’ve compiled a clear table showing the differences between chmod/chown and the ACL tool.
|
Feature |
chmod/chown |
ACL |
|---|---|---|
|
Owner and primary group permissions |
Yes |
Yes |
|
Individual permissions for other users |
No |
Yes |
|
Permissions for additional groups |
No |
Yes |
|
Permission inheritance |
No |
Yes |
|
Permission restrictions through a mask |
No |
Yes |
It turns out that ACLs complement standard Linux permissions, and both mechanisms work together. Therefore, changing permissions via chmod can affect ACLs - something you need to be prepared for and always keep in mind.
Installing ACLs in Linux
In modern ext4 and XFS file systems, which run on most servers and workstations, POSIX ACL support is usually enabled by default. To manage permissions, you need the setfacl and getfacl utilities.
In Debian and Ubuntu, run the following commands to install them:
sudo apt update sudo apt install acl
In Rocky Linux and AlmaLinux:
sudo dnf install acl
To check if the utility is present on your system, run the following command:
getfacl --version
If this command runs and returns a result, the tools are installed. However, it’s best to verify ACL support in the file system itself separately, especially if the files are located on network storage.
How to View and Change ACL Permissions
Let’s create a directory and set restricted access for it:
sudo mkdir -p /srv/shared sudo chmod 750 /srv/shared
Now let’s view the current permissions for this directory using the utility we installed earlier:
getfacl /srv/shared
Sample output:
# file: srv/shared # owner: root # group: root user::rwx group::r-x other::---
Here we see standard Linux permissions, and there are no additional entries yet.
Assigning Permissions to a User
Now it’s time to grant additional permissions. Suppose the user developer needs full access to the directory. In that case, we need to add the corresponding entry:
sudo setfacl -m u:developer:rwx /srv/shared
In this command, the -m parameter modifies the ACL, u:developer specifies the user, and rwx sets the permissions.
After that, let’s check what has changed:
getfacl /srv/shared user::rwx user:developer:rwx group::r-x mask::rwx other::---
We can see that now the user with the login developer can view the directory’s contents and create files in it. Note, however, that permissions on the directory itself do not automatically grant access to edit all files inside it.
Let’s check whether our user can create a file by running the following command on their behalf:
sudo -u developer touch /srv/shared/test.txt
If there are no errors, it means the permissions are working.
Assigning Permissions to a Group
As we recall, we have a group backup that needs read-only access to create backups. To grant these permissions, we use a similar command, but instead of specifying the user u:developer, we specify the group g:backup:
sudo setfacl -m g:backup:rx /srv/shared
If this permission needs to be applied to the entire existing directory tree, then we use the -R option :
sudo setfacl -R -m g:backup:rX /srv/shared
The uppercase X in the setfacl command works slightly differently than the lowercase x. It grants execute permission to all directories, and to regular files only if they already have execute permission for at least one user group.
For directories, the x permission is necessary so that a user can enter them and access the files inside.
However, for regular files, such as .txt, .html, or .jpg, this permission is usually not required.
Therefore, when configuring ACLs recursively, it is more convenient to use X: this ensures that directories receive the necessary access rights, existing executable scripts remain executable, and regular files do not accidentally become executable.
Inheriting Permissions via Default ACLs
Standard ACLs apply to specific files and directories. But if, for example, a new file appears tomorrow in the /srv/shared directory, the developer user might not have access to it.
To solve this problem, there are so-called Default ACLs - inherited permissions. They are set using the command we’re already familiar with, but with the -d option:
sudo setfacl -d -m u:developer:rwx /srv/shared
Now, new files created inside our directory, as well as its subdirectories, will inherit the entry created for the developer user.
You can verify the inheritance settings with the following command:
getfacl -d /srv/shared
Example:
default:user::rwx default:user:developer:rwx default:group::r-x default:mask::rwx default:other::---
However, ACL inheritance has several peculiarities. Suppose we’ve configured the developer user with rwx permissions by default:
sudo setfacl -d -m u:developer:rwx /srv/shared
Obviously, all new files and subdirectories within /srv/shared will now inherit this entry. However, the actual permissions depend on the type of object being created.
For example, when a program creates a regular text file, it most often requests permissions 0666 - read and write for all user categories, without execute permission. Therefore, even if rwx is present in the Default ACL, the developer user will only receive rw- for such a file.
The situation is different with directories. They are usually created with the requested permissions 0777, which include execute permissions, so a new subdirectory can inherit the full set of rwx permissions.
What is umask and how is it related to ACLs
In Linux, there is another mechanism for managing permissions - umask (user file-creation mode mask). This is a mask that restricts access permissions when creating new files and directories.
By default, programs typically request permissions of 0666 for files and 0777 for directories. However, granting all these permissions to every user is insecure, so Linux applies the umask, which removes certain access bits.
You can view the current mask using the command:
umask
For example, if the result is 0022, new objects will typically be granted the following permissions:
|
Object |
Requested Permissions |
umask |
Final Permissions |
|---|---|---|---|
|
File |
|
|
|
|
Directory |
|
|
|
In other words, the file owner can read and modify it, while other users can only read it. For directories, the ability to enter the directory is additionally preserved.
You might ask, how does this relate to Default ACL? We’ll explain.
If the parent directory has Default ACLs configured, Linux uses inherited rules and the mode requested by the program. The usual umask is not applied separately in this case.
Therefore, a file created in a directory with Default ACLs may have broader permissions than the usual 0644. It all depends on the configured inheritance rules and the creation mode.
And one last detail you shouldn’t forget: Default ACLs apply only to new objects. Existing files retain their original permissions, so to change them, you’ll need the standard command setfacl -m, with the -R option for recursive configuration if necessary.
ACL Mask and Effective Permissions
Earlier, we discussed umask, which limits permissions when creating new files and directories. But ACLs also have their own mask - mask - which works differently.
The ACL mask defines the maximum permissions that users and groups can receive through ACL entries. It affects actual access to existing files and directories, not just the permissions set at creation.
For example, the user developer has been assigned rwx permissions, but for some reason, they cannot modify the file. Let’s check the ACL:
user:developer:rwx #effective:r-x mask::r-x
Here, the entry user:developer:rwx grants read, write, and execute permissions. However, the mask mask::r-x denies write access, so the user’s actual (effective) permissions are limited to r-x. This is exactly what the comment #effective indicates.
The ACL mask applies to additional user entries, the file’s primary group, and additional groups. It does not apply to the file owner (user::) or other users (other::).
You can change the mask using the command:
sudo setfacl -m m::rwx /srv/shared
After that, the mask will grant all rwx permissions, but each user will still be limited by the permissions of their own ACL entry.
However, there is a caveat here as well. Expanding the mask can simultaneously increase the actual permissions of multiple users and groups. Therefore, it’s best to review the entire list using getfacl before making any changes.
Another difference from umask relates to the chmod command. If a file has extended ACLs configured, changing group permissions via chmod typically modifies the ACL mask itself. For example:
sudo chmod g-w /srv/shared
This command will remove write permission from the directory’s ACL mask if it has extended ACLs. As a result, users who were previously allowed to write via ACLs may lose that ability.
You can check the final permissions with the command:
getfacl /srv/shared
So, let’s summarize: umask limits permissions when creating objects, while the ACL mask (mask) limits the effective permissions granted by ACL entries.
Removing ACLs and Backing Up
You can remove a specific user’s permissions using the command:
sudo setfacl -x u:developer /srv/shared
To remove additional ACL entries, use:
sudo setfacl -b /srv/shared
To remove all inherited Default ACL rules:
sudo setfacl -k /srv/shared
In fact, before making any bulk changes to ACLs, we can protect against errors by saving the current ACLs - for example, to a plain text file acl-backup.txt - using the command:
sudo getfacl -R -p /srv/shared > acl-backup.txt
Restoring from this file is even easier:
sudo setfacl --restore=acl-backup.txt
The backup file contains the original paths, so before restoring, make sure they match the correct directories.
How to Preserve ACLs When Transferring Files
Unfortunately, additional permissions may be lost when copying files. Therefore, when transferring files - for example, using rsync - use the -A option:
sudo rsync -aA /var/www/project/ /backup/project/
In this example, the -aoption preserves the basic file attributes, while the -Aoption preserves the ACL lists.
If you need to preserve extended file system attributes, add the -Xoption:
sudo rsync -aAX /var/www/project/ /backup/project/
Both the source and destination must support these attributes. After transferring the files, check their permissions using the getfacl command, which we’re already familiar with.
Why ACLs Might Not Work
Even if the setfacl command is executed without errors, the user does not always gain the expected access to files or directories. The reason may lie, for example, in the ACL mask, insufficient permissions on parent directories, or file system-specific characteristics. Sometimes access is blocked by additional security mechanisms, such as SELinux or AppArmor.
Let’s break down the most common problems you might encounter when configuring ACLs and how to diagnose them, but only in a clear, tabular format:
|
Problem |
Possible Cause |
Check |
|---|---|---|
|
|
Insufficient effective permissions |
|
|
Cannot access directory |
Missing |
|
|
|
ACL not supported |
|
|
Permissions are restricted |
ACL mask |
|
|
New files have incorrect permissions |
Incorrect Default ACL configuration |
|
|
ACL lost after migration |
File attributes were not preserved |
Copy options |
Check SELinux or AppArmor separately if they are enabled on your server, as these mechanisms may deny access even with correct ACLs.
There is also a quirk when moving files within the same filesystem. In this case, the file usually retains its original ACLs, and the inherited permissions from the destination directory are not reapplied.
In summary, we’ve found that ACLs are a very useful tool for managing servers where multiple users or services access the same files but require different permissions.
However, if all users have the same privileges, then it’s certainly easier to use a standard Linux group.
As for day-to-day ACL management, you’ll only need two commands: the getfacl command, which displays existing permissions, and the setfacl command, which modifies them.
Most importantly, when working with ACLs, you should always check effective permissions, configure inheritance carefully, and preserve ACLs when transferring data.