Command documentation sourced from the linux-command project This comprehensive command reference is part of the linux-command documentation project.
chmod - Complete Guide to File Mode and Permissions Management
The chmod (change mode) command is one of the most fundamental and critical tools in Unix/Linux system administration. It changes the access permissions of files and directories, controlling who can read, write, and execute files. This command is essential for Linux security, file management, and proper system operation.
Table of Contents
- Overview and History
- Syntax and Basic Usage
- Complete Options Reference
- Unix/Linux Permission System
- Octal Notation (Numeric Mode)
- Symbolic Notation (Character Mode)
- Special Permissions and Bits
- Practical Examples and Use Cases
- Advanced Techniques
- System Administration Scenarios
- Scripting and Automation
- Security Considerations
- Performance Optimization
- Troubleshooting Guide
- Integration with Other Tools
- Best Practices
- Related Commands
Overview and History
Historical Context
The chmod command has its roots in the original Unix system developed at Bell Labs in the early 1970s. The permission system was designed to provide a simple yet effective way to control access to files in a multi-user environment. The concept of read, write, and execute permissions has remained largely unchanged since then, demonstrating its effectiveness and simplicity.
Evolution and Standards
- POSIX: Standardized in IEEE Std 1003.1 (POSIX.1)
- GNU Coreutils: Enhanced version with additional features
- BSD variants: Slight differences in behavior and available options
- Linux: Incorporates features from both GNU and BSD traditions
Why chmod Matters
In modern Linux systems, proper file permissions are crucial for:
- Security: Preventing unauthorized access to sensitive data
- System Integrity: Protecting critical system files
- Multi-user Environments: Managing collaborative workspaces
- Application Security: Ensuring services run with appropriate privileges
- Compliance: Meeting security standards and regulations
Syntax and Basic Usage
Complete Syntax
# Primary syntax forms
chmod [OPTIONS] MODE[,MODE]... FILE...
chmod [OPTIONS] OCTAL-MODE FILE...
chmod [OPTIONS] --reference=RFILE FILE...
# Alternative syntax (legacy compatibility)
chmod [OPTIONS] [ugoa...][[+-=]rwxXst...] FILE...
Basic Examples
# Simple permission changes
chmod 755 script.sh # Numeric mode
chmod u+x script.sh # Symbolic mode
chmod --reference=template file.txt # Reference mode
Complete Options Reference
Operation Mode Options
-R, --recursive
Purpose: Change files and directories recursively
Syntax: chmod -R MODE DIRECTORY
Behavior: Applies permissions to the specified directory and all contents beneath it
Examples:
# Recursively set permissions on a web directory
chmod -R 755 /var/www/html
# Remove write permissions recursively
chmod -R go-w /shared/documents
# Apply permissions with verbose output
chmod -Rv a+rX /public/files
Important Notes:
- Can be dangerous on system directories
- Use with
--preserve-rootto protect root filesystem - Follows symbolic links by default on some systems
-c, --changes
Purpose: Report only when a change is made
Syntax: chmod -c MODE FILE...
Behavior: Displays output only for files whose permissions were actually changed
Examples:
# Show only changed files
chmod -c 644 *.txt
# Combine with recursive operation
chmod -Rc 755 /home/user/public_html
# Useful in scripts for logging
chmod -c --reference=backup.sh new_script.sh && echo "Permissions updated"
-v, --verbose
Purpose: Output a diagnostic for every file processed
Syntax: chmod -v MODE FILE...
Behavior: Shows detailed information about each file, whether changed or not
Examples:
# Detailed output for all files
chmod -v 755 *.sh
# Verbose recursive operation
chmod -Rv 755 /opt/application
# Debug permission issues
chmod -v u+x script.sh 2>&1 | tee chmod.log
-f, --silent, --quiet
Purpose: Suppress most error messages
Syntax: chmod -f MODE FILE...
Behavior: Doesn't report files that can't be modified or don't exist
Examples:
# Silent operation (ignore errors)
chmod -f 755 *.sh
# Clean up permissions without error output
find . -name "*.tmp" -exec chmod -f 600 {} \;
# Batch operations where some files may not exist
chmod -f 644 file1.txt file2.txt file3.txt
Reference Mode Options
--reference=RFILE
Purpose: Use RFILE's mode instead of MODE values
Syntax: chmod --reference=SOURCE_FILE TARGET_FILE...
Behavior: Copies the exact permission bits from the reference file to target files
Examples:
# Copy permissions from template
chmod --reference=config.php new_config.php
# Apply template permissions to multiple files
chmod --reference=secure_template.sh *.sh
# Restore permissions from backup
find . -name "*.backup" -exec chmod --reference={} {}.restored \;
Safety Options
--preserve-root
Purpose: Fail to operate recursively on '/' (root directory)
Syntax: chmod --preserve-root -R MODE /
Behavior: Prevents accidental recursive permission changes on the root filesystem
Examples:
# Safe recursive operations
chmod --preserve-root -R 755 /usr/local/bin
# Set as default in bash profile
alias chmod='chmod --preserve-root'
# Override when absolutely necessary (rare)
chmod --no-preserve-root -R 755 /emergency/fix
--no-preserve-root
Purpose: Treat '/' specially (default behavior)
Syntax: chmod --no-preserve-root -R MODE /
Behavior: Allows recursive operations on root directory (use with extreme caution)
Standard Options
--help
Purpose: Display help message and exit
Syntax: chmod --help
Behavior: Shows usage information and available options
--version
Purpose: Output version information and exit
Syntax: chmod --version
Behavior: Displays chmod version and build information
Unix/Linux Permission System
File Type Indicators
The first character of ls -l output indicates the file type:
- Regular file
d Directory
l Symbolic link
c Character device
b Block device
p Named pipe
s Socket
Standard Permission Bits
Position: 1 2 3 4 5 6 7 8 9
Meaning: t r w x r w x r w x
Type: [ owner ][ group ][ others]
Permission Categories
- User (Owner): The user who owns the file
- Group: The group that owns the file
- Others: All other users on the system
Permission Types
- Read (r): View file contents or directory listing
- Write (w): Modify file contents or directory entries
- Execute (x): Execute file or access directory
Octal Notation (Numeric Mode)
Complete Octal Reference
Value | Binary | Permissions | Description
------+--------+-------------+------------
0 | 000 | --- | No permissions
1 | 001 | --x | Execute only
2 | 010 | -w- | Write only
3 | 011 | -wx | Write and execute
4 | 100 | r-- | Read only
5 | 101 | r-x | Read and execute
6 | 110 | rw- | Read and write
7 | 111 | rwx | All permissions
Special Octal Values (First Digit)
Value | Special Bits | Description
------+--------------+------------
0 | None | No special permissions
1 | Sticky bit | t - Restricted deletion
2 | SGID | s - Group inheritance
3 | SGID + Sticky| sg - Both features
4 | SUID | s - Execute as owner
5 | SUID + Sticky| st - Both features
6 | SUID + SGID | ss - Both features
7 | All three | sst - All special bits
Common Permission Patterns
Standard File Permissions
# Read/write for owner, read for others (644)
chmod 644 document.txt
# drw-r--r-- (owner: read/write, group/others: read)
# Read/write for owner and group, read for others (664)
chmod 664 team_file.txt
# drw-rw-r-- (owner/group: read/write, others: read)
# Private file (600)
chmod 600 private_key.pem
# drw------- (owner: read/write, group/others: none)
Standard Directory Permissions
# Owner full access, others read/execute (755)
chmod 755 public_directory/
# drwxr-xr-x (owner: all, group/others: read/execute)
# Shared directory with group write (775)
chmod 775 team_project/
# drwxrwxr-x (owner/group: all, others: read/execute)
# Private directory (700)
chmod 700 private_folder/
# drwx------ (owner: all, group/others: none)
Executable File Permissions
# Standard executable (755)
chmod 755 script.sh
# drwxr-xr-x (owner: all, group/others: read/execute)
# System executable (755 or 555)
chmod 555 /usr/local/bin/tool
# dr-xr-xr-x (all users: read/execute, no write)
# Development script (775)
chmod 775 build.sh
# drwxrwxr-x (owner/group: all, others: read/execute)
Octal Calculation Examples
# Calculate 755:
# Owner: 7 (4+2+1) = rwx
# Group: 5 (4+0+1) = r-x
# Others: 5 (4+0+1) = r-x
# Result: rwxr-xr-x
# Calculate 640:
# Owner: 6 (4+2+0) = rw-
# Group: 4 (4+0+0) = r--
# Others: 0 (0+0+0) = ---
# Result: rw-r-----
Symbolic Notation (Character Mode)
Symbolic Components
Symbolic notation consists of three parts: [WHO][OPERATION][PERMISSION]
WHO (Target)
u User (file owner)
g Group
o Others
a All (default, equivalent to ugo)
OPERATION
+ Add permissions
- Remove permissions
= Set exact permissions (remove others)
PERMISSIONS
r Read permission
w Write permission
x Execute permission
X Special execute (directory or already executable)
s Set user or group ID
t Sticky bit
Special Execute Permission (X)
The capital X is a powerful feature that adds execute permission only when appropriate:
- Files: Adds execute only if the file is already executable for some user
- Directories: Always adds execute (required for directory access)
# Add execute to scripts but not to regular files
chmod a+X *.sh *.txt
# Scripts: get execute permission
# Text files: remain unchanged
# Make directory tree traversable
chmod -R a+X /path/to/directory
# Directories get execute permission
# Regular files keep current execute status
Symbolic Examples
Basic Operations
# Add execute permission for owner
chmod u+x script.sh
# Remove write permission from group
chmod g-w config.txt
# Add read permission for others
chmod o+r public_file.txt
# Add execute permission for all users
chmod a+x binary_program
Multiple Operations
# Remove write from group and others
chmod go-w important_file.txt
# Set exact permissions for owner, read/execute for others
chmod u=rwx,go=rx script.sh
# Add read/write for owner, read for group
chmod u+rw,g+r document.txt
# Complex: remove execute from all, add read for all
chmod a-x,a+r regular_file.txt
Conditional Operations
# Make executable only if not already executable
chmod +x script.sh # Uses X when combined with a
# Remove all permissions from others
chmod o= secret_file.txt
# Copy permissions between categories
chmod g=u filename # Group gets same as owner
chmod o=g filename # Others get same as group
Multiple File Operations
# Apply different permissions to different file types
chmod u+x *.sh # Scripts executable by owner
chmod go-w *.conf # Config files not writable by group/others
chmod a+r *.txt *.md # Documents readable by all
Special Permissions and Bits
Set User ID (SUID)
Overview
The Set User ID (SUID) bit allows a program to execute with the permissions of the file owner rather than the user who runs it. This is particularly useful for programs that need elevated privileges for specific operations.
SUID in Octal Notation
# Add SUID (4000 + base permissions)
chmod 4755 program # SUID + rwxr-xr-x
chmod 4711 sensitive_tool # SUID + rwx--x--x
chmod 4600 setuid_binary # SUID + rw-------
# Symbolic notation
chmod u+s program # Add SUID
chmod u-s program # Remove SUID
SUID Examples
# Common system programs with SUID
chmod 4755 /usr/bin/passwd # Allow users to change passwords
chmod 4755 /usr/bin/sudo # Allow command execution as other users
chmod 2755 /usr/bin/write # Allow writing to other users' terminals
# Application examples
chmod 4755 backup_script # Script that reads all user files
chmod 4755 log_analyzer # Script that reads all log files
Security Considerations for SUID
# Find all SUID files (security audit)
find / -perm -4000 -type f -ls
# Remove dangerous SUID bits
chmod u-s /tmp/unsafe_program
# Alternative: use capabilities instead of SUID
setcap cap_net_raw+ep /usr/bin/ping # Modern approach
When to Use SUID
- Password programs: Need to modify system files
- Backup utilities: Need read access to all files
- System monitoring: Need access to system information
- Network tools: Need privileged network access
Set Group ID (SGID)
Overview
The Set Group ID (SGID) bit has two distinct behaviors:
- For files: Executable runs with group permissions of file owner
- For directories: New files inherit directory's group ownership
SGID in Octal Notation
# Add SGID (2000 + base permissions)
chmod 2755 program # SGID + rwxr-xr-x
chmod 2775 shared_project/ # SGID + rwxrwxr-x
chmod 2070 team_file.txt # SGID + ---rwx---
# Symbolic notation
chmod g+s program # Add SGID
chmod g+s shared_dir/ # Add SGID to directory
chmod g-s program # Remove SGID
SGID for Directories (Common Use Case)
# Create shared project directory
mkdir /home/shared/team_project
chown :developers /home/shared/team_project
chmod 2775 /home/shared/team_project
# Test SGID behavior
touch /home/shared/team_project/new_file.txt
ls -l new_file.txt
# Output: -rw-rw-r-- 1 user developers new_file.txt
# New file inherits group "developers"
# Apply SGID recursively to existing directory
find /home/shared/team_project -type d -exec chmod g+s {} \;
SGID for Files
# Programs that need group-based privileges
chmod 2755 /usr/bin/write # Write to other users' terminals
chmod 2755 /usr/local/bin/team_tool # Team collaboration tool
# Database programs that need group access
chmod 2755 /usr/local/bin/db_query
SGID Use Cases
- Development teams: Shared code repositories
- Document collaboration: Shared document folders
- Group applications: Programs used by specific groups
- Log directories: Group access to shared logs
Sticky Bit
Overview
The sticky bit, when set on a directory, restricts file deletion to the file owner, directory owner, or root user. This is commonly used on shared temporary directories.
Sticky Bit in Octal Notation
# Add sticky bit (1000 + base permissions)
chmod 1777 /tmp # Sticky bit + rwxrwxrwx
chmod 1775 /shared/uploads/ # Sticky bit + rwxrwxr-x
chmod 1770 /shared/private/ # Sticky bit + rwxrwx---
# Symbolic notation
chmod o+t /tmp # Add sticky bit
chmod o+t /shared/uploads/ # Add sticky bit to directory
chmod o-t /tmp # Remove sticky bit
Sticky Bit Examples
# Standard system directories
chmod 1777 /tmp # Temporary files
chmod 1777 /var/tmp # System temporary files
chmod 1777 /usr/tmp # User temporary files
# Application-specific directories
chmod 1777 /var/www/uploads/ # Web upload directory
chmod 1777 /home/shared/public/ # Shared public directory
chmod 1770 /home/shared/team/ # Team-only shared directory
Testing Sticky Bit Behavior
# Create test directory
mkdir /tmp/sticky_test
chmod 1777 /tmp/sticky_test
cd /tmp/sticky_test
# Create test files as different users
sudo -u user1 touch user1_file.txt
sudo -u user2 touch user2_file.txt
# Try to delete other user's files (should fail)
sudo -u user2 rm user1_file.txt # Permission denied
# user1 can delete their own file
sudo -u user1 rm user1_file.txt # Success
Combining Special Permissions
Multiple Special Bits
# SUID + SGID (6000 + base)
chmod 6755 critical_program # SUID + SGID + rwsr-sr-x
# SUID + Sticky (5000 + base)
chmod 5777 special_dir/ # SUID + sticky + rwxrwxrwt
# SGID + Sticky (3000 + base)
chmod 3777 shared_temp/ # SGID + sticky + rwxrwxrwt
# All three special bits (7000 + base)
chmod 7777 super_special/ # SUID + SGID + sticky + rwsrwsrwt
Real-World Combinations
# Development directory with SGID and sticky
mkdir /home/dev/shared
chmod 3770 /home/dev/shared
chown :developers /home/dev/shared
# New files get developers group, only owners can delete
# Application directory with SUID on binaries
find /opt/app -name "*.bin" -exec chmod 4755 {} \;
# Binaries run as app user
# Temporary directory with SGID and sticky
mkdir /app/shared/tmp
chmod 3777 /app/shared/tmp
chown :appusers /app/shared/tmp
# New files inherit group, only creators can delete
Special Permission Security
Finding Special Permissions
# Find all SUID files
find / -perm -4000 -type f -exec ls -la {} \;
# Find all SGID files
find / -perm -2000 -type f -exec ls -la {} \;
# Find all sticky bit directories
find / -perm -1000 -type d -exec ls -ld {} \;
# Find files with multiple special bits
find / -perm -6000 -type f -exec ls -la {} \; # SUID + SGID
find / -perm -7000 -type f -exec ls -la {} \; # All special bits
Security Audit Script
#!/bin/bash
# audit_special_permissions.sh
echo "=== SUID Files Audit ==="
find / -perm -4000 -type f -exec ls -lah {} \; | head -20
echo -e "\n=== SGID Files Audit ==="
find / -perm -2000 -type f -exec ls -lah {} \; | head -20
echo -e "\n=== Sticky Directories Audit ==="
find / -perm -1000 -type d -exec ls -ldh {} \; | head -20
echo -e "\n=== Suspicious SUID files in /tmp ==="
find /tmp -perm -4000 -type f -exec ls -lah {} \;
Practical Examples and Use Cases
Basic Permission Management
Daily File Operations
# Make script executable
chmod +x backup.sh
# Secure configuration file
chmod 600 config.ini
# Make document readable by all
chmod 644 report.pdf
# Create private directory
mkdir private && chmod 700 private
# Share directory with team
mkdir team_shared && chmod 775 team_shared
File Type-Specific Permissions
# Images and documents
chmod 644 *.jpg *.png *.pdf *.txt
# Configuration files
chmod 600 *.conf *.ini *.cfg
# Scripts and executables
chmod 755 *.sh *.pl *.py
# Data files
chmod 664 *.csv *.json *.xml
# Log files (rotated)
chmod 644 *.log
Web Server Scenarios
Apache/Nginx Web Content
# Standard web directory structure
/var/www/
├── html/ (755) - Public web files
├── logs/ (755) - Web server logs
├── uploads/ (775) - User uploadable files
└── private/ (700) - Private web files
# Apply web permissions
find /var/www/html -type d -exec chmod 755 {} \;
find /var/www/html -type f -exec chmod 644 {} \;
find /var/www/logs -type d -exec chmod 750 {} \;
find /var/www/logs -type f -exec chmod 640 {} \;
find /var/www/uploads -type d -exec chmod 775 {} \;
find /var/www/uploads -type f -exec chmod 664 {} \;
find /var/www/private -type d -exec chmod 700 {} \;
find /var/www/private -type f -exec chmod 600 {} \;
Web Application Permissions
# WordPress/Drupal/CMS setup
chmod 755 /var/www/html/
chmod 644 /var/www/html/*.php
chmod 644 /var/www/html/*.html
chmod 600 /var/www/html/wp-config.php
chmod 755 /var/www/html/wp-content/
chmod 755 /var/www/html/wp-content/themes/
chmod 755 /var/www/html/wp-content/plugins/
chmod 664 /var/www/html/wp-content/themes/*/*.php
chmod 664 /var/www/html/wp-content/plugins/*/*.php
chmod 775 /var/www/html/wp-content/uploads/
Development Environment
# Development workspace
chmod 755 /home/developer/projects/
chmod 775 /home/developer/shared/
chmod 700 /home/developer/.ssh/
chmod 644 /home/developer/.bashrc
chmod 600 /home/developer/.netrc
# Git repository permissions
find /home/developer/git -type d -exec chmod 755 {} \;
find /home/developer/git -name "*.sh" -exec chmod 755 {} \;
find /home/developer/git -name "*.py" -exec chmod 644 {} \;
find /home/developer/git -name "*.md" -exec chmod 644 {} \;
Database Server Scenarios
MySQL/MariaDB Data Directory
# MySQL data directory permissions
chown -R mysql:mysql /var/lib/mysql/
find /var/lib/mysql -type d -exec chmod 750 {} \;
find /var/lib/mysql -type f -exec chmod 660 {} \;
chmod 660 /var/lib/mysql/mysql/user.MYD
chmod 660 /var/lib/mysql/mysql/user.MYI
chmod 644 /var/lib/mysql/mysql/user.frm
# MySQL configuration
chmod 644 /etc/my.cnf
chmod 644 /etc/mysql/my.cnf
PostgreSQL Permissions
# PostgreSQL data directory
chown -R postgres:postgres /var/lib/postgresql/
chmod 700 /var/lib/postgresql/12/main/
chmod 600 /var/lib/postgresql/12/main/postgresql.conf
chmod 600 /var/lib/postgresql/12/main/pg_hba.conf
# PostgreSQL binaries
chmod 755 /usr/lib/postgresql/12/bin/*
chmod 644 /etc/postgresql/12/main/*.conf
System Administration Scenarios
System Configuration Files
# System-wide configuration
chmod 644 /etc/hosts
chmod 644 /etc/fstab
chmod 600 /etc/ssh/sshd_config
chmod 600 /etc/shadow
chmod 644 /etc/passwd
chmod 600 /etc/gshadow
chmod 644 /etc/group
Log File Management
# System logs
chmod 640 /var/log/auth.log
chmod 640 /var/log/syslog
chmod 644 /var/log/dmesg
chmod 600 /var/log/utmp
chmod 600 /var/log/wtmp
# Application logs
chmod 644 /var/log/apache2/*.log
chmod 644 /var/log/nginx/*.log
chmod 660 /var/log/mysql/*.log
Backup and Archive Management
# Backup scripts
chmod 700 /usr/local/bin/backup_*.sh
chmod 600 /etc/backup.conf
chmod 755 /var/backups/
chmod 644 /var/backups/*.tar.gz
# Archive directories
find /var/backups -type d -exec chmod 755 {} \;
find /var/backups -name "*.tar.gz" -exec chmod 644 {} \;
find /var/backups -name "*.backup" -exec chmod 600 {} \;
Security Hardening Examples
User Home Directory Security
# Secure user home directories
chmod 750 /home/username
chmod 700 /home/username/.ssh
chmod 600 /home/username/.ssh/id_rsa
chmod 644 /home/username/.ssh/id_rsa.pub
chmod 600 /home/username/.ssh/authorized_keys
chmod 600 /home/username/.netrc
chmod 644 /home/username/.bashrc
chmod 600 /home/username/.bash_history
Application Security
# Secure application files
chmod 755 /opt/application/bin/app
chmod 600 /opt/application/config/app.conf
chmod 644 /opt/application/templates/*
chmod 775 /opt/application/data/
chmod 664 /opt/application/data/*.*
chmod 755 /opt/application/logs/
chmod 640 /opt/application/logs/*.log
Temporary File Security
# Secure temporary directories
chmod 1777 /tmp
chmod 1777 /var/tmp
chmod 770 /app/tmp
chmod 770 /var/app/cache
# Clean up permissions in temp directories
find /tmp -type f -exec chmod 644 {} \;
find /tmp -type f -name ".*" -exec chmod 600 {} \;
Basic Octal Mode
# Give read, write, execute to owner only
chmod 700 private_file
# Give read/write to owner, read to group and others
chmod 644 public_file.txt
# Give full permissions to everyone (not recommended)
chmod 777 shared_file
# Give read/write to owner and group, no access to others
chmod 660 team_file.txt
# Private directory with sticky bit
chmod 1770 /shared/private
# Executable with SUID for system utility
chmod 4755 /usr/local/bin/backup_tool
# Shared directory with SGID for team collaboration
chmod 2775 /home/developers/shared_project
Basic Symbolic Mode
# Add execute permission for owner
chmod u+x script.sh
# Remove write permission from group
chmod g-w file.txt
# Add read permission for others
chmod o+r public_file
# Add execute permission for all
chmod a+x script.sh
# Complex symbolic operations
chmod u=rwx,g=rx,o=r document.txt
chmod a-w,+rX sensitive_directory/
chmod go=u filename # Copy owner permissions to group and others
Multiple Permissions
# Remove write from group and others
chmod go-w important_file
# Set exact permissions (remove all others)
chmod u=rwx,go=rx script.sh
# Add read and write for owner, read for group
chmod u+rw,g+r document.txt
# Remove execute from all, add read for all
chmod a-xr+r regular_file
# Multiple comma-separated operations
chmod u+x,go-w,g+r script.sh
Directory Permissions
# Give full access to directory and its contents
chmod -R 755 /path/to/directory
# Make directory accessible to web server
chmod -R 755 /var/www/html
# Remove execute from files but keep it for directories
chmod -R a-x /path && chmod -R a+X /path
# Set SGID on all directories in project tree
find /project -type d -exec chmod g+s {} \;
# Complex directory permission setup
chmod 2775 /shared/team_project
chmod 1777 /shared/public_temp
chmod 2770 /shared/team_temp
Advanced Techniques
Reference Mode Operations
Copying Permissions Between Files
# Copy permissions from template to multiple files
chmod --reference=template.sh *.sh
chmod --reference=config.ini *.conf
# Copy directory permissions
chmod --reference=source_dir/ target_dir/
# Preserve permissions during copy
cp --reference=source_file.txt new_file.txt
chmod --reference=source_file.txt new_file.txt
# Restore permissions from backup
find /backup -name "*.orig" -exec sh -c '
orig="$1"
current="${orig%.orig}"
if [ -f "$current" ]; then
chmod --reference="$orig" "$current"
fi
' sh {} \;
Permission Templates
# Create permission templates
touch template_executable chmod 755 template_executable
touch template_config chmod 600 template_config
touch template_shared chmod 664 template_shared
touch template_private chmod 600 template_private
# Apply templates to files
find . -name "*.sh" -exec chmod --reference=template_executable {} \;
find . -name "*.conf" -exec chmod --reference=template_config {} \;
Complex Find and Chmod Operations
Advanced File Selection
# Fix permissions based on file type
find /var/www -type f -name "*.php" -exec chmod 644 {} \;
find /var/www -type f -name "*.sh" -exec chmod 755 {} \;
find /var/www -type f -executable -exec chmod 755 {} \;
# Permissions based on file size
find . -size +10M -exec chmod 600 {} \; # Large files private
find . -size -1k -exec chmod 644 {} \; # Small files readable
# Time-based permission changes
find . -mtime +30 -exec chmod 600 {} \; # Old files secure
find . -mtime -7 -exec chmod 664 {} \; # Recent files shared
Conditional Permission Logic
# Set execute only if file contains shebang
find . -type f -exec sh -c '
head -n1 "$1" | grep -q "^#!" && chmod +x "$1"
' sh {} \;
# Set permissions based on file content
find . -type f -exec sh -c '
if grep -q "private" "$1"; then
chmod 600 "$1"
elif grep -q "public" "$1"; then
chmod 644 "$1"
fi
' sh {} \;
# Recursive permissions with conditions
find /project -type d -exec sh -c '
if [ -f "$1/.private" ]; then
chmod 700 "$1"
else
chmod 755 "$1"
fi
' sh {} \;
Advanced Special Permission Management
Dynamic SUID Management
# Set SUID only on secure executables
find /usr/local/bin -type f -perm /111 -exec sh -c '
if [ "$(stat -c %U "$1")" = "root" ]; then
if grep -q "security_approved" "$1" 2>/dev/null; then
chmod u+s "$1"
echo "Added SUID to $1"
fi
fi
' sh {} \;
# Remove SUID from unauthorized files
find /tmp -perm -4000 -type f -exec chmod u-s {} \;
find /home -perm -4000 -type f ! -user root -exec chmod u-s {} \;
SGID for Project Management
# Setup project directory structure with SGID
setup_project() {
local project_path="$1"
local group="$2"
mkdir -p "$project_path"/{src,docs,tests,config,logs}
chown -R :"$group" "$project_path"
find "$project_path" -type d -exec chmod 2775 {} \;
find "$project_path" -type f -exec chmod 664 {} \;
}
# Usage
setup_project /home/projects/webapp developers
setup_project /home/projects/database dbadmins
Sticky Bit Security
# Monitor sticky bit directories
monitor_sticky_dirs() {
find / -perm -1000 -type d -exec sh -c '
echo "=== Checking $1 ==="
ls -la "$1" | head -10
find "$1" -type f -user root -exec ls -la {} \;
' sh {} \;
}
# Create secure shared temp directories
create_secure_temp() {
local dir="$1"
local group="$2"
mkdir -p "$dir"
chown :"$group" "$dir"
chmod 3770 "$dir" # SGID + sticky + group write
# Set up automatic cleanup
echo "0 2 * * * find $dir -type f -mtime +1 -delete" | crontab -
}
System Administration Scenarios
Web Server Security Hardening
Apache/Nginx Security Setup
#!/bin/bash
# harden_web_server.sh
WEB_ROOT="/var/www/html"
LOG_DIR="/var/log/nginx"
UPLOAD_DIR="/var/www/uploads"
# Secure web content
find "$WEB_ROOT" -type d -exec chmod 755 {} \;
find "$WEB_ROOT" -type f -exec chmod 644 {} \;
# Secure configuration files
chmod 600 /etc/nginx/nginx.conf
chmod 600 /etc/nginx/sites-available/*
chmod 644 /etc/nginx/sites-enabled/*
# Secure logs with restricted access
chmod 750 "$LOG_DIR"
find "$LOG_DIR" -type f -exec chmod 640 {} \;
chown :adm "$LOG_DIR"
# Secure upload directory with sticky bit
mkdir -p "$UPLOAD_DIR"
chown www-data:www-data "$UPLOAD_DIR"
chmod 1770 "$UPLOAD_DIR"
echo "Web server security hardening complete"
WordPress/CMS Security
# WordPress security hardening
wp_harden() {
local wp_path="$1"
# Core WordPress files
chmod 755 "$wp_path"
find "$wp_path" -type d -exec chmod 755 {} \;
find "$wp_path" -type f -exec chmod 644 {} \;
# Secure wp-config.php
chmod 600 "$wp_path/wp-config.php"
# Plugin and theme directories (readable, not writable)
chmod 755 "$wp_path/wp-content/plugins"
chmod 755 "$wp_path/wp-content/themes"
find "$wp_path/wp-content/plugins" -type f -exec chmod 644 {} \;
find "$wp_path/wp-content/themes" -type f -exec chmod 644 {} \;
# Uploads directory (writable by web server)
chmod 755 "$wp_path/wp-content/uploads"
chown www-data:www-data "$wp_path/wp-content/uploads"
find "$wp_path/wp-content/uploads" -type d -exec chmod 755 {} \;
find "$wp_path/wp-content/uploads" -type f -exec chmod 644 {} \;
}
Database Server Configuration
MySQL/MariaDB Security
# MySQL directory security setup
mysql_secure_dirs() {
local mysql_data="/var/lib/mysql"
local mysql_conf="/etc/mysql"
# Data directory - restricted access
chown -R mysql:mysql "$mysql_data"
find "$mysql_data" -type d -exec chmod 750 {} \;
find "$mysql_data" -type f -exec chmod 660 {} \;
# Configuration files
chmod 644 "$mysql_conf/my.cnf"
chmod 600 "$mysql_conf/debian.cnf"
# Log files
find /var/log/mysql -type f -exec chmod 660 {} \;
chown mysql:adm /var/log/mysql
}
# PostgreSQL security setup
postgres_secure_dirs() {
local pg_data="/var/lib/postgresql"
local pg_conf="/etc/postgresql"
# Data directory - very restrictive
chown -R postgres:postgres "$pg_data"
chmod 700 "$pg_data"
find "$pg_data" -type d -exec chmod 700 {} \;
find "$pg_data" -type f -exec chmod 600 {} \;
# Configuration files
find "$pg_conf" -name "*.conf" -exec chmod 644 {} \;
find "$pg_conf" -name "pg_hba.conf" -exec chmod 600 {} \;
# Socket directory
chmod 2775 /var/run/postgresql
chown postgres:postgres /var/run/postgresql
}
Development Environment Setup
Development Project Structure
# Setup development environment with proper permissions
setup_dev_project() {
local project_name="$1"
local user="$2"
local group="$3"
local project_dir="/home/$user/projects/$project_name"
mkdir -p "$project_dir"/{src,tests,docs,config,logs,temp}
chown -R "$user":"$group" "$project_dir"
# Source code (group writable for collaboration)
find "$project_dir/src" -type d -exec chmod 2775 {} \;
find "$project_dir/src" -type f -exec chmod 664 {} \;
# Tests (readable by all, writable by group)
find "$project_dir/tests" -type d -exec chmod 775 {} \;
find "$project_dir/tests" -type f -exec chmod 664 {} \;
# Documentation (readable by all)
find "$project_dir/docs" -type d -exec chmod 755 {} \;
find "$project_dir/docs" -type f -exec chmod 644 {} \;
# Configuration (secure)
find "$project_dir/config" -type d -exec chmod 750 {} \;
find "$project_dir/config" -type f -exec chmod 640 {} \;
# Logs (writable by application)
mkdir -p "$project_dir/logs/app"
chmod 775 "$project_dir/logs"
find "$project_dir/logs" -type d -exec chmod 775 {} \;
find "$project_dir/logs" -type f -exec chmod 664 {} \;
# Temporary files (with sticky bit)
chmod 1777 "$project_dir/temp"
}
# Git repository setup
setup_git_repo() {
local repo_path="$1"
local group="$2"
# Initialize with proper permissions
git init --bare "$repo_path"
chown -R :"$group" "$repo_path"
find "$repo_path" -type d -exec chmod 2775 {} \;
find "$repo_path" -type f -exec chmod 664 {} \;
# Git post-receive hook
cat > "$repo_path/hooks/post-receive" << 'EOF'
#!/bin/bash
git checkout -f
EOF
chmod +x "$repo_path/hooks/post-receive"
}
Scripting and Automation
Permission Management Scripts
Comprehensive Permission Fix Script
#!/bin/bash
# fix_permissions.sh - Comprehensive permission fix script
LOG_FILE="/var/log/permission_fix.log"
DATE=$(date +%Y-%m-%d_%H:%M:%S)
log_message() {
echo "[$DATE] $1" | tee -a "$LOG_FILE"
}
fix_web_permissions() {
local web_root="$1"
local user="${2:-www-data}"
local group="${3:-www-data}"
log_message "Fixing web permissions for $web_root"
# Directories
find "$web_root" -type d -exec chmod 755 {} \;
log_message "Set directory permissions to 755"
# Regular files
find "$web_root" -type f -exec chmod 644 {} \;
log_message "Set file permissions to 644"
# Executable scripts
find "$web_root" -name "*.sh" -o -name "*.pl" -o -name "*.py" | \
xargs chmod 755
log_message "Set executable permissions for scripts"
# Configuration files (more restrictive)
find "$web_root" -name "*.conf" -o -name "*.ini" -o -name "*.cfg" | \
xargs chmod 600
log_message "Secured configuration files"
# Upload directories with sticky bit
find "$web_root" -type d -name "upload*" -o -name "temp*" | \
xargs chmod 1770
log_message "Set sticky bit on upload directories"
# Set ownership
chown -R "$user":"$group" "$web_root"
log_message "Set ownership to $user:$group"
}
fix_home_directory() {
local home_dir="$1"
log_message "Fixing home directory permissions for $home_dir"
# Home directory itself
chmod 750 "$home_dir"
# SSH directory
if [ -d "$home_dir/.ssh" ]; then
chmod 700 "$home_dir/.ssh"
find "$home_dir/.ssh" -type f -exec chmod 600 {} \;
find "$home_dir/.ssh" -type f -name "*.pub" -exec chmod 644 {} \;
fi
# Configuration files
find "$home_dir" -maxdepth 1 -name ".*rc" -o -name ".*profile" | \
xargs chmod 644
log_message "Home directory permissions fixed"
}
audit_permissions() {
local scan_path="${1:-/}"
log_message "Starting permission audit for $scan_path"
# Find SUID files
log_message "=== SUID Files ==="
find "$scan_path" -perm -4000 -type f -ls 2>/dev/null | \
head -20 | tee -a "$LOG_FILE"
# Find SGID files
log_message "=== SGID Files ==="
find "$scan_path" -perm -2000 -type f -ls 2>/dev/null | \
head -20 | tee -a "$LOG_FILE"
# Find world-writable files
log_message "=== World-Writable Files ==="
find "$scan_path" -perm -002 -type f -ls 2>/dev/null | \
head -20 | tee -a "$LOG_FILE"
# Find files with no permissions
log_message "=== Files with No Permissions ==="
find "$scan_path" -perm -000 -type f -ls 2>/dev/null | \
head -10 | tee -a "$LOG_FILE"
}
# Main execution
case "$1" in
web)
fix_web_permissions "$2" "$3" "$4"
;;
home)
fix_home_directory "$2"
;;
audit)
audit_permissions "$2"
;;
*)
echo "Usage: $0 {web|home|audit} [arguments]"
echo " web <path> [user] [group] - Fix web server permissions"
echo " home <path> - Fix home directory permissions"
echo " audit [path] - Audit permissions"
exit 1
;;
esac
Automated Permission Monitor
#!/bin/bash
# permission_monitor.sh - Monitor and alert on permission changes
CONFIG_FILE="/etc/permission_monitor.conf"
ALERT_EMAIL="admin@example.com"
LOG_FILE="/var/log/permission_monitor.log"
# Default configuration
SCAN_PATHS="/etc /var/www /home /usr/local/bin"
SUID_WHITELIST="/usr/bin/passwd /usr/bin/sudo /usr/bin/su"
ALERT_ON_SUID=true
ALERT_ON_WORLD_WRITE=true
load_config() {
if [ -f "$CONFIG_FILE" ]; then
source "$CONFIG_FILE"
fi
}
check_suid_files() {
local suid_files=$(find / -perm -4000 -type f 2>/dev/null)
for file in $suid_files; do
if ! echo "$SUID_WHITELIST" | grep -q "$file"; then
echo "ALERT: Unexpected SUID file found: $file" | \
mail -s "SUID File Alert" "$ALERT_EMAIL"
echo "[$(date)] Unexpected SUID: $file" >> "$LOG_FILE"
fi
done
}
check_world_writable() {
local writable_files=$(find $SCAN_PATHS -perm -002 -type f 2>/dev/null)
if [ -n "$writable_files" ]; then
echo "ALERT: World-writable files found:" | \
mail -s "World-Writable Files Alert" "$ALERT_EMAIL"
echo "$writable_files" | mail -s "World-Writable Files List" "$ALERT_EMAIL"
echo "[$(date)] World-writable files detected" >> "$LOG_FILE"
fi
}
check_directory_permissions() {
local issues=0
# Check for directories without execute permission
find $SCAN_PATHS -type d ! -perm /111 2>/dev/null | \
while read dir; do
echo "WARNING: Directory without execute permission: $dir" >> "$LOG_FILE"
issues=$((issues + 1))
done
if [ $issues -gt 0 ]; then
echo "Found $issues directory permission issues" | \
mail -s "Directory Permission Issues" "$ALERT_EMAIL"
fi
}
generate_report() {
echo "=== Permission Monitor Report - $(date) ===" > "/tmp/perm_report.txt"
echo "Scan paths: $SCAN_PATHS" >> "/tmp/perm_report.txt"
echo "" >> "/tmp/perm_report.txt"
echo "=== SUID Files ===" >> "/tmp/perm_report.txt"
find / -perm -4000 -type f -ls 2>/dev/null >> "/tmp/perm_report.txt"
echo "" >> "/tmp/perm_report.txt"
echo "=== SGID Files ===" >> "/tmp/perm_report.txt"
find / -perm -2000 -type f -ls 2>/dev/null >> "/tmp/perm_report.txt"
echo "" >> "/tmp/perm_report.txt"
echo "=== World-Writable Files ===" >> "/tmp/perm_report.txt"
find $SCAN_PATHS -perm -002 -type f -ls 2>/dev/null >> "/tmp/perm_report.txt"
cat "/tmp/perm_report.txt" | mail -s "Permission Monitor Report" "$ALERT_EMAIL"
}
# Main monitoring loop
load_config
case "$1" in
check)
[ "$ALERT_ON_SUID" = true ] && check_suid_files
[ "$ALERT_ON_WORLD_WRITE" = true ] && check_world_writable
check_directory_permissions
;;
report)
generate_report
;;
*)
echo "Usage: $0 {check|report}"
exit 1
;;
esac
Security Considerations
Common Security Vulnerabilities
SUID/SGID Security Risks
# Dangerous SUID files to watch for
DANGEROUS_SUID_PATTERNS=(
"/tmp"
"/home"
"/var/tmp"
"*.sh"
"nc"
"ncat"
"tcpdump"
"vi"
"vim"
"nano"
)
# Scan for potentially dangerous SUID files
scan_dangerous_suid() {
for pattern in "${DANGEROUS_SUID_PATTERNS[@]}"; do
find / -perm -4000 -path "*$pattern*" -type f -ls 2>/dev/null
done
}
# Remove unauthorized SUID bits
cleanup_unauthorized_suid() {
local authorized_suids="/usr/bin/passwd /usr/bin/sudo /usr/bin/su /usr/bin/gpasswd"
find / -perm -4000 -type f | while read file; do
if ! echo "$authorized_suids" | grep -q "$file"; then
echo "Removing SUID from: $file"
chmod u-s "$file"
fi
done
}
World-Writable File Security
# Find and fix world-writable files in sensitive locations
fix_world_writable() {
local sensitive_dirs="/etc /bin /sbin /usr/bin /usr/sbin /lib"
for dir in $sensitive_dirs; do
if [ -d "$dir" ]; then
find "$dir" -perm -002 -type f -exec chmod o-w {} \;
echo "Removed world-write permission from files in $dir"
fi
done
}
# Monitor for new world-writable files
monitor_world_writable() {
local watch_dirs="/etc /home /var/www /tmp"
local alert_file="/var/log/world_writable_alerts.log"
for dir in $watch_dirs; do
find "$dir" -perm -002 -type f -newer /tmp/last_check | \
while read file; do
echo "$(date): World-writable file detected: $file" >> "$alert_file"
# You could add email alerting here
done
done
touch /tmp/last_check
}
Permission Auditing Scripts
Comprehensive Security Audit
#!/bin/bash
# security_audit.sh - Comprehensive permission security audit
REPORT_FILE="/var/log/security_audit_$(date +%Y%m%d_%H%M%S).log"
audit_header() {
echo "=== Linux Permission Security Audit ===" > "$REPORT_FILE"
echo "Date: $(date)" >> "$REPORT_FILE"
echo "System: $(hostname)" >> "$REPORT_FILE"
echo "Kernel: $(uname -r)" >> "$REPORT_FILE"
echo "======================================" >> "$REPORT_FILE"
echo "" >> "$REPORT_FILE"
}
audit_suid_files() {
echo "=== SUID Files Audit ===" >> "$REPORT_FILE"
find / -perm -4000 -type f -ls 2>/dev/null >> "$REPORT_FILE"
echo "" >> "$REPORT_FILE"
}
audit_sgid_files() {
echo "=== SGID Files Audit ===" >> "$REPORT_FILE"
find / -perm -2000 -type f -ls 2>/dev/null >> "$REPORT_FILE"
echo "" >> "$REPORT_FILE"
}
audit_world_writable() {
echo "=== World-Writable Files Audit ===" >> "$REPORT_FILE"
find / -perm -002 -type f -ls 2>/dev/null | head -50 >> "$REPORT_FILE"
echo "" >> "$REPORT_FILE"
}
audit_unowned_files() {
echo "=== Unowned Files Audit ===" >> "$REPORT_FILE"
find / -nouser -o -nogroup -ls 2>/dev/null >> "$REPORT_FILE"
echo "" >> "$REPORT_FILE"
}
audit_home_directories() {
echo "=== Home Directory Permissions Audit ===" >> "$REPORT_FILE"
find /home -maxdepth 1 -type d -exec ls -ld {} \; >> "$REPORT_FILE"
find /home -name ".ssh" -type d -exec ls -la {} \; >> "$REPORT_FILE"
echo "" >> "$REPORT_FILE"
}
audit_critical_configs() {
echo "=== Critical Configuration Files ===" >> "$REPORT_FILE"
local critical_files=(
"/etc/passwd"
"/etc/shadow"
"/etc/group"
"/etc/gshadow"
"/etc/ssh/sshd_config"
"/etc/sudoers"
"/etc/crontab"
)
for file in "${critical_files[@]}"; do
if [ -f "$file" ]; then
echo "$file: $(stat -c "%a %U:%G" "$file")" >> "$REPORT_FILE"
fi
done
echo "" >> "$REPORT_FILE"
}
audit_directory_permissions() {
echo "=== Directory Permission Issues ===" >> "$REPORT_FILE"
# Directories without execute permission
find / -type d ! -perm /111 2>/dev/null | head -20 >> "$REPORT_FILE"
# Directories with sticky bit
find / -perm -1000 -type d 2>/dev/null | head -20 >> "$REPORT_FILE"
echo "" >> "$REPORT_FILE"
}
generate_summary() {
echo "=== Audit Summary ===" >> "$REPORT_FILE"
local suid_count=$(find / -perm -4000 -type f 2>/dev/null | wc -l)
local sgid_count=$(find / -perm -2000 -type f 2>/dev/null | wc -l)
local world_write_count=$(find / -perm -002 -type f 2>/dev/null | wc -l)
local unowned_count=$(find / -nouser -o -nogroup 2>/dev/null | wc -l)
echo "SUID files: $suid_count" >> "$REPORT_FILE"
echo "SGID files: $sgid_count" >> "$REPORT_FILE"
echo "World-writable files: $world_write_count" >> "$REPORT_FILE"
echo "Unowned files: $unowned_count" >> "$REPORT_FILE"
if [ $world_write_count -gt 10 ] || [ $unowned_count -gt 0 ]; then
echo "*** SECURITY ISSUES DETECTED ***" >> "$REPORT_FILE"
fi
}
# Run complete audit
audit_header
audit_suid_files
audit_sgid_files
audit_world_writable
audit_unowned_files
audit_home_directories
audit_critical_configs
audit_directory_permissions
generate_summary
echo "Security audit complete. Report saved to: $REPORT_FILE"
# Send email if issues detected
if grep -q "SECURITY ISSUES" "$REPORT_FILE"; then
mail -s "Security Issues Detected on $(hostname)" admin@example.com < "$REPORT_FILE"
fi
Troubleshooting Guide
Common Permission Problems
Permission Denied Errors
# Problem: Cannot access file or directory
# Solution: Check and fix permissions
diagnose_permission_denied() {
local path="$1"
local user="$2"
echo "Diagnosing permission issues for $path as user $user"
# Check if path exists
if [ ! -e "$path" ]; then
echo "Path does not exist: $path"
return 1
fi
# Check current permissions
echo "Current permissions: $(stat -c "%A %U:%G" "$path")"
echo "Octal permissions: $(stat -c "%a" "$path")"
# Check if user can access
if [ "$user" = "$(whoami)" ]; then
# Test read access
if [ -r "$path" ]; then
echo "✓ User can read $path"
else
echo "✗ User cannot read $path"
fi
# Test write access
if [ -w "$path" ]; then
echo "✓ User can write to $path"
else
echo "✗ User cannot write to $path"
fi
# Test execute access
if [ -x "$path" ]; then
echo "✓ User can execute $path"
else
echo "✗ User cannot execute $path"
fi
fi
# For directories, check parent permissions
if [ -d "$path" ]; then
local parent=$(dirname "$path")
echo "Parent directory permissions: $(stat -c "%A %U:%G" "$parent")"
fi
}
# Fix common permission issues
fix_permission_issues() {
local path="$1"
local issue_type="$2"
case "$issue_type" in
"web_content")
find "$path" -type d -exec chmod 755 {} \;
find "$path" -type f -exec chmod 644 {} \;
;;
"executable")
chmod +x "$path"
;;
"config_file")
chmod 600 "$path"
;;
"shared_directory")
chmod 775 "$path"
;;
*)
echo "Unknown issue type: $issue_type"
return 1
;;
esac
}
Directory Access Issues
# Problem: Cannot list directory contents
# Solution: Ensure directory has execute permissions
fix_directory_access() {
local dir_path="$1"
echo "Fixing directory access for $dir_path"
# Check if directory exists
if [ ! -d "$dir_path" ]; then
echo "Directory does not exist: $dir_path"
return 1
fi
# Add execute permission for user, group, and others as needed
if [ -r "$dir_path" ]; then
chmod u+x "$dir_path" # Owner can traverse
fi
if [ -w "$dir_path" ]; then
chmod g+x "$dir_path" # Group can traverse if writable
fi
# Ensure all directories in path are traversable
local current_path="$dir_path"
while [ "$current_path" != "/" ]; do
if [ ! -x "$current_path" ]; then
echo "Adding execute permission to $current_path"
chmod u+x "$current_path"
fi
current_path=$(dirname "$current_path")
done
}
# Diagnose path traversal issues
diagnose_path_traversal() {
local target_path="$1"
echo "Diagnosing path traversal to $target_path"
local current_path="/"
local remaining_path="${target_path#/}"
IFS='/' read -ra path_components <<< "$remaining_path"
for component in "${path_components[@]}"; do
if [ -z "$component" ]; then
continue
fi
current_path="$current_path$component"
echo "Checking: $current_path"
if [ ! -e "$current_path" ]; then
echo "✗ Path does not exist: $current_path"
return 1
fi
if [ ! -x "$current_path" ]; then
echo "✗ Cannot traverse: $current_path"
else
echo "✓ Can traverse: $current_path"
fi
current_path="$current_path/"
done
}
Script Execution Problems
# Problem: Script won't execute
# Solution: Check multiple permission and file format issues
fix_script_execution() {
local script_path="$1"
echo "Fixing script execution for $script_path"
# Check if file exists
if [ ! -f "$script_path" ]; then
echo "Script does not exist: $script_path"
return 1
fi
# Add execute permission
chmod +x "$script_path"
echo "Added execute permission to $script_path"
# Check shebang line
if [ -r "$script_path" ]; then
local first_line=$(head -n1 "$script_path")
if [[ ! "$first_line" =~ ^#! ]]; then
echo "Warning: No shebang line found in $script_path"
else
echo "Shebang: $first_line"
# Check if interpreter exists
local interpreter=$(echo "$first_line" | cut -d' ' -f1 | sed 's/^#!//')
if [ ! -x "$interpreter" ]; then
echo "Warning: Interpreter not found or not executable: $interpreter"
fi
fi
fi
# Check file encoding
file "$script_path"
# Check for Windows line endings
if grep -q $'\r' "$script_path"; then
echo "Warning: Windows line endings detected"
echo "Converting to Unix format..."
dos2unix "$script_path" 2>/dev/null || \
sed -i 's/\r$//' "$script_path"
fi
}
# Test script execution
test_script_execution() {
local script_path="$1"
echo "Testing script execution for $script_path"
# Check permissions
if [ -x "$script_path" ]; then
echo "✓ Script is executable"
else
echo "✗ Script is not executable"
chmod +x "$script_path"
echo "✓ Made script executable"
fi
# Try to execute with error checking
if "$script_path" --help >/dev/null 2>&1; then
echo "✓ Script executes successfully"
else
echo "✗ Script execution failed"
echo "Exit code: $?"
echo "Error output:"
"$script_path" 2>&1 | head -5
fi
}
Integration with Other Tools
Permission Backup and Restore
Using ACL for Advanced Permissions
# Backup permissions using ACL
backup_permissions() {
local source_dir="$1"
local backup_file="$2"
echo "Backing up permissions for $source_dir to $backup_file"
# Create backup directory structure
getfacl -R "$source_dir" > "$backup_file.acl"
# Create file list with permissions
find "$source_dir" -exec stat -c "%a %n" {} \; > "$backup_file.perms"
# Create ownership information
find "$source_dir" -exec stat -c "%U %G %n" {} \; > "$backup_file.owners"
echo "Permission backup completed"
}
# Restore permissions from backup
restore_permissions() {
local target_dir="$1"
local backup_base="$2"
echo "Restoring permissions to $target_dir from $backup_base"
# Restore permissions
if [ -f "$backup_base.perms" ]; then
while read perms file; do
if [ -f "$target_dir$file" ] || [ -d "$target_dir$file" ]; then
chmod "$perms" "$target_dir$file"
fi
done < "$backup_base.perms"
fi
# Restore ownership
if [ -f "$backup_base.owners" ]; then
while read user group file; do
if [ -e "$target_dir$file" ]; then
chown "$user":"$group" "$target_dir$file"
fi
done < "$backup_base.owners"
fi
# Restore ACLs
if [ -f "$backup_base.acl" ]; then
setfacl --restore="$backup_base.acl"
fi
echo "Permission restoration completed"
}
Permission Synchronization
# Synchronize permissions between directories
sync_permissions() {
local source_dir="$1"
local target_dir="$2"
echo "Synchronizing permissions from $source_dir to $target_dir"
# Create mapping of relative paths
find "$source_dir" -type f -o -type d | while read source_file; do
# Calculate relative path
rel_path="${source_file#$source_dir}"
target_file="$target_dir$rel_path"
if [ -e "$target_file" ]; then
# Copy permissions
chmod --reference="$source_file" "$target_file"
# Copy ownership
owner=$(stat -c "%U" "$source_file")
group=$(stat -c "%G" "$source_file")
chown "$owner":"$group" "$target_file"
echo "Synced: $rel_path"
fi
done
}
# Real-time permission monitoring and fixing
monitor_and_fix_permissions() {
local watch_dir="$1"
local reference_file="$2"
local check_interval="${3:-60}"
echo "Monitoring permissions in $watch_dir (reference: $reference_file)"
while true; do
# Check for files with wrong permissions
find "$watch_dir" -type f ! -perm $(stat -c "%a" "$reference_file") | \
while read file; do
echo "Fixing permissions for: $file"
chmod --reference="$reference_file" "$file"
done
sleep "$check_interval"
done
}
Integration with Version Control
Git Permission Management
# Setup Git repository with proper permissions
setup_git_permissions() {
local repo_path="$1"
local shared_group="$2"
# Create repository with shared group
mkdir -p "$repo_path"
cd "$repo_path"
git init --shared=group
# Set proper ownership and permissions
chown -R :"$shared_group" .
find . -type d -exec chmod 2775 {} \;
find . -type f -exec chmod 664 {} \;
# Ensure hooks are executable
find .git/hooks -type f -exec chmod 755 {} \;
}
# Fix permissions after Git operations
fix_git_permissions() {
local repo_path="$1"
local group="$2"
cd "$repo_path"
# Fix directory permissions
find . -type d -exec chmod 2775 {} \;
chgrp -R "$group" .
# Fix file permissions
find . -type f -exec chmod 664 {} \;
# Make sure scripts are executable
find . -name "*.sh" -exec chmod 775 {} \;
# Fix git objects (should be group-writable)
find .git/objects -type f -exec chmod 664 {} \;
find .git/objects -type d -exec chmod 2775 {} \;
}
# Git post-receive hook for permission fixing
install_git_post_receive_hook() {
local repo_path="$1"
local group="$2"
cat > "$repo_path/hooks/post-receive" << EOF
#!/bin/bash
# Git post-receive hook to fix permissions
# Determine the repository path
GIT_DIR=\$(pwd)
WORK_TREE=\${GIT_DIR/\.git/}
# Checkout files
git checkout -f
# Fix permissions
find "\$WORK_TREE" -type d -exec chmod 2775 {} \;
find "\$WORK_TREE" -type f -exec chmod 664 {} \;
chgrp -R "$group" "\$WORK_TREE"
# Make scripts executable
find "\$WORK_TREE" -name "*.sh" -exec chmod 775 {} \;
echo "Permissions fixed for deployment"
EOF
chmod +x "$repo_path/hooks/post-receive"
chgrp "$group" "$repo_path/hooks/post-receive"
}
Performance Optimization
Large-Scale Permission Operations
Efficient Bulk Permission Changes
# Optimized permission fixing for large directory trees
optimize_permission_fix() {
local target_dir="$1"
local max_processes="${2:-8}"
echo "Optimizing permission fix for $target_dir (using $max_processes processes)"
# Use xargs with parallel processing
find "$target_dir" -type d -print0 | \
xargs -0 -P "$max_processes" -I {} chmod 755 {}
find "$target_dir" -type f -print0 | \
xargs -0 -P "$max_processes" -I {} chmod 644 {}
# Fix specific file types
find "$target_dir" -name "*.sh" -print0 | \
xargs -0 -P "$max_processes" -I {} chmod 755 {}
echo "Bulk permission fix completed"
}
# Memory-efficient permission scanning
memory_efficient_scan() {
local scan_path="$1"
local report_file="$2"
echo "Memory-efficient permission scan of $scan_path"
# Process directories one at a time to avoid memory issues
find "$scan_path" -type d -print0 | \
while IFS= read -r -d '' dir; do
echo "Processing directory: $dir"
# Check for permission issues in this directory only
find "$dir" -maxdepth 1 -type f ! -perm 644 -ls >> "$report_file"
find "$dir" -maxdepth 1 -type d ! -perm 755 -ls >> "$report_file"
done
}
# Progressive permission fixing (handles very large filesystems)
progressive_fix() {
local target_dir="$1"
local batch_size="${2:-1000}"
echo "Progressive permission fix for $target_dir (batch size: $batch_size)"
local count=0
find "$target_dir" -type f -print0 | \
while IFS= read -r -d '' file; do
chmod 644 "$file"
count=$((count + 1))
if [ $((count % batch_size)) -eq 0 ]; then
echo "Processed $count files..."
fi
done
echo "Progressive fix completed. Total files: $count"
}
Permission Change Monitoring
# Monitor permission changes in real-time
monitor_permission_changes() {
local watch_path="$1"
local log_file="${2:-/tmp/perm_changes.log}"
echo "Monitoring permission changes in $watch_path"
echo "Log file: $log_file"
# Use inotifywait if available (more efficient)
if command -v inotifywait >/dev/null 2>&1; then
inotifywait -m -r -e attrib "$watch_path" --format '%w%f %e' | \
while read file event; do
echo "$(date): Permission changed on $file ($event)" >> "$log_file"
# Get current permissions
current_perms=$(stat -c "%a" "$file" 2>/dev/null || echo "N/A")
echo "Current permissions: $current_perms" >> "$log_file"
done
else
# Fallback to periodic checking
echo "inotifywait not available, using periodic checking"
while true; do
# Create permission snapshot
find "$watch_path" -exec stat -c "%a %n" {} \; > "/tmp/current_perms.$$"
# Compare with previous snapshot
if [ -f "/tmp/prev_perms.$$" ]; then
if ! diff "/tmp/prev_perms.$$" "/tmp/current_perms.$$" >/dev/null; then
echo "$(date): Permission changes detected" >> "$log_file"
diff "/tmp/prev_perms.$$" "/tmp/current_perms.$$" >> "$log_file"
fi
fi
mv "/tmp/current_perms.$$" "/tmp/prev_perms.$$"
sleep 60
done
fi
}
Related Commands
Core Permission Commands
chown: Change file ownership and groupchgrp: Change group ownershipls: List directory contents with permissionsstat: Display detailed file status and permissionsfind: Find files with specific permissions
Advanced Permission Commands
getfacl: Get file ACL informationsetfacl: Set file ACLsumask: Set default file creation maskaccess: Check user's permissions for a filegetfattr: Get extended file attributes
Security and Monitoring
sudo: Execute commands with different user privilegessu: Switch user identityauditd: Linux auditing daemon for monitoring system callsausearch: Search audit logs for permission-related events
Best Practices
Security Best Practices
-
Principle of Least Privilege
- Grant only necessary permissions to users and processes
- Start with restrictive permissions (600/700) and add only what's needed
- Regularly review and audit permissions
-
Secure System Files
# Critical system files should be secure
chmod 600 /etc/shadow
chmod 644 /etc/passwd
chmod 600 /etc/ssh/sshd_config
chmod 644 /etc/hosts -
User Home Directory Security
# Secure user home directories
chmod 750 /home/username
chmod 700 /home/username/.ssh
chmod 600 /home/username/.ssh/id_rsa
chmod 644 /home/username/.ssh/id_rsa.pub
Operational Best Practices
-
Use Symbolic Notation for Clarity
chmod go-w fileis clearer thanchmod 644 filechmod u+x script.shclearly shows what's changing- Use symbolic notation for complex or conditional changes
-
Careful Recursive Operations
# Always check before recursive operations
find /target/directory -type f | wc -l # Count files first
chmod -R --verbose 755 /target/directory # See what's happening
chmod --preserve-root -R 755 /directory # Protect root filesystem -
Directory vs File Permissions
- Directories need execute permission to be accessible
- Use
755for most directories,700for private directories - Use
X(capital X) to set execute only on directories
Application-Specific Practices
-
Web Server Permissions
# Standard web content
find /var/www -type d -exec chmod 755 {} \;
find /var/www -type f -exec chmod 644 {} \;
# Upload directories with sticky bit
chmod 1777 /var/www/uploads
# Configuration files (restricted)
chmod 600 /var/www/config/*.conf -
Application Deployment
# Development environment
chmod 775 /app/src # Group writable for development
chmod 644 /app/src/*.php # Source code readable
# Production environment
chmod 755 /app/src # Not group writable in production
chmod 644 /app/src/*.php
chmod 600 /app/config/production.conf
Monitoring and Maintenance
-
Regular Permission Audits
# Daily security audit
find / -perm -4000 -type f > /var/log/suid_files.log
find / -perm -002 -type f > /var/log/world_writable.log
find / -nouser -o -nogroup > /var/log/unowned_files.log -
Automated Permission Fixing
# Cron job for regular permission maintenance
0 2 * * * /usr/local/bin/fix_permissions.sh /var/www
0 3 * * 0 /usr/local/bin/security_audit.sh -
Documentation and Change Management
- Document all permission changes
- Use version control for permission scripts
- Maintain change logs for critical system files
- Test permission changes in staging first
The chmod command is fundamental to Linux security and file management. Understanding both octal and symbolic notation, special permissions, and best practices is essential for effective system administration and maintaining proper security on Linux systems. This comprehensive guide covers everything from basic usage to advanced techniques for enterprise environments.