Permission Denied¶
An application logs Permission denied on a file it should be able to use. The interviewer checks whether the candidate goes past file modes to ACLs, attributes, mount options and mandatory access control, and reads which layer actually refused.
Symptom¶
"The app logs Permission denied on a file it should be able to read (or on a binary it should run), but the owner and mode look right."
Clarifying Questions¶
- Read, write or execute? Each maps to different causes (
wblocked by a read-only mount or immutability,xbynoexec). - Which user, and is it a confined service? An unconfined shell user is limited by DAC; a service like nginx is also limited by SELinux.
- File or a directory on the path? A missing
xon a parent directory blocks access to everything inside. - On RHEL or Ubuntu? Points to SELinux or AppArmor for the mandatory-access layer.
Diagnostic Path¶
On web (Rocky Linux 10.2), the user deploy reads /srv/app/config.
1. File Mode¶
ls -l /srv/app/config
sudo -u deploy cat /srv/app/config; echo "exit=$?"
Output:
-rw-r-----. 1 root root 12 Sep 17 17:08 /srv/app/config
cat: /srv/app/config: Permission denied
exit=1
Mode 640, owner root:root: deploy is neither owner nor in group root, so other (no bits) applies. This is ordinary discretionary access control.
2. An ACL Grants One User¶
sudo setfacl -m u:deploy:r /srv/app/config
getfacl -pt /srv/app/config | grep -vE '^$'
ls -l /srv/app/config
sudo -u deploy cat /srv/app/config; echo "exit=$?"
Output:
# file: /srv/app/config
USER root rw-
user deploy r--
GROUP root r--
mask r--
other ---
-rw-r-----+ 1 root root 12 Sep 17 17:09 /srv/app/config
secret data
after-acl exit=0
The + in ls -l marks an ACL. getfacl shows the extra user:deploy:r--, which grants access the mode bits alone would deny. An ACL can also hide access: check getfacl when the mode looks permissive but access fails.
3. A Directory on the Path¶
sudo chmod 750 /srv/app; sudo chown root:root /srv/app
sudo -u deploy cat /srv/app/config; echo "exit=$?"
Output:
cat: /srv/app/config: Permission denied
no-traverse exit=1
The file is readable, but /srv/app is 750 and owned by root:root, so deploy cannot traverse it. On a directory, x means "enter"; without it, nothing inside is reachable whatever the file's own mode.
4. An Immutable File¶
sudo chmod 777 /srv/app/config
sudo chattr +i /srv/app/config
lsattr /srv/app/config
echo more | sudo tee -a /srv/app/config; echo "exit=$?"
sudo chattr -i /srv/app/config
Output:
----i---------e------- /srv/app/config
tee: /srv/app/config: Operation not permitted
root-append exit=1
Mode 777 and still Operation not permitted, even for root: the immutable attribute (chattr +i) blocks every write until it is cleared. Operation not permitted (EPERM), not Permission denied (EACCES), is the tell.
5. A noexec Mount¶
sudo mount -t tmpfs -o noexec,nosuid tmpfs /mnt/apps
sudo install -m 755 /dev/stdin /mnt/apps/run.sh <<< $'#!/bin/sh\necho ran'
/mnt/apps/run.sh; echo "direct exit=$?"
sh /mnt/apps/run.sh; echo "via-sh exit=$?"
findmnt -no OPTIONS /mnt/apps
Output:
bash: line 1: /mnt/apps/run.sh: Permission denied
direct exit=126
ran
via-sh exit=0
rw,nosuid,noexec,relatime,seclabel
Executing the script directly fails with exit 126, although the mode is 755, because the filesystem is mounted noexec. Running it through an interpreter (sh script) works, which distinguishes noexec from a mode problem. A read-only mount gives touch: ... Read-only file system for writes.
6. Mandatory Access Control for Services¶
For a confined service (not an unconfined shell user), SELinux or AppArmor can deny access even when DAC allows it: nginx reading a file with the wrong label logs Permission denied while ls -Z and an AVC record show the cause. SELinux captures this case.
Root Causes¶
| Branch | Evidence | Fix |
|---|---|---|
| File mode | ls -l; user not owner/group | chmod/chown, or add an ACL |
| ACL | + in ls -l; getfacl entry | setfacl -m/-x |
| Directory traversal | File readable, parent lacks x | chmod o+x/g+x on the directory, or an ACL |
| Immutable attribute | Operation not permitted for root; lsattr i | chattr -i |
noexec mount | Exit 126 on run, sh script works | Move the binary, or remount without noexec |
| Read-only mount | Read-only file system on write | Remount rw, fix the underlying cause |
| SELinux/AppArmor | Service denied, DAC fine; AVC or apparmor="DENIED" | Fix the label/profile, not setenforce 0 |
Fix¶
Depends on the branch. For the captured mode case, grant access narrowly with an ACL rather than opening the mode:
sudo setfacl -m u:deploy:r /srv/app/config
sudo -u deploy cat /srv/app/config # confirm
Prevention¶
- Prefer group ownership or ACLs over widening modes; never
chmod 777a shared file. - Keep
xon the directories an application must traverse, and check the whole path withnamei -l. - Document
noexec,nosuidand read-only mounts, so a deploy to/tmpor/varfails visibly, not silently. - On RHEL, fix labels with
restorecon/semanage, and read AVCs before touching enforcement.
Related¶
- Basic Permissions and ACL: modes and ACLs
- File Attributes:
chattr +i - Mounting and fstab:
noexec,nosuid, read-only - SELinux: denied access for confined services
Captured on Rocky Linux 10.2 (SELinux enforcing) on an iximiuz Labs FlexBox microVM, kernel 6.1.167, 2026-09.