G
GuideDevOps
Lesson 7 of 17

File Permissions

Part of the Linux Fundamentals tutorial series.

The Linux Security Model

Linux determines who can access files using three categories of users and three permission types. This is the foundation of Linux security.


The Permission Model

User Categories (U-G-O)

Every file has three layers of access:

CategorySymbolMeaning
UseruThe owner of the file
GroupgUsers who belong to the file's group
OthersoEveryone else on the system

Permission Types (R-W-X)

PermissionSymbolOn a FileOn a Directory
ReadrView file contentsList directory contents
WriterModify file contentsCreate/delete files in directory
ExecutexRun file as a programEnter (cd into) directory

Reading Permissions — ls -l

When you run ls -l, the first column shows permissions in symbolic form:

$ ls -la /var/log/
total 216
drwxr-x---  2 root adm     4096 Apr 10 08:00 apache2/
-rw-r-----  1 root adm     6340 Apr 10 09:05 auth.log
drwxr-xr-x  3 root root   4096 Apr 10 08:00 apt/
-rw-r--r--  1 root root  4096 Apr 10 08:00 bootstrap.log

Breaking Down the Permission String

For auth.log:

-rw-r-----  1  root  adm  6340  Apr 10 09:05  auth.log
│││││││││
│││││││││└─ Other: READ only
││││││││└── Other: no WRITE
│││││││└─── Other: no EXECUTE
││││││└──── Group: READ only
│││││└───── Group: no WRITE
││││└────── Group: EXECUTE (can cd into dir — not meaningful for files)
│││└─────── User (owner): READ + WRITE
││└──────── User (owner): no EXECUTE
│└───────── File type: regular file (-)
└────────── File type: regular file (-)

For apache2/:

drwxr-x---  2  root  adm  4096  Apr 10 08:00  apache2/
│││││││││
│││││││││└─ Other: no permissions
││││││││└── Other: no access
│││││││└──── Group: READ + EXECUTE (can list and cd)
││││││└───── Group: no WRITE
│││││└────── User (owner): READ + WRITE + EXECUTE
││││└──────── User (owner): full access
││└────────── Directory type: d
└──────────── File type: directory

Permission String Reference

File type + User  + Group  + Other
     -             rwx     r-x     r--
     d             rwx     r-x     r-x

Changing Permissions — chmod

Symbolic Notation

# Add execute permission to a file
chmod +x script.sh
 
# Remove write permission from a directory for others
chmod o-w /shared
 
# Add read permission for group
chmod g+r config.yaml
 
# Set multiple permissions at once
chmod u+rw,g+r,o+r file.txt

Numeric (Octal) Notation

Each permission has a numeric value:

PermissionNumericCalculation
r4Read
w2Write
x1Execute
rwx74 + 2 + 1
r-x54 + 0 + 1
rw-64 + 2 + 0

Three digits = Owner, Group, Others

# rwx for owner, r-x for group, r-- for others (755)
# Most common for executables and directories
chmod 755 script.sh
 
# Full access for owner, read-only for group and others (744)
chmod 744 private.txt
 
# rwx for owner, --- for group, --- for others (700)
# Only the owner can read, write, or execute
chmod 700 /home/admin/secrets
 
# Full access for owner and group, read-only for others (774)
chmod 774 shared_folder/
 
# rw-rw-rw- (666) — world-writable — BAD practice
chmod 666 shared_file.txt
 
# For directories, 'x' must be set to allow traversal (cd)
# chmod 644 mydir/ would prevent cd into mydir/

Common Permission Patterns

ModePermissionsUse Case
777rwxrwxrwxFull access for all (avoid!)
755rwxr-xr-xExecutables, scripts, public dirs
750rwxr-x---Private project directories
700rwx------Private files, scripts, secrets
644rw-r--r--Config files, documentation
600rw-------Private keys, credentials
400r--------Highly sensitive (SSH private keys)

Special Permissions

# SetUID (set user ID) — run as file owner, not caller
# When a user runs /usr/bin/passwd, it runs as root
chmod u+s /usr/bin/passwd
# Shows as 's' in owner execute position: -rwsr-xr-x
 
# SetGID (set group ID) — run as file's group
chmod g+s /usr/bin/myapp
# Shows as 's' in group execute position: -rwxr-sr-x
 
# Sticky Bit — in a directory, only owners can delete files
# Classic use: /tmp, /var/tmp
chmod +t /shared/upload
# Shows as 't' in other execute position: drwxrwxrwt
 
# Sticky bit on /tmp
$ ls -la /tmp | head -5
drwxrwxrwt  11 root root  4096 Apr 10 08:00 .

Changing Ownership — chown

# Change file owner
sudo chown alice report.txt
 
# Change file owner and group
sudo chown alice:developers report.txt
 
# Change group only (shorthand)
sudo chown :admins report.txt
 
# Recursively change directory contents
sudo chown -R alice:developers /var/www/app
 
# Change ownership of a symbolic link itself (not target)
sudo chown -h alice link.txt
# Before
-rw-r--r--  1 bob   users   4096  Apr 10 09:00  report.txt
 
# After: sudo chown alice:developers report.txt
-rw-r--r--  1 alice developers  4096  Apr 10 09:00  report.txt

Recursive Ownership Changes

# Give a new user ownership of an entire application directory
sudo chown -R www-data:www-data /var/www/myapp
 
# Common Docker container directory permissions
sudo chown -R 1000:1000 /home/admin/app

Viewing Permissions

# Basic listing
ls -l config.yaml
-rw-r--r--  1 admin admin  894 Apr 10 08:45 config.yaml
 
# Get only permissions (numeric)
stat -c '%a' config.yaml
644
 
# Get only permissions (symbolic)
stat -c '%A' config.yaml
-rw-r--r--
 
# Full stat information
stat config.yaml
  File: config.yaml
  Size: 894        Blocks: 8          IO Block: 4096   regular file
Device: fd00h/64768d   Inode: 131088      Links: 1
Access: (0644/-rw-r--r--)  Uid: ( 1000/   admin)   Gid: ( 1000/   admin)
Access: 2026-04-10 08:45:00.000000000 +0000
Modify: 2026-04-10 08:45:00.000000000 +0000
Change: 2026-04-10 09:00:00.000000000 +0000

Umask — Default Permissions

The umask determines the default permissions for newly created files and directories.

# View current umask
$ umask
0022
 
# How umask works:
# Files get: 666 - umask = default
# Directories get: 777 - umask = default
 
# umask 0022:
# Files: 666 - 022 = 644 (rw-r--r--)
# Dirs:  777 - 022 = 755 (rwxr-xr-x)
 
# Set umask for current session
umask 027    # More restrictive: only owner has full access

Umask values and their effects:

UmaskFile DefaultDir Default
0000666 (rw-rw-rw-)777 (rwxrwxrwx)
0022644 (rw-r--r--)755 (rwxr-xr-x)
0027640 (rw-r-----)750 (rwxr-x---)
0077600 (rw-------)

Real-World DevOps Scenarios

Scenario 1: Web Server Permissions

# NGINX runs as www-data. It needs to read app files.
sudo chown -R www-data:www-data /var/www/html
sudo chmod -R 755 /var/www/html     # Read + execute for all
 
# For uploads directory, www-data needs write
sudo chown www-data:www-data /var/www/html/uploads
sudo chmod 775 /var/www/html/uploads  # Group can write too

Scenario 2: SSH Key Permissions

# Private key — most restrictive
chmod 600 ~/.ssh/id_ed25519
-rw------- 1 admin admin 1679 Apr 10 08:00 id_ed25519
 
# Public key — can be readable
chmod 644 ~/.ssh/id_ed25519.pub
-rw-r--r-- 1 admin admin  397 Apr 10 08:00 id_ed25519.pub
 
# SSH directory
chmod 700 ~/.ssh
drwx------ 2 admin admin 4096 Apr 10 08:00 .ssh
 
# Authorized keys
chmod 600 ~/.ssh/authorized_keys
-rw------- 1 admin admin  397 Apr 10 08:00 authorized_keys

Scenario 3: Application Log Directory

# App writes logs, admin needs to read them
sudo chown -R app:adm /var/log/myapp
sudo chmod -R 750 /var/log/myapp   # app and adm can access, others cannot

Quick Reference

TaskCommand
Show permissionsls -l file
Add execute for ownerchmod u+x file
Set exact permissionschmod 755 file
Make file privatechmod 600 file
Make directory accessiblechmod 755 dir
Recursive permissionschmod -R 755 dir
Change ownerchown user file
Change owner + groupchown user:group file
Recursive ownershipchown -R user:group dir
View umaskumask
Set umaskumask 027
Check special permsstat file

Practice Challenge

  1. Create a file secret.txt and set its permissions to 600
  2. Create a directory shared and set permissions to 775
  3. Create an executable script hello.sh — can you run it with ./hello.sh?
  4. What happens when you chmod -x hello.sh and try to run it again?
  5. Set the sticky bit on the shared directory and verify it appears as drwxrwxrwt
  6. Use stat to view all permission details of a file