Introduction
File permissions are the foundation of security on any Linux system. Every file and directory on disk carries metadata that determines who can read, write, or execute it. A misconfigured permission on a single file can expose sensitive data, allow unauthorized code execution, or break an entire application.
This guide walks you through the complete Linux permission model — from the basic rwx bits through special permissions like setuid and the sticky bit, all the way to POSIX Access Control Lists (ACLs) for fine-grained, multi-user access control. Whether you are hardening a production server, configuring a shared development environment, or troubleshooting a “Permission denied” error, this article gives you the knowledge and commands you need.
Prerequisites
Before you begin, make sure you have:
- A Linux system (Ubuntu, Debian, CentOS, Fedora, Arch, or any distribution).
- Terminal access with a regular user account.
sudoprivileges for commands that require root access.- The
aclpackage installed (for ACL commands). On most distributions it is included by default. If not:
# Debian/Ubuntu
sudo apt install acl
# CentOS/RHEL/Fedora
sudo dnf install acl
- A filesystem that supports ACLs. ext4, XFS, and Btrfs all support ACLs natively.
Understanding Linux Permissions
The ls -l Output Explained
The ls -l command is your primary tool for inspecting permissions. Each line of output contains a wealth of information:
ls -l /etc/passwd
-rw-r--r-- 1 root root 2847 Feb 10 08:30 /etc/passwd
Breaking this down field by field:
| Field | Value | Meaning |
|---|---|---|
| File type | - | Regular file (d = directory, l = symlink, b = block device, c = char device) |
| Owner permissions | rw- | Owner can read and write, but not execute |
| Group permissions | r-- | Group members can read only |
| Others permissions | r-- | All other users can read only |
| Hard link count | 1 | Number of hard links to this inode |
| Owner | root | The user who owns the file |
| Group | root | The group assigned to the file |
| Size | 2847 | File size in bytes |
| Timestamp | Feb 10 08:30 | Last modification time |
| Name | /etc/passwd | File path |
The Three Permission Categories
Linux divides access into three categories:
- Owner (u) — The user who owns the file. By default, the user who creates a file becomes its owner.
- Group (g) — The group assigned to the file. All members of this group share the group permissions.
- Others (o) — Everyone else on the system who is not the owner and not a member of the file’s group.
The Three Permission Bits
Each category can have three types of access:
| Permission | Symbol | Octal | Effect on Files | Effect on Directories |
|---|---|---|---|---|
| Read | r | 4 | View file contents | List directory contents |
| Write | w | 2 | Modify file contents | Create, delete, rename files inside |
| Execute | x | 1 | Run as a program | Enter (cd into) the directory |
A critical distinction: write permission on a directory allows creating and deleting files inside it, regardless of the permissions on those individual files. This is why the sticky bit exists (covered later).
Directories vs. Files
Permission behavior differs between files and directories. Understanding this distinction prevents common mistakes:
# A directory needs execute permission for users to cd into it
ls -ld /var/log
drwxr-xr-x 15 root syslog 4096 Feb 12 00:00 /var/log
# A file needs execute permission only if it is a script or binary
ls -l /usr/bin/ls
-rwxr-xr-x 1 root root 142144 Sep 5 2025 /usr/bin/ls
A directory with read but no execute permission produces an unusual result — users can list filenames inside it, but cannot access file metadata (size, permissions) or open any files within it.
Permission Notation
Symbolic Notation
Symbolic notation uses letters and operators to describe permissions. It is the format displayed by ls -l and accepted by chmod in symbolic mode.
The format is: [ugoa][+-=][rwxXstugo]
- Who:
u(owner),g(group),o(others),a(all) - Operator:
+(add),-(remove),=(set exactly) - Permission:
r(read),w(write),x(execute),X(execute only if directory or already executable),s(setuid/setgid),t(sticky)
Examples:
# Add execute permission for the owner
chmod u+x script.sh
# Remove write permission from group and others
chmod go-w config.yml
# Set exact permissions: owner rwx, group rx, others rx
chmod u=rwx,g=rx,o=rx app.bin
# Add read permission for all categories at once
chmod a+r document.txt
Octal Notation
Octal notation represents each permission set as a single digit (0–7). Each digit is the sum of its permission values:
| Octal | Binary | Permissions |
|---|---|---|
| 0 | 000 | --- (none) |
| 1 | 001 | --x (execute only) |
| 2 | 010 | -w- (write only) |
| 3 | 011 | -wx (write + execute) |
| 4 | 100 | r-- (read only) |
| 5 | 101 | r-x (read + execute) |
| 6 | 110 | rw- (read + write) |
| 7 | 111 | rwx (all) |
The three-digit octal number maps to owner, group, and others in order:
# 755 = owner rwx (7), group r-x (5), others r-x (5)
chmod 755 /var/www/html
# 644 = owner rw- (6), group r-- (4), others r-- (4)
chmod 644 /var/www/html/index.html
# 600 = owner rw- (6), group --- (0), others --- (0)
chmod 600 ~/.ssh/id_rsa
Quick Reference Table
| Use Case | Symbolic | Octal | Permission String |
|---|---|---|---|
| Private SSH key | u=rw,go= | 600 | -rw------- |
| Standard file | u=rw,go=r | 644 | -rw-r--r-- |
| Executable script | u=rwx,go=rx | 755 | -rwxr-xr-x |
| Shared directory | u=rwx,g=rwx,o=rx | 775 | drwxrwxr-x |
| Fully private | u=rwx,go= | 700 | -rwx------ |
| Config with secrets | u=rw,g=r,o= | 640 | -rw-r----- |
Changing Permissions with chmod
The chmod (change mode) command modifies the permission bits on files and directories.
Basic Usage
# Symbolic mode — add execute for owner
chmod u+x deploy.sh
# Octal mode — set to 755
chmod 755 deploy.sh
# Check the result
ls -l deploy.sh
-rwxr-xr-x 1 admin admin 2048 Feb 12 10:00 deploy.sh
Recursive Permission Changes
Use the -R flag to apply permissions recursively to all files and directories inside a path:
# Set directories to 755 and files to 644 recursively
# First, set everything to 755
chmod -R 755 /var/www/html
# Then, set only regular files to 644
find /var/www/html -type f -exec chmod 644 {} \;
The two-step approach is important. Applying chmod 644 recursively would remove execute permission from directories, making them inaccessible. Always handle files and directories separately:
# Set all directories to 755
find /var/www/html -type d -exec chmod 755 {} \;
# Set all files to 644
find /var/www/html -type f -exec chmod 644 {} \;
The Capital X Permission
The X (capital) permission adds execute only if the target is a directory or already has execute permission. This is a safe way to apply permissions recursively without manually separating files and directories:
# Remove execute from files but keep it on directories
chmod -R u=rwX,go=rX /var/www/html
Preserving Root Directory Permissions
When applying recursive changes, be careful not to alter the parent directory itself if it has intentionally different permissions:
# Apply to contents only, not the directory itself
find /opt/app -mindepth 1 -type d -exec chmod 750 {} \;
find /opt/app -mindepth 1 -type f -exec chmod 640 {} \;
Changing Ownership with chown and chgrp
chown — Change Owner and Group
The chown command changes the owner and optionally the group of a file or directory. Only root can change file ownership.
# Change the owner to 'webadmin'
sudo chown webadmin index.html
# Change owner and group simultaneously
sudo chown webadmin:www-data index.html
# Change only the group (note the leading colon)
sudo chown :www-data index.html
# Recursive ownership change
sudo chown -R webadmin:www-data /var/www/html
chgrp — Change Group Only
The chgrp command changes only the group. A regular user can use chgrp if they are a member of the target group:
# Change the group to 'developers'
chgrp developers project-plan.md
# Recursive group change
sudo chgrp -R developers /opt/project
Practical Example: Transfer Ownership
When a team member leaves and you need to reassign their files:
# Find all files owned by the departing user
find /opt/projects -user olduser -type f
# Reassign ownership
sudo find /opt/projects -user olduser -exec chown newuser:devteam {} \;
Understanding umask
The umask (user file-creation mask) determines the default permissions for newly created files and directories. It specifies which permission bits to remove from the system defaults.
The system defaults are:
- Files:
666(rw-rw-rw-) - Directories:
777(rwxrwxrwx)
The umask is subtracted from these defaults:
# Check the current umask
umask
0022
# With umask 022:
# New files: 666 - 022 = 644 (rw-r--r--)
# New directories: 777 - 022 = 755 (rwxr-xr-x)
Setting umask
# Set umask for the current session
umask 027
# With umask 027:
# New files: 666 - 027 = 640 (rw-r-----)
# New directories: 777 - 027 = 750 (rwxr-x---)
Making umask Persistent
To set the umask permanently, add it to your shell configuration:
# For bash — add to ~/.bashrc or ~/.profile
echo "umask 027" >> ~/.bashrc
# For system-wide default — edit /etc/login.defs
sudo grep -n UMASK /etc/login.defs
# UMASK 022
On systems using pam_umask, the system-wide umask is configured in /etc/login.defs. Per-user overrides go in ~/.bashrc or ~/.profile.
Common umask Values
| umask | File Default | Directory Default | Use Case |
|---|---|---|---|
022 | 644 | 755 | Standard — readable by everyone |
027 | 640 | 750 | Moderate — group can read, others blocked |
077 | 600 | 700 | Restrictive — owner only |
002 | 664 | 775 | Collaborative — group can write |
Special Permissions
Beyond the basic rwx bits, Linux supports three special permission bits: setuid, setgid, and the sticky bit. These are represented as a fourth octal digit prepended to the standard three.
setuid (Set User ID) — Octal 4
When set on an executable file, setuid causes the program to run with the privileges of the file owner, not the calling user.
# Classic example: the passwd command runs as root
ls -l /usr/bin/passwd
-rwsr-xr-x 1 root root 68208 Feb 6 2025 /usr/bin/passwd
The s in the owner’s execute position indicates setuid is active. This allows regular users to change their own passwords by writing to /etc/shadow, a file only root can modify.
# Set the setuid bit
sudo chmod u+s /usr/local/bin/custom-tool
# Using octal (4 = setuid)
sudo chmod 4755 /usr/local/bin/custom-tool
Security warning: setuid on shell scripts is ignored by the kernel on modern Linux systems for security reasons. setuid is only effective on compiled binaries. Applying setuid carelessly is a major security risk — it can grant root access to any user.
setgid (Set Group ID) — Octal 2
setgid behaves differently depending on whether it is applied to a file or a directory.
On executable files: The program runs with the privileges of the file’s group, not the user’s primary group.
# Example: the wall command runs with group 'tty'
ls -l /usr/bin/wall
-rwxr-sr-x 1 root tty 30800 Sep 5 2025 /usr/bin/wall
On directories (much more common): New files and subdirectories created inside inherit the group of the parent directory, instead of the user’s primary group. This is essential for shared directories.
# Create a shared project directory with setgid
sudo mkdir /opt/shared-project
sudo chown root:developers /opt/shared-project
sudo chmod 2775 /opt/shared-project
# Verify — note the 's' in the group execute position
ls -ld /opt/shared-project
drwxrwsr-x 2 root developers 4096 Feb 12 10:00 /opt/shared-project
# Any file created inside inherits the 'developers' group
touch /opt/shared-project/newfile.txt
ls -l /opt/shared-project/newfile.txt
-rw-rw-r-- 1 alice developers 0 Feb 12 10:01 newfile.txt
Sticky Bit — Octal 1
The sticky bit on a directory restricts file deletion. Only the file owner, the directory owner, or root can delete or rename files inside the directory.
# /tmp is the classic sticky-bit directory
ls -ld /tmp
drwxrwxrwt 18 root root 4096 Feb 12 10:00 /tmp
The t in the others’ execute position indicates the sticky bit is active.
# Set the sticky bit on a shared directory
sudo chmod +t /opt/shared-uploads
# Or using octal
sudo chmod 1777 /opt/shared-uploads
Special Permissions Summary
| Permission | Octal | On Files | On Directories |
|---|---|---|---|
| setuid | 4000 | Runs as file owner | No standard effect |
| setgid | 2000 | Runs as file group | New files inherit directory group |
| sticky | 1000 | No modern effect | Only owner can delete their files |
Identifying Special Permissions in ls -l
| Symbol | Position | Meaning |
|---|---|---|
s (lowercase) | Owner execute | setuid + execute |
S (uppercase) | Owner execute | setuid without execute (unusual) |
s (lowercase) | Group execute | setgid + execute |
S (uppercase) | Group execute | setgid without execute |
t (lowercase) | Others execute | sticky + execute |
T (uppercase) | Others execute | sticky without execute (unusual) |
An uppercase letter (S or T) indicates the special bit is set but the underlying execute bit is not — this usually indicates a misconfiguration.
POSIX Access Control Lists (ACLs)
Why ACLs?
Standard Linux permissions support exactly one owner and one group per file. This creates problems in real-world scenarios:
- A web directory needs to be writable by the
webadminuser, readable by thewww-datagroup, and also writable by thedeployeruser for automated deployments. - A shared project needs different access levels for different teams.
- Log files need to be readable by monitoring systems without adding them to the file’s group.
ACLs solve this limitation by allowing you to attach multiple named user and group entries to a file’s permission metadata.
Checking ACL Support
Verify that your filesystem supports ACLs:
# Check mount options
mount | grep ' / '
/dev/sda1 on / type ext4 (rw,relatime,errors=remount-ro)
# For ext4, ACLs are enabled by default
# For older systems, check /etc/fstab for the 'acl' mount option
grep acl /etc/fstab
Reading ACLs with getfacl
The getfacl command displays the full ACL of a file or directory:
# Standard file without extra ACLs
getfacl /var/www/html/index.html
# file: var/www/html/index.html
# owner: webadmin
# group: www-data
user::rw-
group::r--
other::r--
This output mirrors the standard ls -l permissions. The user:: entry (without a name) represents the owner, group:: represents the owning group, and other:: represents everyone else.
Setting ACLs with setfacl
The setfacl command adds, modifies, or removes ACL entries.
# Grant read and write access to a specific user
setfacl -m u:deployer:rw /var/www/html/index.html
# Grant read access to a specific group
setfacl -m g:monitoring:r /var/log/app/application.log
# Verify the ACL
getfacl /var/www/html/index.html
# file: var/www/html/index.html
# owner: webadmin
# group: www-data
user::rw-
user:deployer:rw-
group::r--
group:monitoring:r--
mask::rw-
other::r--
Notice two new elements:
user:deployer:rw-— The named user entry.mask::rw-— The ACL mask (explained below).
Common setfacl Operations
# Add permission for a named user
setfacl -m u:alice:rwx /opt/project
# Add permission for a named group
setfacl -m g:qa-team:rx /opt/project
# Set multiple entries at once
setfacl -m u:alice:rwx,g:qa-team:rx,g:devops:rw /opt/project
# Remove a specific ACL entry
setfacl -x u:alice /opt/project
# Remove all ACL entries (revert to standard permissions)
setfacl -b /opt/project
# Apply ACLs recursively
setfacl -R -m g:developers:rwX /opt/project
Default ACLs for Directories
Default ACLs are set on directories and automatically apply to new files and subdirectories created inside them. They do not affect the directory itself — they define what ACL entries new children inherit.
# Set a default ACL so new files inside are readable by 'monitoring'
setfacl -d -m g:monitoring:r /var/log/app
# Set default ACLs for a shared project directory
setfacl -d -m u::rwx,g::rwx,o::rx /opt/shared-project
# Verify default ACLs
getfacl /opt/shared-project
# file: opt/shared-project
# owner: root
# group: developers
user::rwx
group::rwx
other::r-x
default:user::rwx
default:group::rwx
default:group:monitoring:r--
default:other::r-x
default:mask::rwx
Lines prefixed with default: show the inheritable ACL entries.
The ACL Mask
The ACL mask defines the maximum effective permissions for all named user entries, the owning group, and all named group entries. It acts as an upper-bound filter.
# Set the mask to read-only
setfacl -m m::r /opt/project/sensitive.conf
# Now even if alice has rwx in her ACL entry, the effective permission is r only
getfacl /opt/project/sensitive.conf
# file: opt/project/sensitive.conf
# owner: admin
# group: devteam
user::rw-
user:alice:rwx #effective:r--
group::rw- #effective:r--
mask::r--
other::---
The #effective: comment shows the actual permissions after the mask is applied. The mask does not affect the owner entry (user::) or the others entry (other::).
Important: When you use chmod on a file that has ACLs, the group permission field modifies the mask, not the owning group’s ACL entry. This is a common source of confusion:
# This changes the ACL mask, not the group permission
chmod g-w /opt/project/sensitive.conf
Detecting ACL-Enabled Files
Files with ACLs show a + sign at the end of the permission string in ls -l:
ls -l /opt/project/
-rw-r--r--+ 1 admin devteam 1024 Feb 12 10:00 sensitive.conf
The + indicates that extended ACL entries exist beyond the base permissions.
Backing Up and Restoring ACLs
# Save ACLs for an entire directory tree
getfacl -R /opt/project > project-acls.txt
# Restore ACLs from a backup
setfacl --restore=project-acls.txt
Web Server Permissions
Proper file permissions are critical for web server security. The web server process runs as a specific user — www-data on Debian/Ubuntu (Apache and Nginx) or nginx on CentOS/RHEL.
Identifying the Web Server User
# Apache on Debian/Ubuntu
grep -i 'user\|group' /etc/apache2/envvars
export APACHE_RUN_USER=www-data
export APACHE_RUN_GROUP=www-data
# Nginx
grep -i 'user' /etc/nginx/nginx.conf
user www-data;
# Check which user the web server process is running as
ps aux | grep -E 'apache|nginx|httpd' | head -5
Standard Web Directory Permissions
# Set ownership — your user as owner, web server as group
sudo chown -R webadmin:www-data /var/www/html
# Directories: 755 (owner rwx, group and others r-x)
sudo find /var/www/html -type d -exec chmod 755 {} \;
# Files: 644 (owner rw, group and others r)
sudo find /var/www/html -type f -exec chmod 644 {} \;
WordPress Permissions Example
WordPress requires specific permissions for different directories:
# Set base ownership
sudo chown -R www-data:www-data /var/www/wordpress
# Standard directory permissions
sudo find /var/www/wordpress -type d -exec chmod 755 {} \;
# Standard file permissions
sudo find /var/www/wordpress -type f -exec chmod 644 {} \;
# wp-config.php should be more restrictive
sudo chmod 640 /var/www/wordpress/wp-config.php
# wp-content needs write access for uploads and plugin updates
sudo chmod 775 /var/www/wordpress/wp-content
sudo chmod -R 775 /var/www/wordpress/wp-content/uploads
sudo chmod -R 775 /var/www/wordpress/wp-content/plugins
sudo chmod -R 775 /var/www/wordpress/wp-content/themes
# Prevent .htaccess modification unless explicitly needed
sudo chmod 644 /var/www/wordpress/.htaccess
Using ACLs for Web Deployment
When a deployment user (separate from the web server user) needs write access:
# Base ownership goes to the web server
sudo chown -R www-data:www-data /var/www/app
# Grant the deployment user write access via ACL
sudo setfacl -R -m u:deployer:rwX /var/www/app
sudo setfacl -R -d -m u:deployer:rwX /var/www/app
# Grant the developer group read access for debugging
sudo setfacl -R -m g:developers:rX /var/www/app
sudo setfacl -R -d -m g:developers:rX /var/www/app
Shared Directory Setup
A common requirement in multi-user environments is a directory where several users or teams can collaborate on files. Here is a complete setup:
# Create the shared directory
sudo mkdir -p /opt/team-project
# Create the group and add users
sudo groupadd project-team
sudo usermod -aG project-team alice
sudo usermod -aG project-team bob
sudo usermod -aG project-team carol
# Set ownership and setgid
sudo chown root:project-team /opt/team-project
sudo chmod 2775 /opt/team-project
# Ensure new files inherit the group (setgid handles this)
# Set a collaborative umask for team members
# Each user should add to their ~/.bashrc:
# umask 002
# Add the sticky bit to prevent accidental deletion of others' files
sudo chmod +t /opt/team-project
# Final permissions: drwxrwsr-t
ls -ld /opt/team-project
drwxrwsr-t 2 root project-team 4096 Feb 12 10:00 /opt/team-project
With this configuration:
- All team members can create, edit, and read files.
- New files automatically belong to the
project-teamgroup (setgid). - Users cannot delete each other’s files (sticky bit).
- The umask of
002ensures new files are group-writable by default.
Adding a Read-Only Auditor with ACLs
If an auditor needs to read files but not modify anything:
# Grant read-only access to the auditor user
sudo setfacl -R -m u:auditor:rX /opt/team-project
# Set default ACLs so new files also grant read access to the auditor
sudo setfacl -R -d -m u:auditor:rX /opt/team-project
# Verify
getfacl /opt/team-project
Finding Permission Issues with find
The find command is invaluable for auditing permissions across a filesystem.
Find Files by Permission
# Find all world-writable files
find / -type f -perm -o=w -not -path "/proc/*" -not -path "/sys/*" 2>/dev/null
# Find all world-writable directories (excluding standard ones)
find / -type d -perm -o=w -not -path "/tmp*" -not -path "/proc/*" -not -path "/sys/*" 2>/dev/null
# Find all setuid executables
find / -type f -perm -4000 2>/dev/null
# Find all setgid executables
find / -type f -perm -2000 2>/dev/null
# Find all setuid and setgid files combined
find / -type f \( -perm -4000 -o -perm -2000 \) -ls 2>/dev/null
Find Files with Unusual Ownership
# Find files with no valid owner (orphaned files)
find / -nouser 2>/dev/null
# Find files with no valid group
find / -nogroup 2>/dev/null
# Find files owned by a specific user
find /home -user alice -type f
# Find files not owned by a specific user in a web directory
find /var/www -not -user www-data -type f
Find Files by Permission Patterns
# Find files that are exactly 777 (a security risk)
find /var/www -type f -perm 0777
# Find files that have at least the specified permission bits set
find /opt -type f -perm -0755
# Find executable files that are not owned by root
find /usr/local/bin -type f -perm -o=x -not -user root
# Find configuration files that are world-readable
find /etc -name "*.conf" -perm -o=r -type f 2>/dev/null | head -20
Auditing Scripts
Create a simple audit script to regularly check for permission anomalies:
#!/bin/bash
# permission-audit.sh — Scan for common permission issues
echo "=== World-Writable Files ==="
find /var/www /opt /srv -type f -perm -o=w 2>/dev/null
echo ""
echo "=== Setuid Binaries (non-standard) ==="
find /usr/local -type f -perm -4000 2>/dev/null
echo ""
echo "=== Orphaned Files (no valid owner) ==="
find /home /opt /var -nouser -o -nogroup 2>/dev/null
echo ""
echo "=== Files with 777 Permissions ==="
find /var/www /opt /srv -type f -perm 0777 2>/dev/null
chmod 750 permission-audit.sh
sudo ./permission-audit.sh
Security Best Practices
Principle of Least Privilege
Always grant the minimum permissions necessary. Start restrictive and loosen only when required:
# Start with restrictive permissions
chmod 600 /etc/app/secrets.conf
chown root:root /etc/app/secrets.conf
# Only add read access for the application group if needed
chmod 640 /etc/app/secrets.conf
chown root:appgroup /etc/app/secrets.conf
Never Use 777
There is almost never a legitimate reason to set 777 on any file or directory in production. If you find yourself reaching for 777, reconsider the access model:
# WRONG — granting full access to everyone
chmod 777 /var/www/uploads # Security vulnerability
# CORRECT — grant write only to the web server and content team
sudo chown www-data:content-team /var/www/uploads
sudo chmod 2775 /var/www/uploads
Protect Sensitive Files
# SSH keys — owner-only read/write
chmod 600 ~/.ssh/id_rsa
chmod 644 ~/.ssh/id_rsa.pub
chmod 700 ~/.ssh
# Application secrets
chmod 600 /etc/app/database.yml
chmod 600 /etc/app/.env
# SSL/TLS private keys
chmod 600 /etc/ssl/private/server.key
chown root:root /etc/ssl/private/server.key
# Sudo configuration
chmod 440 /etc/sudoers.d/custom
Audit setuid and setgid Regularly
Unauthorized setuid binaries are a common attack vector for privilege escalation:
# Record baseline setuid/setgid files
find / -type f \( -perm -4000 -o -perm -2000 \) -ls 2>/dev/null > /root/suid-baseline.txt
# Compare against baseline periodically
find / -type f \( -perm -4000 -o -perm -2000 \) -ls 2>/dev/null | diff /root/suid-baseline.txt -
Immutable Files
For files that must never be modified (even by root without explicitly unlocking them), use the chattr command:
# Make a file immutable
sudo chattr +i /etc/resolv.conf
# Verify
lsattr /etc/resolv.conf
----i--------e-- /etc/resolv.conf
# Remove the immutable flag when changes are needed
sudo chattr -i /etc/resolv.conf
Filesystem Mount Options
Enhance security by restricting entire mount points:
# In /etc/fstab — mount /tmp with noexec, nosuid, nodev
tmpfs /tmp tmpfs defaults,noexec,nosuid,nodev 0 0
# Mount a data partition read-only
/dev/sdb1 /data ext4 defaults,ro 0 2
noexec— Prevents execution of any binaries on this mount.nosuid— Ignores the setuid and setgid bits for files on this mount.nodev— Prevents device file interpretation.
Troubleshooting Common Issues
”Permission denied” When Running a Script
# Check if the file has execute permission
ls -l script.sh
-rw-r--r-- 1 user user 256 Feb 12 10:00 script.sh
# Solution 1: Add execute permission
chmod +x script.sh
# Solution 2: Run with the interpreter directly (no execute bit needed)
bash script.sh
Cannot cd Into a Directory
# A directory needs execute permission to enter
ls -ld /opt/restricted
drw-r--r-- 2 root root 4096 Feb 12 10:00 /opt/restricted
# Fix: add execute permission
sudo chmod +x /opt/restricted
Files Created with Wrong Group
If new files in a shared directory get the user’s primary group instead of the directory’s group:
# Check if setgid is set on the parent directory
ls -ld /opt/shared
drwxrwxr-x 2 root devteam 4096 Feb 12 10:00 /opt/shared
# Fix: add setgid
sudo chmod g+s /opt/shared
ACL Not Working
# Check if the filesystem supports ACLs
mount | grep $(df --output=source /opt | tail -1) | grep acl
# Check if the acl package is installed
which getfacl setfacl
# If not installed
sudo apt install acl # Debian/Ubuntu
sudo dnf install acl # CentOS/RHEL/Fedora
# Verify ACLs are applied
getfacl /path/to/file
Permission Lost After chmod on ACL File
Remember that chmod modifies the ACL mask, not the group permission:
# This changes the mask — may reduce effective permissions for ACL entries
chmod 644 /opt/project/file.txt
# Check the effective permissions
getfacl /opt/project/file.txt
# Restore the mask to allow full ACL permissions
setfacl -m m::rwx /opt/project/file.txt
Resolving SELinux or AppArmor Denials
If permissions look correct but access is still denied, the issue may be a mandatory access control system:
# Check if SELinux is enforcing (CentOS/RHEL/Fedora)
getenforce
# If "Enforcing", check the audit log
sudo ausearch -m AVC -ts recent
# Check AppArmor status (Debian/Ubuntu)
sudo aa-status
# Check syslog for AppArmor denials
sudo grep DENIED /var/log/syslog | tail -10
SSH Key Permissions
SSH is strict about file permissions. If SSH refuses to use your key:
# Required permissions
chmod 700 ~/.ssh
chmod 600 ~/.ssh/id_rsa
chmod 644 ~/.ssh/id_rsa.pub
chmod 600 ~/.ssh/authorized_keys
chmod 644 ~/.ssh/known_hosts
chmod 644 ~/.ssh/config
# Ownership must be the current user
chown -R $(whoami):$(whoami) ~/.ssh
Summary
Linux file permissions are a layered system that starts simple and scales to complex multi-user environments:
- Basic permissions (
rwx) cover most single-user and small-team scenarios. - Ownership (
chown,chgrp) assigns files to the correct users and groups. - umask controls default permissions for newly created files.
- Special permissions (setuid, setgid, sticky bit) solve specific security and collaboration requirements.
- POSIX ACLs (
setfacl,getfacl) provide the fine-grained, per-user and per-group access control needed in enterprise environments.
Apply the principle of least privilege at every level. Audit permissions regularly. Use find to hunt for anomalies. Document your permission scheme so that future administrators understand the intent behind each configuration choice.