Lab: Permissions and ACLs in a Shared Directory
The permissions, ownership, and special permissions articles each covered one mechanism in isolation. A real production incident rarely involves just one — it involves a shared directory where three different teams need different levels of access, new files keep landing with the wrong group, and someone eventually calls you because "the developers can't read the ops logs." This lab reproduces exactly that setup on a disposable lab machine and fixes it using DAC (chmod/chgrp), setgid for automatic group inheritance, the sticky bit, and POSIX ACLs for the one exception that ordinary Unix permissions cannot express on their own.
Goal and End Result
By the end of this lab, /srv/shared-project will:
- be owned by a shared group
project-team, with every new file automatically inheriting that group (setgid); - allow every
project-teammember to create and edit files, but not delete files owned by other members (sticky bit); - grant one additional user,
auditor, read-only access without adding them toproject-teamat all (ACL); - be verified from the point of view of each user separately, not just as root.
Topology and Starting State
A single lab VM or container is enough; no networking is required. You will create three ordinary users and one shared group.
sudo groupadd project-team
sudo useradd -m -G project-team dev1
sudo useradd -m -G project-team dev2
sudo useradd -m auditor
sudo passwd dev1
sudo passwd dev2
sudo passwd auditor
Requirements
aclpackage installed, forsetfacl/getfacl:
- ability to switch users (
su - <user>) to verify behavior from each account's perspective, not only as root.
Security and Snapshot Note
Warning
This lab creates real user accounts and modifies permissions under /srv. If you are on a shared or production-adjacent machine, run this in a disposable VM or container instead. Before changing anything, record the current state so you have something to compare against and roll back to:
Step 1: Create the Shared Directory
sudo mkdir -p /srv/shared-project
sudo chgrp project-team /srv/shared-project
sudo chmod 2775 /srv/shared-project
2775 breaks down the same way Special Permissions explained: the leading 2 sets the setgid bit, 775 gives the owner and group full read/write/execute, others read/execute only.
Verify:
The s in place of the group's execute bit confirms setgid is active — this is the detail to check immediately if new files ever start appearing with the wrong group later in this lab.
Step 2: Confirm setgid Inheritance
The file's group is project-team, not dev1's primary group — exactly the setgid behavior being tested. Without setgid on the parent directory, this file would have inherited dev1's own primary group instead, and dev2 would have been unable to write to it despite both being in project-team.
Step 3: Add the Sticky Bit to Prevent Cross-Deletion
Right now, any project-team member can delete any other member's file, because both have write access to the directory itself — write access to a directory, not to the file, is what rm actually checks.
Verify the sticky bit actually blocks cross-deletion:
su - dev2
touch /srv/shared-project/dev2-file.txt
exit
su - dev1
rm /srv/shared-project/dev2-file.txt
This confirms the exact mechanism from Special Permissions: the sticky bit restricts deletion to a file's owner (or root), even when every group member has write access to the directory that contains it.
dev2 can still remove their own file — the sticky bit only blocks deletion by other users, not by the owner.
Step 4: Grant Read-Only Access to One Extra User with ACLs
auditor needs to read everything in /srv/shared-project without becoming a full project-team member (which would also grant write access) and without opening the directory to every other user on the system — a requirement ordinary Unix permissions cannot express, since there is no fourth permission slot for "this one specific extra user."
sudo setfacl -m u:auditor:rx /srv/shared-project
sudo setfacl -d -m u:auditor:rx /srv/shared-project
The first command grants auditor read and execute (traverse) on the directory itself; the second sets a default ACL, which is inherited by every file and subdirectory created afterward — without it, auditor's access would apply only to the directory entry, not to any of the files inside it.
Before running getfacl, note that an ordinary ls -ld already tells you an ACL is present:
The trailing + on the permission string (drwxrwsr-t+) is the signal that an ACL beyond the standard owner/group/other bits exists on this path — a detail interviewers ask about directly ("how do you tell a file has an ACL without running getfacl?"), and the reason the plain mode bits shown by ls -l can be misleading on their own: a + means the effective permissions are not fully described by the rwx triplets alone.
Verify:
# file: srv/shared-project
# owner: root
# group: project-team
user::rwx
user:auditor:r-x
group::rwx
mask::rwx
other::r-x
default:user::rwx
default:user:auditor:r-x
default:group::rwx
default:mask::rwx
default:other::r-x
su - auditor
ls /srv/shared-project
cat /srv/shared-project/dev1-notes.txt
touch /srv/shared-project/should-fail.txt
auditor can list and read, exactly as granted, and cannot create files — the ACL entry stopped exactly at rx, with no w.
Troubleshooting Tips
- A new file has the wrong group despite setgid being set. Check that setgid is set on the directory the file was created in, not a parent directory further up — setgid is not automatically recursive to subdirectories created before the bit was set; each existing subdirectory needs
chmod g+sapplied individually, orchmod -R g+sif this is genuinely intended for the whole tree (see the danger note below). getfaclshows no ACL entries at all. Theaclpackage may not be installed, or the filesystem may have been mounted without ACL support — check withmount | grep <mountpoint>for theacloption, and add it in/etc/fstabif missing (requires a remount).- A permission that should apply doesn't, and
mask::ingetfacloutput is more restrictive than expected. The ACL mask caps the effective permissions of every named user and group entry — amask::r--silently reduces an ACL entry that grantsrwxdown tor--;setfacl -m mask::rwx <path>fixes this if it was unintentional.
Rollback and Cleanup
Danger
chmod -R and userdel -r are destructive and irreversible on real data. Confirm you are removing lab-only accounts and directories, not anything with real content, before running the following.
sudo rm -rf /srv/shared-project
sudo userdel -r dev1
sudo userdel -r dev2
sudo userdel -r auditor
sudo groupdel project-team
userdel -r also removes each user's home directory — appropriate here since these were lab-only accounts created in Step 0, but never run this against an account that might hold real data without confirming first.
Final Assessment Criterion
This lab is complete when all of the following hold:
- a file created by
dev1inside/srv/shared-projectis owned by groupproject-team, notdev1's own primary group; dev1cannot delete a file created bydev2in the same directory, and vice versa;auditorcan read every file in the directory and list its contents, but cannot create or modify anything inside it;getfacloutput for the directory shows both a regular and a default ACL entry forauditor.
Exercises
- Add a third developer,
dev3, toproject-teamafter completing this lab, and confirm — without re-running anysetfaclorchmodcommand — thatdev3immediately has the same group-based access asdev1anddev2. Explain why no ACL change was needed for this, but would have been needed forauditor. - Remove the sticky bit (
chmod -t /srv/shared-project) and repeat Step 3's cross-deletion test. Confirm the deletion now succeeds, then restore the sticky bit. - Using
find, write a single command that audits/srvfor any directory with the setgid bit set, in the style of Special Permissions' audit section. - Grant
auditorwrite access to exactly one specific file in the directory (not the whole directory) using a file-level ACL entry, and verify withgetfaclon that file alone that the permission differs from the directory's default.