Server

Linux Permissions and ACLs, Properly

By Prabath Thalangama· October 24, 2026· 3 min read
#linux#security#troubleshooting

Introduction

The classic owner/group/other model handles most cases. When it doesn't — "this one service account also needs read access" — the answer is ACLs, not a tangle of supplementary groups. This covers both.

The mode bits

ls -l file
# -rwxr-x---  1 alice devs  ...
#  |L-+-L-+-L-+
#  | |   |   L other:  ---
#  | |   L group devs: r-x
#  | L owner alice:  rwx
#  L type: - file, d dir, l symlink

Octal: r=4 w=2 x=1, per class. 750 = rwxr-x---. chmod 640 file, chmod u+x,g-w file.

On directories, x means "can traverse into / access entries by name", r means "can ls the contents", w means "can create/delete entries" (note: deleting a file needs w on the directory, not the file).

Special bits

Bit Octal On a file On a directory
setuid 4000 Runs as the file's owner (passwd, sudo) (ignored)
setgid 2000 Runs as the file's group New files inherit the dir's group
sticky 1000 (ignored) Only the owner can delete their own files (/tmp)
chmod 2775 /srv/shared        # setgid dir: everything created here is group 'devs'
find / -perm -4000 -type f 2>/dev/null    # audit: all setuid binaries

setgid on a shared directory is the standard fix for "files created by different people end up with the wrong group".

umask

New files are created with 0666 & ~umask, directories 0777 & ~umask. A umask of 022 (the common default) gives files 644, dirs 755. For a locked-down service, set UMask=0027 in the systemd unit's [Service] section rather than wrapping the command in a shell that runs umask.

POSIX ACLs

When you need "group devs has rwx, and also user backup has r-x, and everyone else nothing" — that's more than three classes. ACLs add named-user and named-group entries.

# Requires the filesystem mounted with 'acl' (default on ext4/xfs for years)
getfacl /srv/app                       # show all ACL entries

setfacl -m u:backup:rx /srv/app        # grant user backup read+execute
setfacl -m g:auditors:r /srv/app/logs  # grant group auditors read
setfacl -x u:backup /srv/app           # remove that entry
setfacl -b /srv/app                    # strip all ACLs

Default ACLs (inheritance)

A default ACL on a directory is the template new entries inherit:

# Every new file/dir under /srv/app gets user backup:rx automatically
setfacl -d -m u:backup:rx /srv/app
setfacl -R -m u:backup:rx /srv/app       # also apply to existing contents

The mask

getfacl shows a mask:: line — it's the ceiling on all named entries and the owning group. If you setfacl -m u:backup:rwx but the mask is r--, backup effectively gets r--. setfacl recalculates the mask automatically unless you pass -n. A + at the end of ls -l mode means ACLs are present.

Verification and troubleshooting

# What can user 'backup' actually do with this path?
sudo -u backup test -r /srv/app/config && echo readable
namei -l /srv/app/config/db.yaml        # permission of every path component
  • "Permission denied" despite correct file mode — a parent directory lacks x for that user. namei -l walks the whole path and shows where it breaks.
  • ACL set but no effect — the mask is clamping it. getfacl and look at the #effective: comments; setfacl -m m::rwx to widen the mask.
  • New files don't inherit the ACL — you set an access ACL, not a default one. Use setfacl -d -m ... on the directory.
  • setgid dir but files still wrong group — the process is calling chown/chmod after creating, or the mount lost the bit. Re-chmod g+s.
  • Operation not supported — filesystem mounted without acl (rare now), or a filesystem that doesn't support them (some FUSE, older NFS without nfs4_acl).
  • SELinux blocks it even though the permissions allow it — ls -Z, ausearch -m avc -ts recent. Discretionary permissions and SELinux are both checked; both must pass.
PrabathStuck on something this site can't fix?Reach out to Prabath directly on LinkedIn.