Users, groups, and permissions
In this series (15 parts)
- What is Linux and how it differs from other OSes
- Installing Linux and setting up your environment
- The Linux filesystem explained
- Users, groups, and permissions
- Essential command line tools
- Shell scripting fundamentals
- Processes and job control
- Standard I/O, pipes, and redirection
- The Linux networking stack
- Package management and software installation
- Disk management and filesystems
- Logs and system monitoring
- SSH and remote access
- Cron jobs and task scheduling
- Linux security basics for sysadmins
Every file in Linux has an owner, a group, and a set of permissions that determine who can do what with it. This is the foundation of Linux security. A misconfigured permission is one of the most common causes of both “I can’t access my file” frustrations and privilege escalation vulnerabilities.
Prerequisites
You should understand the Linux filesystem and how files are organized before diving into permissions.
Users and groups
Every user on a Linux system has:
- A username (like
pratik) - A UID (user ID, a number like
1000) - A primary group (usually the same name as the username)
- A GID (group ID for the primary group)
- A home directory (usually
/home/username) - A login shell (usually
/bin/bash)
This information is stored in /etc/passwd:
cat /etc/passwd | grep pratik
Output:
pratik:x:1000:1000:Pratik Tiwari:/home/pratik:/bin/bash
The fields are separated by colons:
username:password:UID:GID:comment:home:shell
The x in the password field means the actual password hash is stored in /etc/shadow, which only root can read:
sudo cat /etc/shadow | grep pratik
Output:
pratik:$6$abc123...xyz:19890:0:99999:7:::
The hash starts with $6$ which means SHA-512. This is the hashed and salted version of the password.
Managing users
# Create a new user
sudo useradd -m -s /bin/bash newuser
# Set their password
sudo passwd newuser
# Add user to a group
sudo usermod -aG docker newuser
# Delete a user and their home directory
sudo userdel -r newuser
Groups
Groups let you give the same permissions to multiple users. Every user has a primary group and can belong to additional (supplementary) groups.
# See your groups
groups
Output:
pratik adm cdrom sudo docker
# See all groups on the system
cat /etc/group | head -10
Output:
root:x:0:
daemon:x:1:
bin:x:2:
sys:x:3:
adm:x:4:pratik
sudo:x:27:pratik
docker:x:999:pratik
# Create a new group
sudo groupadd developers
# Add a user to the group
sudo usermod -aG developers pratik
The permission model
Every file has three sets of permissions for three categories of users:
graph LR F[File] --> O["Owner (u)"] F --> G["Group (g)"] F --> A["Others (o)"] O --> R1[Read] O --> W1[Write] O --> X1[Execute] G --> R2[Read] G --> W2[Write] G --> X2[Execute] A --> R3[Read] A --> W3[Write] A --> X3[Execute] style F fill:#f9a825,stroke:#f57f17,color:#000
| Permission | On a file | On a directory |
|---|---|---|
| Read (r) | Can view file contents | Can list directory contents |
| Write (w) | Can modify file contents | Can create/delete files in directory |
| Execute (x) | Can run the file as a program | Can enter the directory (cd) |
The ls -l output shows permissions as a 10-character string:
ls -l /etc/hostname
Output:
-rw-r--r-- 1 root root 7 Jun 15 10:00 /etc/hostname
Breaking down -rw-r--r--:
- rw- r-- r--
| | | |
| | | +-- others: read only
| | +-- group: read only
| +-- owner: read + write
+-- file type (- = regular file)
Octal notation
Each permission set (rwx) maps to a 3-bit number:
| Permission | Binary | Octal |
|---|---|---|
| --- | 000 | 0 |
| —x | 001 | 1 |
| -w- | 010 | 2 |
| -wx | 011 | 3 |
| r— | 100 | 4 |
| r-x | 101 | 5 |
| rw- | 110 | 6 |
| rwx | 111 | 7 |
So rw-r--r-- = 644 (6 for owner, 4 for group, 4 for others).
Common permission values:
| Octal | Meaning | Use case |
|---|---|---|
| 644 | Owner reads/writes, everyone reads | Config files, documents |
| 755 | Owner full, everyone reads/executes | Scripts, programs, directories |
| 700 | Owner only | Private files, SSH keys |
| 600 | Owner reads/writes only | Private config files |
| 777 | Everyone full access | ⚠ Almost never appropriate |
Changing permissions with chmod
Symbolic notation
# Add execute for the owner
chmod u+x script.sh
# Remove write for group and others
chmod go-w file.txt
# Set exact permissions: owner rwx, group rx, others rx
chmod u=rwx,g=rx,o=rx program
# Add read for everyone
chmod a+r document.txt
Octal notation
# Set 755 (rwxr-xr-x)
chmod 755 script.sh
# Set 644 (rw-r--r--)
chmod 644 config.txt
# Set 600 (rw-------)
chmod 600 ~/.ssh/id_rsa
Changing ownership with chown
# Change owner
sudo chown newuser file.txt
# Change owner and group
sudo chown newuser:developers file.txt
# Change recursively
sudo chown -R www-data:www-data /var/www/
Special permission bits
There are three additional permission bits that go beyond the standard rwx model. These are important for security because they are common targets for privilege escalation.
setuid (Set User ID)
When set on an executable, the program runs with the permissions of the file owner, not the user who launched it.
ls -l /usr/bin/passwd
Output:
-rwsr-xr-x 1 root root 68208 Jun 15 10:00 /usr/bin/passwd
The s in the owner execute position means setuid is set. When any user runs passwd, it executes as root. This is necessary because passwd needs to write to /etc/shadow, which only root can modify.
⚠ A setuid binary owned by root that has a vulnerability can give an attacker root access. This is a classic privilege escalation vector.
setgid (Set Group ID)
On an executable, the program runs with the permissions of the file’s group. On a directory, new files created inside inherit the directory’s group instead of the creator’s primary group.
# Create a shared directory
sudo mkdir /shared
sudo chown root:developers /shared
sudo chmod 2775 /shared
# The 2 at the front is the setgid bit
ls -ld /shared
Output:
drwxrwsr-x 2 root developers 4096 Jun 15 10:00 /shared
The s in the group execute position means setgid. Any file created in /shared will belong to the developers group.
Sticky bit
On a directory, only the file owner (or root) can delete files, even if others have write permission on the directory. /tmp uses this:
ls -ld /tmp
Output:
drwxrwxrwt 15 root root 4096 Jun 15 10:00 /tmp
The t at the end is the sticky bit. Everyone can create files in /tmp, but you can only delete your own.
Special bits in octal
| Bit | Octal | Position |
|---|---|---|
| setuid | 4 | First digit (e.g., 4755) |
| setgid | 2 | First digit (e.g., 2775) |
| sticky | 1 | First digit (e.g., 1777) |
Example 1: Set permissions using symbolic and octal notation
Let’s create a project with proper permissions:
# Create a project directory
mkdir -p ~/project
cd ~/project
# Create files with different purposes
touch app.py config.yaml deploy.sh README.md
# Make deploy.sh executable
chmod u+x deploy.sh
# Make config.yaml owner-only (contains secrets)
chmod 600 config.yaml
# Make README world-readable
chmod 644 README.md
# Check our work
ls -l
Output:
total 0
-rw-r--r-- 1 pratik pratik 0 Jun 15 10:00 app.py
-rw------- 1 pratik pratik 0 Jun 15 10:00 config.yaml
-rwxr--r-- 1 pratik pratik 0 Jun 15 10:00 deploy.sh
-rw-r--r-- 1 pratik pratik 0 Jun 15 10:00 README.md
Now let’s verify who can do what. Create a test user and try to access the config:
# As root, create a test user
sudo useradd -m testuser
# Try to read the config as testuser
sudo -u testuser cat ~/project/config.yaml
Output:
cat: /home/pratik/project/config.yaml: Permission denied
The 600 permission means only the owner (pratik) can read it. This is exactly what you want for files containing database passwords, API keys, or other secrets.
Example 2: Trace what a permission string actually allows
Let’s decode a real permission problem. Suppose a web server cannot read files in /var/www/html:
# Check the web server's user
ps aux | grep nginx | head -2
Output:
www-data 1234 0.0 0.1 12345 5678 ? S 10:00 0:00 nginx: worker process
root 1230 0.0 0.0 12345 2345 ? Ss 10:00 0:00 nginx: master process
Nginx worker runs as www-data. Let’s check file permissions:
ls -la /var/www/html/
Output:
total 12
drwxr-x--- 2 root root 4096 Jun 15 10:00 .
drwxr-xr-x 3 root root 4096 Jun 15 10:00 ..
-rw-r----- 1 root root 612 Jun 15 10:00 index.html
Walk through the access check for www-data reading index.html:
- Is www-data the owner? No, owner is
root. - Is www-data in the group? The group is
root. Check:groups www-datashowswww-data. Not in the root group. - Check “others” permission. The directory is
drwxr-x---. Others have no permissions (---). www-data cannot even enter the directory.
Fix:
# Option 1: Change ownership
sudo chown -R www-data:www-data /var/www/html/
# Option 2: Add www-data to the correct group and set group read
sudo chgrp -R www-data /var/www/html/
sudo chmod 750 /var/www/html/
sudo chmod 640 /var/www/html/index.html
# Option 3: Open "others" read (least restrictive)
sudo chmod o+rx /var/www/html/
sudo chmod o+r /var/www/html/index.html
The best choice depends on your security requirements. Option 1 is simplest. Option 2 is more precise. Option 3 is the most permissive and generally not recommended for sensitive content.
# Verify the fix (using option 2)
ls -la /var/www/html/
Output:
total 12
drwxr-x--- 2 root www-data 4096 Jun 15 10:00 .
drwxr-xr-x 3 root root 4096 Jun 15 10:00 ..
-rw-r----- 1 root www-data 612 Jun 15 10:00 index.html
Now www-data can read the directory (group has r-x) and read the file (group has r—).
What comes next
With filesystem structure and permissions covered, you are ready for Essential command line tools, where you will learn the core commands for navigating, searching, and processing files.
For an attacker’s view of Linux permissions, see Linux privilege escalation, which shows how misconfigured permissions lead to security breaches.