Filesystem and Permissions¶
What You'll Learn¶
- Where things live in the Linux directory tree, and why it matters in operations
- How ownership and
rwxpermissions decide who can do what - What setuid, setgid, and the sticky bit do, and why they're security-sensitive
- How
umaskand ACLs work, and how to debug "Permission denied" methodically
The Directory Layout¶
Linux follows the Filesystem Hierarchy Standard. Knowing it tells you where to look during an incident.
| Path | Contains | Operational note |
|---|---|---|
/etc |
System and application configuration | Back it up; manage it with Ansible instead of hand edits |
/var/log |
Log files | A common cause of full disks |
/var/lib |
Application state: databases, Docker, containerd | Often needs its own volume |
/opt |
Self-contained third-party software | Where tarball installs like SonarQube go |
/usr/bin, /usr/sbin |
Installed programs | Owned by the package manager — don't edit |
/usr/local |
Software you installed outside the package manager | |
/home, /root |
User home directories | |
/tmp |
Temporary files, often cleared on reboot | World-writable with the sticky bit |
/proc, /sys |
Virtual filesystems exposing kernel and process state | Read them to inspect the live system |
/dev |
Device files for disks, terminals, and more |
Reading ls -l¶
$ ls -l /etc/shadow /usr/bin/passwd deploy.sh
-rw-r----- 1 root root 1284 Sep 10 09:12 /etc/shadow
-rwsr-xr-x 1 root root 64152 May 30 12:01 /usr/bin/passwd
-rwxr-x--- 1 app devops 812 Sep 14 08:40 deploy.sh
-rwxr-x--- 1 app devops 812 Sep 14 08:40 deploy.sh
│└┬┘└┬┘└┬┘ │ │
│ │ │ │ │ └── group owner
│ │ │ │ └─────── user owner
│ │ │ └── other: no access
│ │ └───── group: read and execute
│ └──────── user: read, write, execute
└────────── type: - file, d directory, l symlink
What r, w, and x Mean¶
The same letters mean different things on files and directories. Most permission surprises come from the directory column.
| Permission | On a file | On a directory |
|---|---|---|
r (4) |
Read contents | List names inside (ls) |
w (2) |
Modify contents | Create, delete, and rename entries inside |
x (1) |
Execute as a program | Enter it and access entries by name (cd, open files inside) |
Two consequences worth remembering:
- To read
/srv/app/config.yml, a user needsxon/srv,/srv/app, andron the file. - Anyone with
won a directory can delete files in it, even files they don't own — unless the sticky bit is set.
Changing Ownership and Modes¶
sudo chown app:devops deploy.sh # user and group
sudo chown -R app:app /srv/app # recursively
chmod 750 deploy.sh # rwx r-x ---
chmod u+x,g-w,o= deploy.sh # symbolic form: add, remove, set
chmod -R g+rX /srv/app # capital X: execute only on directories
# and files that are already executable
| Octal | Symbolic | Typical use |
|---|---|---|
600 |
rw------- |
Private keys, .env files with secrets |
640 |
rw-r----- |
Config readable by a service group |
644 |
rw-r--r-- |
Normal files |
700 |
rwx------ |
~/.ssh, private directories |
750 |
rwxr-x--- |
Scripts and directories shared with a group |
755 |
rwxr-xr-x |
Programs and public directories |
Avoid chmod 777. It "fixes" a permission error by letting every process on the machine modify the file.
Special Bits¶
| Bit | Octal | On a file | On a directory |
|---|---|---|---|
| setuid | 4000 |
Runs with the file owner's privileges (passwd writes /etc/shadow as root) |
No effect on Linux |
| setgid | 2000 |
Runs with the file's group privileges | New files inherit the directory's group — ideal for shared team directories |
| sticky | 1000 |
No effect on Linux | Only a file's owner (or root) can delete it — used on /tmp |
# A shared directory where everything stays in the devops group
sudo mkdir /srv/shared
sudo chown root:devops /srv/shared
sudo chmod 2775 /srv/shared # setgid + rwxrwxr-x
# Audit setuid and setgid binaries — unexpected entries are a red flag
sudo find / -xdev -perm /6000 -type f -exec ls -l {} + 2>/dev/null
umask: Default Permissions for New Files¶
New files start from 666 and directories from 777, and umask removes bits.
Set a service's umask in its systemd unit (UMask=0027) rather than relying on a login shell default.
ACLs: Permissions for More Than One Group¶
Standard modes allow one owner and one group. Access control lists add named users and groups.
sudo apt install -y acl
# Let the "monitoring" group read application logs without changing ownership
sudo setfacl -R -m g:monitoring:rX /var/log/app
# Default ACL: files created later inherit it
sudo setfacl -R -d -m g:monitoring:rX /var/log/app
getfacl /var/log/app
A + at the end of the mode in ls -l (drwxr-x---+) tells you an ACL is present.
Immutable Files¶
sudo chattr +i /etc/resolv.conf # nobody, including root, can modify or delete it
lsattr /etc/resolv.conf
sudo chattr -i /etc/resolv.conf
Useful to stop automation from overwriting a critical file — and a classic cause of confusing "Operation not permitted" errors when someone forgets it's set.
Debugging "Permission denied"¶
Work through the checks in order:
# 1. Who is the process actually running as?
ps -o user,group,pid,cmd -C nginx
id app
# 2. Check every directory in the path, not just the file
namei -l /srv/app/config/app.yml
# 3. Check for ACLs and attributes
getfacl /srv/app/config/app.yml
lsattr /srv/app/config/app.yml
# 4. Check mandatory access control
getenforce 2>/dev/null # SELinux (RHEL family)
sudo ausearch -m avc -ts recent 2>/dev/null
sudo aa-status 2>/dev/null # AppArmor (Ubuntu)
sudo journalctl -k | grep -i apparmor | tail
# 5. Check the mount options
findmnt -T /srv/app # look for ro or noexec
namei -l is the fastest win: it prints the owner and mode of every path component, so a missing x on a parent directory stands out immediately. If step 4 shows a denial, SELinux and AppArmor explains how to fix it without disabling either.
Paste your own namei -l and id output below. The walker applies the kernel's rules to every component and names the one that blocks you:
Permission walker (needs JavaScript). Without it: run namei -l on the path, find which class you are in for each component (owner, else group, else other), and check that every directory gives you x and the file gives you the bit you need.
What Most Engineers Miss¶
- Only one class is checked, and the first match wins. If you own a file, only the owner bits apply. A file with mode
----rw-rw-is unreadable to its owner and readable by everyone else. The kernel doesn't fall through to the more generous group or other bits. - Deleting a file is a change to the directory. With
wandxon the directory you can delete a read-only file you don't own;rmonly asks first ("remove write-protected regular file?"). The sticky bit is the only thing that stops it, which is why/tmpisdrwxrwxrwt. - Root still needs an execute bit. Root bypasses read and write checks, but
execof a file with noxbit anywhere fails even for root. A script with--xand norfails for everyone else, because the interpreter has to read it;bash script.shneeds onlyr. - New group membership needs a new login.
usermod -aG docker alicechanges/etc/group, not the groups of processes already running.idin the same shell shows the old list. Log out and in, runnewgrp docker, or restart the service. A running process's groups are ingrep Groups /proc/<pid>/status. - Numeric
chmodkeeps setgid on directories. GNUchmod 755 dirleaves an existing setgid bit set. To clear it you needchmod 00755 dir,chmod g-s dir, orchmod =755 dir. noexecmounts win over modes. On/tmpor/dev/shmmountednoexec,./scriptfails withPermission denied(exit status 126) however you set the bits.findmnt -T <path>shows the mount options.
Real incident: PwnKit (2022), a setuid binary on every distribution
In January 2022 Qualys disclosed that pkexec, a setuid-root program installed by default on nearly every major Linux distribution since 2009, could be made to run code as root by any local user (CVE-2021-4034, advisory). The flaw sat in the binary for over twelve years. Every setuid binary is part of your attack surface, which is why the audit command in Special Bits is worth running: remove the setuid bit from programs nobody needs (chmod u-s), and patch the rest.
Common Mistakes¶
- Running
chmod -R 777to fix an error, and turning a single misconfiguration into a machine-wide security hole. - Checking the file's permissions but not the parent directories' execute bit.
- Forgetting that
sudoin front of a redirect doesn't apply to the redirect:sudo echo x > /etc/filefails; useecho x | sudo tee /etc/file. - Deploying files as root with
600, then wondering why the service user can't read them. - Ignoring SELinux or AppArmor denials and disabling them entirely instead of fixing the label or profile.
Check Yourself¶
Owner versus everyone else
report.txt is owned by alice with mode -r--rw-rw-. Alice tries to edit it. What happens?
- It works, because the group and other bits allow writing
- It fails: Alice is the owner, so only the owner bits (
r--) apply - It works, because owners can always write their own files
The kernel picks one class, and owner comes first. Alice can still fix it herself with chmod u+w report.txt, because the owner may always change the mode.
Deleting someone else's file
/srv/shared is drwxrwxr-x root devops. Bob is in devops. Alice's file /srv/shared/notes.txt is -r--------. Can Bob delete it?
- Yes: deleting needs
wandxon the directory, which Bob has throughdevops - No: Bob can't even read the file
- Only with
sudo
The file's mode is irrelevant to deleting it. Set the sticky bit (chmod +t /srv/shared) if only owners should be able to remove their own files.
Interview Questions¶
- What does the execute bit mean on a directory?
- A user has
rwon a file but still gets "Permission denied" when reading it. What do you check? - What is setgid on a directory used for?
- Why is the sticky bit set on
/tmp? - How would you give a second group read access to a directory without changing its owner?
Next¶
Continue to Processes and Signals.