Files, Permissions & Ownership
Why This Matters
It is 2 AM. A deployment just went out and the application is returning 403 Forbidden errors. The developer says "it works on my machine." You SSH into the production server, check the application directory, and discover that the deployment script set the wrong file permissions -- the web server process cannot read the files it needs to serve. You run one chmod command and the site comes back to life.
Permission problems are among the most common issues in Linux administration. They are also among the quickest to fix -- if you understand how they work. This chapter gives you a thorough, hands-on understanding of Linux file types, permission bits, ownership, and the more advanced features like SUID, sticky bits, and ACLs.
Try This Right Now
# Look at permissions on some files
ls -l /etc/passwd
ls -l /etc/shadow
ls -la ~/
# Create a test file and inspect it
touch /tmp/permtest
ls -l /tmp/permtest
# What is your current umask?
umask
# Who are you?
id
whoami
groups
Look at the output of ls -l /etc/passwd:
-rw-r--r-- 1 root root 2847 Jan 15 10:30 /etc/passwd
That string -rw-r--r-- encodes the file type and permissions. By the end of this chapter, you will be able to read and set these in your sleep.
File Types in Linux
Linux has seven file types. The first character in the ls -l output tells you which type you are looking at.
| Symbol | Type | Example |
|---|---|---|
- | Regular file | /etc/passwd |
d | Directory | /home/alice |
l | Symbolic link | /bin -> /usr/bin |
b | Block device | /dev/sda |
c | Character device | /dev/tty |
s | Socket | /run/systemd/journal/socket |
p | Named pipe (FIFO) | Created with mkfifo |
Let us see each one:
# Regular file
ls -l /etc/hostname
# -rw-r--r-- ...
# Directory
ls -ld /etc
# drwxr-xr-x ...
# Symbolic link
ls -l /bin
# lrwxrwxrwx ... /bin -> usr/bin
# Block device (disk)
ls -l /dev/sda 2>/dev/null || ls -l /dev/vda 2>/dev/null
# brw-rw---- ...
# Character device (terminal)
ls -l /dev/tty
# crw-rw-rw- ...
# Socket
ls -l /run/systemd/journal/dev-log 2>/dev/null
# srw-rw-rw- ...
# Named pipe
mkfifo /tmp/mypipe
ls -l /tmp/mypipe
# prw-r--r-- ...
rm /tmp/mypipe
Think About It: Why does Linux distinguish between block devices and character devices? Think about how you access a hard disk (in blocks of data) versus a keyboard (one character at a time).
Understanding Permission Bits
Every file has three sets of permission bits:
Owner Group Others
r w x r w x r w x
| Permission | On a File | On a Directory |
|---|---|---|
r (read) | Read file contents | List directory contents (ls) |
w (write) | Modify file | Create/delete files in directory |
x (execute) | Run as program | Enter directory (cd) |
Let us decode the string -rw-r--r--:
- rw- r-- r--
| | | |
| | | +-- Others: read only
| | +--------- Group: read only
| +---------------- Owner: read + write
+--------------------- Type: regular file
Hands-On: See Permissions in Action
# Create a test directory
mkdir -p /tmp/permlab
cd /tmp/permlab
# Create a file
echo "Hello, permissions!" > testfile.txt
ls -l testfile.txt
# -rw-r--r-- 1 youruser yourgroup 20 ... testfile.txt
# Remove read permission for yourself
chmod u-r testfile.txt
cat testfile.txt
# cat: testfile.txt: Permission denied
# Restore read, remove for others
chmod u+r,o-r testfile.txt
ls -l testfile.txt
# -rw-r----- 1 youruser yourgroup 20 ... testfile.txt
# Make it executable
chmod u+x testfile.txt
ls -l testfile.txt
# -rwxr----- 1 youruser yourgroup 20 ... testfile.txt
Directory Permissions Are Different
This is a common source of confusion. For directories:
r(read): You can list the directory contents withlsw(write): You can create, rename, or delete files insidex(execute): You can enter the directory withcdand access files inside
# Create a test directory
mkdir /tmp/dirtest
echo "secret" > /tmp/dirtest/file.txt
# Remove execute permission
chmod a-x /tmp/dirtest
ls /tmp/dirtest # Works (read still allowed)
cat /tmp/dirtest/file.txt # FAILS -- can't traverse directory
# Restore execute, remove read
chmod a+x,a-r /tmp/dirtest
ls /tmp/dirtest # FAILS -- can't list
cat /tmp/dirtest/file.txt # Works (if you know the filename)
# Clean up
chmod 755 /tmp/dirtest
Think About It: A directory has
rpermission but notx. Can you list files in it? Can you read those files? What about the reverse --xbut nor?
Octal (Numeric) Notation
Sysadmins almost always use the octal shorthand instead of the symbolic rwx notation. Each permission bit is a power of 2:
r = 4
w = 2
x = 1
So: rwx = 4+2+1 = 7
rw- = 4+2+0 = 6
r-x = 4+0+1 = 5
r-- = 4+0+0 = 4
--- = 0+0+0 = 0
Three digits for owner, group, others:
chmod 755 file --> rwxr-xr-x (owner: full, group/others: read+execute)
chmod 644 file --> rw-r--r-- (owner: read+write, group/others: read only)
chmod 700 file --> rwx------ (owner: full, nobody else)
chmod 600 file --> rw------- (owner: read+write, nobody else)
chmod 777 file --> rwxrwxrwx (everyone: full access -- almost never do this!)
Common permissions you will use every day:
| Octal | Symbolic | Typical Use |
|---|---|---|
644 | rw-r--r-- | Regular files (configs, HTML, etc.) |
755 | rwxr-xr-x | Directories, executable scripts |
600 | rw------- | Private files (SSH keys, secrets) |
700 | rwx------ | Private directories |
750 | rwxr-x--- | Group-accessible directories |
664 | rw-rw-r-- | Shared writable files |
# Practice: set permissions using octal
chmod 644 /tmp/permlab/testfile.txt
ls -l /tmp/permlab/testfile.txt
chmod 600 /tmp/permlab/testfile.txt
ls -l /tmp/permlab/testfile.txt
chmod 755 /tmp/permlab/testfile.txt
ls -l /tmp/permlab/testfile.txt
Safety Warning: Never use
chmod 777on production systems. It means "every user on this system can read, write, and execute this file." If someone tells you to runchmod -R 777 /, they are either joking or about to destroy your system's security.
Ownership: chown and chgrp
Every file has an owner (user) and a group. You change them with chown and chgrp.
# View ownership
ls -l /tmp/permlab/testfile.txt
# -rwxr-xr-x 1 youruser yourgroup 20 ... testfile.txt
# ^^^^^^^^ ^^^^^^^^^
# owner group
# Change owner (requires root)
sudo chown root /tmp/permlab/testfile.txt
ls -l /tmp/permlab/testfile.txt
# Change group
sudo chgrp root /tmp/permlab/testfile.txt
ls -l /tmp/permlab/testfile.txt
# Change both at once: user:group
sudo chown youruser:yourgroup /tmp/permlab/testfile.txt
ls -l /tmp/permlab/testfile.txt
# Recursively change ownership of a directory
sudo chown -R www-data:www-data /var/www/html 2>/dev/null
Real-World Scenario: Web Server Permissions
A web server (Nginx or Apache) runs as a specific user, typically www-data (Debian/Ubuntu) or nginx (RHEL/Fedora). The web content files must be readable by that user.
# Typical web directory setup
sudo mkdir -p /var/www/mysite
sudo chown -R www-data:www-data /var/www/mysite
sudo chmod -R 755 /var/www/mysite
sudo chmod 644 /var/www/mysite/*.html 2>/dev/null
Distro Note: The web server user is
www-dataon Debian/Ubuntu,nginxon RHEL/Fedora (when using Nginx), andapacheon RHEL/Fedora (when using Apache httpd).
The umask: Default Permission Control
When you create a new file or directory, the permissions are not set to 777. They are filtered through your umask (user file-creation mask).
# Check your current umask
umask
# Typical output: 0022
# What does 0022 mean?
# Default for files: 666 - 022 = 644 (rw-r--r--)
# Default for dirs: 777 - 022 = 755 (rwxr-xr-x)
# Verify
touch /tmp/umask-test-file
mkdir /tmp/umask-test-dir
ls -l /tmp/umask-test-file
ls -ld /tmp/umask-test-dir
The umask subtracts permissions. Common umask values:
| umask | File result | Directory result | Use case |
|---|---|---|---|
022 | 644 | 755 | Default (shared readable) |
027 | 640 | 750 | Group-readable, others blocked |
077 | 600 | 700 | Private (only owner) |
002 | 664 | 775 | Group-collaborative |
# Temporarily change umask
umask 077
touch /tmp/private-file
ls -l /tmp/private-file
# -rw------- 1 youruser yourgroup 0 ... /tmp/private-file
# Reset to default
umask 022
To make umask changes permanent, add umask 027 to your ~/.bashrc or set it system-wide in /etc/login.defs or /etc/profile.
Special Permission Bits: SUID, SGID, and Sticky
Beyond the basic rwx bits, there are three special bits that you will encounter in production systems.
SUID (Set User ID) -- The 4 Bit
When set on an executable, the program runs as the file's owner, not the user who invoked it. This is how passwd can modify /etc/shadow even when run by a regular user.
# Notice the 's' in the owner's execute position
ls -l /usr/bin/passwd
# -rwsr-xr-x 1 root root ... /usr/bin/passwd
# ^
# SUID bit
# Find all SUID binaries on the system
sudo find / -perm -4000 -type f 2>/dev/null
Setting SUID:
# Using octal (4 prepended)
chmod 4755 somefile
# Using symbolic
chmod u+s somefile
Safety Warning: SUID binaries are a security-sensitive feature. A SUID root program with a vulnerability can give attackers root access. Regular security audits should check for unexpected SUID binaries.
SGID (Set Group ID) -- The 2 Bit
On an executable: the program runs with the file's group privileges. On a directory: new files created inside inherit the directory's group (instead of the creator's primary group). This is extremely useful for shared project directories.
# Create a shared project directory
sudo mkdir /tmp/project
sudo chgrp developers /tmp/project 2>/dev/null || sudo chgrp staff /tmp/project
sudo chmod 2775 /tmp/project
ls -ld /tmp/project
# drwxrwsr-x ... /tmp/project
# ^
# SGID bit (s in group execute position)
# New files inherit the group
touch /tmp/project/newfile
ls -l /tmp/project/newfile
# The group will be 'developers' (or 'staff'), not your primary group
Sticky Bit -- The 1 Bit
When set on a directory, only the file's owner (or root) can delete or rename files inside, even if others have write permission. The classic example is /tmp.
ls -ld /tmp
# drwxrwxrwt ... /tmp
# ^
# Sticky bit (t in others execute position)
# Without the sticky bit, any user could delete any other user's files in /tmp
# With it, you can only delete your own files
Setting the sticky bit:
chmod 1777 /tmp/shared-dir # Octal
chmod +t /tmp/shared-dir # Symbolic
Summary of Special Bits
Octal Symbolic Position Effect
4xxx u+s Owner execute: s/S SUID -- run as file owner
2xxx g+s Group execute: s/S SGID -- run as file group / inherit group
1xxx +t Other execute: t/T Sticky -- only owner can delete
When you see an uppercase S or T instead of lowercase, it means the special bit is set but the underlying execute bit is NOT set (an unusual and often incorrect configuration).
# Uppercase S means SUID without execute -- usually a mistake
chmod 4644 /tmp/permlab/testfile.txt
ls -l /tmp/permlab/testfile.txt
# -rwSr--r-- ... (capital S = SUID set but no execute)
# Fix it
chmod 4755 /tmp/permlab/testfile.txt
ls -l /tmp/permlab/testfile.txt
# -rwsr-xr-x ... (lowercase s = SUID with execute)
Access Control Lists (ACLs)
Traditional Unix permissions are limited: one owner, one group, and everyone else. What if you need to give user alice read access and user bob read+write access to the same file, without creating a special group? ACLs solve this.
Checking ACL Support
# Check if the filesystem supports ACLs
mount | grep -E 'ext4|xfs|btrfs'
# Most modern filesystems support ACLs by default
# Install ACL tools if needed
# Debian/Ubuntu: sudo apt install acl
# Fedora/RHEL: sudo dnf install acl
# (usually pre-installed)
Using getfacl and setfacl
# Create a test file
echo "ACL test" > /tmp/acltest.txt
# View current ACL
getfacl /tmp/acltest.txt
# file: tmp/acltest.txt
# owner: youruser
# group: yourgroup
# user::rw-
# group::r--
# other::r--
# Grant specific user read+write access
sudo setfacl -m u:nobody:rw /tmp/acltest.txt
# View updated ACL
getfacl /tmp/acltest.txt
# Notice the new line: user:nobody:rw-
# ls -l now shows a '+' indicating ACLs are present
ls -l /tmp/acltest.txt
# -rw-rw-r--+ 1 youruser yourgroup ... /tmp/acltest.txt
# ^
# The + means ACLs are set
# Grant a group specific permissions
sudo setfacl -m g:adm:r /tmp/acltest.txt
# Set a default ACL on a directory (affects new files created inside)
mkdir /tmp/acl-dir
setfacl -d -m u:nobody:rwx /tmp/acl-dir
getfacl /tmp/acl-dir
# Remove a specific ACL entry
setfacl -x u:nobody /tmp/acltest.txt
# Remove all ACLs
setfacl -b /tmp/acltest.txt
ACL Quick Reference
| Command | Effect |
|---|---|
getfacl file | Show ACLs on a file |
setfacl -m u:alice:rwx file | Give user alice rwx access |
setfacl -m g:devs:rx file | Give group devs r-x access |
setfacl -x u:alice file | Remove alice's ACL entry |
setfacl -b file | Remove all ACLs |
setfacl -d -m u:alice:rwx dir | Set default ACL for new files in dir |
setfacl -R -m u:alice:rx dir | Apply recursively |
Think About It: When would you use ACLs instead of traditional Unix permissions? Think about a web development team where the designer needs read-only access to CSS files, the developer needs full access, and the deployment bot needs read+execute.
Real-World Permission Scenarios
Scenario 1: SSH Key Permissions
SSH is very strict about permissions. If your key files are too permissive, SSH will refuse to use them.
# These are the required permissions
chmod 700 ~/.ssh
chmod 600 ~/.ssh/id_rsa # Private key: owner read/write ONLY
chmod 644 ~/.ssh/id_rsa.pub # Public key: readable
chmod 600 ~/.ssh/authorized_keys # authorized_keys: owner read/write ONLY
chmod 644 ~/.ssh/config # SSH config: readable
# If you get "Permissions too open" errors, this is why
Scenario 2: Shared Team Directory
# Create a shared directory for the 'devteam' group
sudo groupadd devteam
sudo usermod -aG devteam alice
sudo usermod -aG devteam bob
sudo mkdir /opt/project
sudo chown root:devteam /opt/project
sudo chmod 2775 /opt/project
# 2 = SGID (new files inherit the group)
# 775 = rwxrwxr-x (owner and group can write, others can read)
Scenario 3: Fixing the Web Server 403 Error
# The problem: web server can't read files
# Step 1: Check what user the web server runs as
ps aux | grep -E 'nginx|apache|httpd' | head -3
# Step 2: Check current permissions
ls -la /var/www/html/
# Step 3: Fix ownership
sudo chown -R www-data:www-data /var/www/html/
# Step 4: Fix permissions
sudo find /var/www/html -type d -exec chmod 755 {} \;
sudo find /var/www/html -type f -exec chmod 644 {} \;
Debug This
You have a script called deploy.sh with the following permissions:
-rw-r--r-- 1 deploy deploy 1542 Jan 20 14:30 deploy.sh
When you try to run it, you get "Permission denied." You run chmod 777 deploy.sh and it works, but your security team flags this as a violation. What is the minimum permission change needed?
Solution
The script is missing the execute bit. You do not need 777. The minimum fix is:
chmod u+x deploy.sh
# Result: -rwxr--r-- (owner can execute, no extra access for others)
# Or if the whole team needs to run it:
chmod 755 deploy.sh
# Result: -rwxr-xr-x (everyone can execute, only owner can modify)
Using 777 was wrong because it gives write access to everyone -- any user on the system could modify your deploy script and inject malicious commands.
What Just Happened?
+------------------------------------------------------------------+
| CHAPTER 6 RECAP |
+------------------------------------------------------------------+
| |
| File Types: regular(-) dir(d) link(l) block(b) char(c) |
| socket(s) pipe(p) |
| |
| Permission Bits: r=4 w=2 x=1 |
| Three sets: Owner | Group | Others |
| Common octals: 644 (files), 755 (dirs), 600 (private) |
| |
| Commands: |
| chmod -- Change permissions (symbolic or octal) |
| chown -- Change owner (and optionally group) |
| chgrp -- Change group |
| umask -- Set default permission mask |
| |
| Special bits: |
| SUID (4xxx) -- Run as file owner |
| SGID (2xxx) -- Run as file group / inherit group in dirs |
| Sticky (1xxx) -- Only owner can delete in shared dirs |
| |
| ACLs: getfacl / setfacl for fine-grained access control |
| |
+------------------------------------------------------------------+
Try This
Exercises
-
Decode these permissions. Without using a computer, write out what each octal means in
rwxnotation:7504711177727550600
-
Permission detective. Run
ls -l /usr/bin/passwd,ls -l /etc/shadow, andls -l /etc/passwd. Explain whypasswdhas the SUID bit and how this allows regular users to change their password. -
Directory permissions lab. Create a directory where:
- The owner can do everything
- The group can read and enter but not create files
- Others have no access at all What octal value is this?
-
ACL challenge. Create a file. Using ACLs, give three different users three different levels of access (one gets
r, anotherrw, anotherrwx). Verify withgetfacl. -
Security audit. Find all SUID and SGID binaries on your system. How many are there? Research what three of them do and explain why they need the special bit.
Bonus Challenge
Write a script that takes a directory as an argument and produces a permission report:
- Lists all files with their octal permissions
- Flags any files with
777permissions - Flags any SUID/SGID files
- Reports the total number of files for each permission level
#!/bin/bash
# permission-audit.sh - Audit file permissions in a directory
DIR="${1:-.}"
echo "=== Permission Audit: $DIR ==="
echo ""
echo "--- Files with 777 permissions (DANGER!) ---"
find "$DIR" -perm 777 -type f 2>/dev/null
echo ""
echo "--- SUID files ---"
find "$DIR" -perm -4000 -type f 2>/dev/null
echo ""
echo "--- SGID files ---"
find "$DIR" -perm -2000 -type f 2>/dev/null
echo ""
echo "--- Permission distribution ---"
find "$DIR" -type f -printf '%m\n' 2>/dev/null | sort | uniq -c | sort -rn | head -20
echo ""
echo "--- World-writable files ---"
find "$DIR" -perm -o+w -type f 2>/dev/null
Next up: Chapter 7 -- Disks, Partitions & Filesystems. Now that you know about files and permissions, let us look at the physical storage underneath it all.