Linux ACL Permissions Tutorial

By | 2020-02-28

Linux ACL permissions are based on the draft POSIX 1003.1e. Even though ACLs are in use, the standard never made it out of the draft status.

I assume you have a solid grasp on regular file permissions. If not, I suggest reading my guide on the topic before working with ACLs.

Use Cases

Access control lists (ACL)s are useful for granting a user access to a resource without adding them to a group or creating a new group. Consider the example below:

$ ls -l
total 4
-rw-r----- 1 root www-data 26 Feb 19 19:30 LocalSettings.php

Suppose you need to give a user access to the file LocalSettings.php, but don’t want them to have access to everything the group www-data does. This is when ACLs are useful.

I frequently use ACLs to give developers access to certain files on productions servers.

ACLs can be used to ensure access to shared files on file servers. Default ACLs can be used to automatically add ACL entries to newly created files.

Identifying Files With ACLs

The -l option of ls will show you if you have an ACL or not.

$ ls -l
total 12
drwxr-xr-x+ 3 tyler tyler 4096 Feb 19 17:13 default
drwxr-xr-x  2 tyler tyler 4096 Feb 19 19:30 ldap
drwxr--r-x+ 3 tyler tyler 4096 Feb 19 17:16 mask

Permission strings with a + at the end indicate the file in question has an ACL.

Commands Overview

There are two commands you will use when working with ACLs: getfacl and setfacl. They are used to view and modify ACLs, respectively. Below are examples of each. I will explain them in more detail later.


# setfacl -m g:adm:rw- backup.ldif 
# getfacl backup.ldif 
# file: backup.ldif
# owner: openldap
# group: openldap
user::rw-
group::r--
group:adm:rw-
mask::r--
other
# ls -l
total 4
-rw-rw----+ 1 openldap openldap 26 Feb 19 19:30 backup.ldif

Notice in the output of ls -l the files permission string now has a + at the end. This indicates that the file has ACL entries.

Understanding ACLs

One way of describing ACLs is they are additional sets of file permissions on top of the normal owner, group, and other permissions. Below I explain each line from the output of getfacl on a file with an ACL.

$ ls -ld example/
drwxr-x---+ 2 root root 4096 Feb 20 18:09 example/
$ getfacl example
# file: example/
# owner: root
# group: root
user::rwx
user:tyler:rwx			#effective:r-x
group::r-x
group:adm:rwx			#effective:r-x
mask::r-x
other::---
default:user::rwx
default:user:tyler:rwx		#effective:r-x
default:group::r-x
default:group:adm:r-x
default:mask::r-x
default:other::r-x

The first three lines, shown below, are the file’s name, owner, and group, respectively.

# file: example/
# owner: root
# group: root

The next two lines show what individual users can do. The line that starts with user:: applies to the file’s owner. In this case, root. The line with tyler between the :: shows the permissions that apply to the user tyler, which are rwx. Notice how off to the right, you have #effective:r-x. This means that a mask is only allowing tyler r-x. The permissions listed after #effective: are what will be enforced.

user::rwx
user:tyler:rwx			#effective:r-x

The next two lines are similar to the previous two, but apply to groups instead of users. The line beginning with group:: are the permissions that apply to the group that owns the file.

group::r-x
group:adm:rwx			#effective:r-x

ACL masks are a way to enforce a maximum level of access, regardless of what a particular user or group is granted by an ACL. This is why you see the lines with #effective: that are more restrictive than the ACL entry earlier in the line. In the example, the mask is set to r-x. This means that any user or group that have ACL entries will only be able to read and execute the file, even if the ACL entry gives them write access.

mask::r-x

Next are the other permissions. These are the permissions from the other position as you would see in the output of ls -l. They apply to any user or group that doesn’t own the file and don’t have an ACL entry.

other::---

The last six lines are default ACLs. Default ACLs only apply to directories. These are the ACLs entries that are automatically added to any new file or directory created inside of it.

default:user::rwx
default:user:tyler:rwx		#effective:r-x
default:group::r-x
default:group:adm:r-x
default:mask::r-x
default:other::r-x

Precedence and Masks

Before I show you how to modify ACLs, there are some things that you should know. When access to a file with an ACL is determined, the first set of permissions to match the following list is applied.

  1. File’s owner
  2. User’s ACL entry less any permissions not allowed by the mask
  3. Group owner or group ACL entries less mask restrictions
  4. Other

Consider the following ACL:

# groups tyler
tyler : tyler tty cdrom floppy audio dip video plugdev netdev scanner bluetooth vboxusers openldap
# getfacl a
# file: a
# owner: root
# group: root
user::rwx
user:root:r-x
user:tyler:r-x
group::r-x
group:tyler:rwx
mask::rwx
other::---

The user tyler can only read or execute the file even though it is in the tyler group, which has write access. As you can see, ACLs can be used to restrict access as well.

$ echo abc > a
bash: a: Permission denied

Consider the user acl, which is in the tyler group:

$ id  
uid=1002(acl) gid=1000(tyler) groups=1000(tyler)
$ echo abc > a

As you can see, the user acl has no access to the file other than through the tyler group.

Let’s move on to another example:

# getfacl b
# file: b
# owner: root
# group: openldap
user::rw-
user:tyler:r--
group::rw-
mask::rw-
other::---
$ id -un
tyler
$ groups tyler
tyler : tyler tty cdrom floppy audio dip video plugdev netdev scanner bluetooth vboxusers openldap
$ echo hi > b
bash: b: Permission denied

The user tyler is in the group openldap, which owns the file b. Even though the group openldap has write access to the file, it cannot write to it due to the ACL entry.

As mentioned earlier, ACL entries have precedence over the other permission set. Consider the ACL entries on file c:

# getfacl c
# file: c
# owner: root
# group: root
user::rw-
user:acl:r--
group::rw-
group:tyler:r--
mask::rw-
other::rw-

The user acl has a read only user ACL entry. The ACL entry takes precedence, even though the other set allows write access to any user.

code>$ id
uid=1002(acl) gid=144(haproxy) groups=144(haproxy)
echo c > c
bash: c: Permission denied

The same applies to the acl2 user due to it being in the tyler group.

$ id  
uid=1003(acl2) gid=1003(acl2) groups=1003(acl2),1000(tyler)
$ echo c > c
bash: c: Permission denied

Lastly, I will demonstrate masks:

$ getfacl d
# file: d
# owner: root
# group: haproxy
user::rwx
user:acl2:rwx			#effective:r--
group::rwx			#effective:r--
mask::r--
other::rwx

$ ls -l d
-rwxr--rwx+ 1 root root 0 Feb 23 06:37 d

Whenever an ACL mask is changed, the group permissions of a file’s standard permission set change to match the mask permissions.

$ id
uid=1002(acl) gid=144(haproxy) groups=144(haproxy)
$ echo d > d
bash: d: Permission denied

Notice how the user acl cannot write to d even though the other permissions allow it and the standard group listed in the ACL allows it. Again, when a mask is in place, an ACL entry cannot grant a permission that isn’t allowed by the mask. Again, group permissions have precedence over other permissions.

$ id -un
tyler
$ echo d > d

The tyler user is able to write to the file because neither it nor any of its groups have ACL entries, and the other permission set allows it.

$ id
uid=1003(acl2) gid=1003(acl2) groups=1003(acl2),1000(tyler)
$ cat d
d
$ echo d > d
bash: d: Permission denied

The user acl2 has an ACL entry that grants it full access to the file d. The mask only allows ACL entries read access to file. This is why acl2 can read he file but not write to it.

Creating, Setting, and Removing ACLs

ACLs are modified with the setfacl program. I’m not going to cover it exhaustively. but, I will cover the most common tasks and explain any nuances.

Adding and Modifying ACL Entires

To add or change an ACL entry, use setfacl -m, followed by the ACL specification, and then a list of files you wish to apply the changes to.

# setfacl -m u:tyler:rwx myfile
# getfacl myfile
# file: myfile
# owner: root
# group: root
user::rw-
user:tyler:rwx
group::r--
mask::rwx
other::r--

The ACL specification consists of 2-4 fields separated by :. Specifications for adding and changing user and group entries start with a single letter denoting whether it is a user or group. The characters u and g correspond to user and group, respectively. The next field is the name of the user or group. The last field is used to specify the permissions that you wish for the entry to have. If an entry for the applicable group or user exists, any permissions granted by the existing entry are replaced by the permissions in the final part of the specification.

# setfacl -m u:tyler:r-- myfile
# getfacl myfile
# file: myfile
# owner: root
# group: root
user::rw-
user:tyler:r--
group::r--
mask::r--
other::r--

Working With Masks

As with regular entries, use -m to modify the mask. Mask specifications are simple. The first field is m and the second is the permission string:

# setfacl -m m:rw myfile 
# getfacl myfile 
# file: myfile
# owner: root
# group: root
user::rw-
user:tyler:r--
group::r--
mask::rw-
other::r--

The mask entry is modified automatically when you modify an ACL entry or group permissions. setfacl sets the mask permissions to the union of all permissions affected by the mask.

# setfacl -m g:openldap:--x myfile 
# getfacl myfile 
# file: myfile
# owner: root
# group: root
user::rw-
user:tyler:r--
group::r--
group:openldap:--x
mask::r-x
other::---

Since the user tyler and owning group have read access, the read permission is added to the mask. The openldap group has execute, so it is added to the mask. If I remove the openldap group, the execute permission is removed from the mask:

# setfacl -x g:openldap myfile 
# getfacl myfile 
# file: myfile
# owner: root
# group: root
user::rw-
user:tyler:r--
group::r--
mask::r--
other::---

Use the -n option to prevent setfacl from modifying the mask:

# setfacl -nm g:openldap:rwx myfile 
# getfacl myfile 
# file: myfile
# owner: root
# group: root
user::rw-
user:tyler:r--
group::r--
group:openldap:rwx		#effective:r--
mask::r--
other::---

Modifying Default ACLs

When working with default ACLs, all of the above applies with a single exception. Just add d: to the beginning of the ACL specification. Only directories may have default ACL entries.

# setfacl -m d:u:tyler:rwx acl_example
# getfacl acl_example
# file: acl_example
# owner: root
# group: root
user::rwx
group::r-x
other::r-x
default:user::rwx
default:user:tyler:rwx
default:group::r-x
default:mask::rwx
default:other::r-x

Removing Entries

Use setfacl -x to remove an ACL entry. When removing entries, omit the permission string:

# setfacl -x g:openldap myfile 
# getfacl myfile 
# file: myfile
# owner: root
# group: root
user::rw-
user:tyler:r--
group::r--
mask::r--
other::---

As with regular entries, setfacl -x can be used to remove the mask or default entries.

$ setfacl -x m dir
$ setfacl -x d:m dir
$ setfacl -x d:g:openldap dir

Use the -b option to remove all ACL entries, masks, and defaults:

$ getfacl default
# file: default
# owner: tyler
# group: tyler
user::rwx
group::r-x
other::r-x
default:user::rwx
default:user:openldap:r--
default:group::r-x
default:mask::r-x
default:other::r-x

$ setfacl -b default
$ getfacl default
# file: default
# owner: tyler
# group: tyler
user::rwx
group::r-x
other::r-x

Common setfacl Options

Option Description
-b Removes all ACL entries from a file.
-n Prevents the mask from being modified.
-R Recursively applies changes to a directory and everything in it.
-m Modify or add an ACL entry.
-x Removes an ACL entry.

How Default ACLs Work

Setting default ACL entries on directories causes new files and directories created within it to have the entries listed as default. Newly created directories inherit default entries as well.

I have used default ACLs on Samba and NFS shares where users vary in the level access they should have.

# setfacl -m d:u:tyler:rwx acl_example
# getfacl acl_example
# file: acl_example
# owner: root
# group: root
user::rwx
group::r-x
other::r-x
default:user::rwx
default:user:tyler:rwx
default:group::r-x
default:mask::rwx
default:other::r-x

# cd acl_example
# touch file
# getfacl file
# file: file
# owner: root
# group: root
user::rw-
user:tyler:rwx			#effective:rw-
group::r-x			#effective:r--
mask::rw-
other::r--

# mkdir new_dir
# getfacl new_dir
# file: new_dir
# owner: root
# group: root
user::rwx
user:tyler:rwx
group::r-x
mask::rwx
other::r-x
default:user::rwx
default:user:tyler:rwx
default:group::r-x
default:mask::rwx
default:other::r-x

As you can see the file file inherited the ACL entry for the user tyler from acl_example. The directory new_dir inherited both the default and regular entry for the tyler user.

Directories can have default masks as well:

$ setfacl -m d:m:r-x default
$ getfacl default
# file: default
# owner: tyler
# group: tyler
user::rwx
group::r-x
other::r-x
default:user::rwx
default:group::r-x
default:mask::r-x
default:other::r-x

Examples

Command Description
setfacl -m u:tyler:rwx file Adds the user tyler with read, write, and execute
setfacl -b file Removes all ACL entries, defaults, and masks from file
setfacl -x g:openldap file Removes the openldap group ACL entry
setfacl -m d:u:tyler:r-x directory Adds a default ACL entry allowing the user tyler to read and execute files created within the directory
setfacl -R m:r-x directory Sets the mask of the directory and everything in it to read and execute
getfacl template | setfacl --set-file=- hello.c Copies the ACL entries from the file template to the file hello.c
setfacl -m d:m:r-- directory Sets a default mask of read only

See Also

References

Discuss