Permissions and User Management
Chapter 6: Permissions and User Management
Linux's permission system is the foundation of its multi-user design. From the basic rwx triple, to SetUID/SetGID special bits, to POSIX ACL fine-grained control, and finally to Linux capabilities that surgically split root's omnipotence โ each layer exists for a reason. This chapter explains every mechanism from a kernel perspective, helping you write secure system configurations and understand why misconfigured permissions are one of the most common sources of security vulnerabilities in production.
1. Permission Model: rwx Three Groups
Reading ls -l output
$ ls -l /etc/passwd
-rw-r--r-- 1 root root 2847 Apr 10 12:00 /etc/passwd
โโโฌโโโฌโโโฌโ โ โโโโ โโโโ
โ โ โ โ โ owner group
โ โ โ โ hard link count
โ โ โ โโโ other permissions (everyone else)
โ โ โโโโโโ group permissions (group members)
โ โโโโโโโโโ user permissions (owner)
โโโโโโโโโโโ file type: - regular / d directory / l symlink
/ c char device / b block device / p pipe / s socket
Permission octal table
# Permission bit values
# r = 4, w = 2, x = 1
# rwx = 4+2+1 = 7
# rw- = 4+2+0 = 6
# r-x = 4+0+1 = 5
# r-- = 4+0+0 = 4
# -wx = 0+2+1 = 3
# -w- = 0+2+0 = 2
# --x = 0+0+1 = 1
# --- = 0+0+0 = 0
# Common permission combinations
755 โ rwxr-xr-x executables/directories (owner write, others read+exec)
644 โ rw-r--r-- config files (everyone can read, only owner writes)
600 โ rw------- SSH private keys, password files
640 โ rw-r----- sensitive configs (group can read)
777 โ rwxrwxrwx DANGEROUS โ never use in production
700 โ rwx------ private scripts/directories
750 โ rwxr-x--- program directories (group can read+exec, others blocked)
2. chmod: Modifying Permissions
# Octal mode (most common)
chmod 755 script.sh # rwxr-xr-x
chmod 644 config.conf # rw-r--r--
chmod 600 ~/.ssh/id_rsa # SSH private key must be 600
# Symbolic mode (fine-grained adjustments)
chmod u+x script.sh # add execute for owner
chmod g-w file.txt # remove group write
chmod o=r file.txt # set other to read-only
chmod a+r file.txt # add read for all (a = ugo)
chmod u+x,g+r,o-x file.sh # combine operations
# Recursive (-R)
chmod -R 755 /var/www/html/
chmod -R u+X /srv/data/ # capital X: only adds exec to dirs and already-executable files
# Copy permissions from another file
chmod --reference=/etc/passwd /etc/shadow_backup
Capital X trick:
chmod -R a+X dir/adds execute only to directories and files that already have an execute bit โ it won't turn text files into executables. This is the standard way to fix directory permissions in bulk.
3. chown / chgrp
# chown: change owner (and optionally group)
chown alice file.txt # owner only
chown alice:developers file.txt # change owner and group
chown :developers file.txt # group only (same as chgrp)
# Recursive
chown -R www-data:www-data /var/www/html/
chown -R nginx: /etc/nginx/
# Copy ownership from another file
chown --reference=/etc/passwd /etc/newfile
# chgrp: change group only
chgrp developers project/
chgrp -R docker /var/run/docker.sock
4. Special Permission Bits: SetUID / SetGID / Sticky
SetUID (SUID): Execute as file owner
When a file has the SUID bit set, any user executing it runs with the file owner's effective UID. The passwd command uses this to let ordinary users modify /etc/shadow (owned by root).
# Inspect SUID files
ls -l /usr/bin/passwd
# -rwsr-xr-x 1 root root ... /usr/bin/passwd
# ^โโ s = SUID set (replaces the x position)
# Set SUID (prepend 4 to octal)
chmod 4755 /usr/local/bin/mytool # rwsr-xr-x
chmod u+s /usr/local/bin/mytool # symbolic form
# Security audit: find all SUID files
find / -perm -4000 -type f 2>/dev/null
SetGID (SGID): Directory group inheritance
On a directory, SGID causes new files created inside to automatically inherit the directory's group rather than the creator's primary group. Ideal for team shared directories.
mkdir /srv/team-files
chown :developers /srv/team-files
chmod 2775 /srv/team-files # 2 = SGID
# drwxrwsr-x โ new files inherit the developers group
chmod g+s /srv/team-files # symbolic form
Sticky Bit: /tmp's secret
On a directory, the sticky bit restricts file deletion so only the file's owner or root can delete it, even if other users have write permission on the directory. This is how /tmp works โ anyone can create files there, but can't delete others' files.
ls -ld /tmp
# drwxrwxrwt ... โ t = sticky bit (replaces the x position)
chmod 1777 /tmp # 1 = sticky bit
chmod +t /shared-dir # symbolic form
5. umask: Default Permission Mask
umask determines the default permissions for newly created files and directories. Formula: actual permissions = maximum permissions - umask. Max for files is 666 (no default execute), max for directories is 777.
# View current umask
umask # e.g. 0022
umask -S # symbolic form: u=rwx,g=rx,o=rx
# umask calculation examples
# umask=022: file=666-022=644, dir=777-022=755 (most common)
# umask=027: file=666-027=640, dir=777-027=750 (secure services)
# umask=077: file=666-077=600, dir=777-077=700 (strictest, home dirs)
# Set temporarily
umask 027
# Set permanently (add to ~/.bashrc or /etc/profile)
echo "umask 022" >> ~/.bashrc
# Verify
umask 027
touch newfile.txt && ls -l newfile.txt # should be -rw-r-----
mkdir newdir && ls -ld newdir # should be drwxr-x---
6. sudo Complete Guide
# Always edit sudoers with visudo (syntax check prevents lockout)
visudo
visudo -f /etc/sudoers.d/myapp
# sudoers format:
# user host=(run-as) commands
# alice can run all commands as root on all hosts (with password)
alice ALL=(ALL:ALL) ALL
# bob can restart nginx without password
bob ALL=(root) NOPASSWD: /bin/systemctl restart nginx
# developers group can run all commands (with password)
%developers ALL=(ALL) ALL
# Group commands with Cmnd_Alias
Cmnd_Alias WEBOPS = /bin/systemctl restart nginx, /bin/systemctl reload nginx, /usr/bin/tail -f /var/log/nginx/*.log
webteam ALL=(root) NOPASSWD: WEBOPS
# Check your own sudo permissions
sudo -l
Security warning: Never grant unrestricted NOPASSWD access to vim/less/more/find/awk/python or similar tools โ these can trivially escape to a shell (e.g.,
:!/bin/bashin vim), giving full root access. These are known GTFOBins vectors.
# sudo common usage
sudo command # run as root
sudo -u postgres psql # run as postgres user
sudo -i # switch to root login shell
sudo -s # switch to root non-login shell
sudo -l # list current user's sudo permissions
sudo -k # clear cached credentials
sudo -v # refresh credential timeout
7. su vs sudo
| Aspect | su | sudo |
|---|---|---|
| Password required | Target user's password | Your own password |
| Audit logging | Limited | Detailed (per-command) |
| Least privilege | Gets full user permissions | Can restrict to specific commands |
| Shared secrets | root password shared with many | Each user uses their own password |
| Recommendation | Avoid in modern systems | Preferred |
# su vs su -
su postgres # switch user, keep current environment variables
su - postgres # switch user AND load full login environment
# Run single command as another user
su -c "pg_dump mydb" postgres
# Equivalent sudo approach (no password required for postgres)
sudo -u postgres pg_dump mydb
8. User Management
# useradd: create user
useradd -m -s /bin/bash alice # -m create home dir, -s set shell
useradd -m -s /bin/bash -G sudo,docker alice # add to supplementary groups
useradd -r -s /sbin/nologin nginx # system account (no login shell)
# Set password
passwd alice
echo "alice:SecurePass123" | chpasswd # batch (for scripts)
# usermod: modify existing user
usermod -aG docker alice # -a APPEND to group (omitting -a replaces all groups)
usermod -aG sudo alice
usermod -s /bin/zsh alice # change shell
usermod -l newname alice # rename user
usermod -L alice # lock account (prepend ! to password)
usermod -U alice # unlock account
# userdel: delete user
userdel alice # remove account, keep home directory
userdel -r alice # also remove home directory and mail spool
# groupadd / groupmod / groupdel
groupadd developers
groupadd -g 2000 devops
groupmod -n devteam developers # rename group
groupdel devteam
# Inspect user identity
id
groups
id alice
9. POSIX ACL: Fine-grained Permission Control
Traditional Unix permissions only support three groups: owner/group/other. You cannot express "user charlie has read access to this file, but charlie doesn't belong to the file's group." POSIX ACLs fill this gap.
# Install (Debian/Ubuntu)
apt install acl
# getfacl: view ACL
getfacl /srv/project/
# # file: srv/project/
# # owner: alice
# # group: developers
# user::rwx
# group::r-x
# other::---
# mask::rwx
# setfacl: set ACL
setfacl -m u:charlie:r-- /srv/project/readme.txt # give charlie read access
setfacl -m g:ops:rwx /srv/project/ # give ops group full access
setfacl -x u:charlie /srv/project/readme.txt # remove charlie's ACL entry
setfacl -b /srv/project/readme.txt # remove all ACL entries
# Default ACL: new files in directory inherit ACL
setfacl -d -m u:charlie:r-- /srv/project/
setfacl -d -m g:ops:rwx /srv/project/
# Copy ACL from one directory to another
getfacl source-dir | setfacl --set-file=- target-dir
# Files with ACL show a + in ls -l
ls -l /srv/project/readme.txt
# -rw-r--r--+ 1 alice developers ...
# ^โโ + indicates additional ACL entries
10. Linux Capabilities: Splitting Root's Power
Traditional Unix access is binary: either root (omnipotent) or regular user (restricted). Linux capabilities split root's privileges into ~40 individual abilities that can be precisely granted, eliminating the need to give an entire program root just to bind port 80.
| Capability | Meaning | Typical use |
|---|---|---|
| CAP_NET_BIND_SERVICE | Bind ports below 1024 | Web server listening on 80/443 |
| CAP_NET_RAW | Use raw sockets | ping, tcpdump |
| CAP_SYS_PTRACE | Trace arbitrary processes | strace, gdb |
| CAP_SYS_ADMIN | Broad system administration | mount, namespace operations |
| CAP_DAC_OVERRIDE | Bypass file permission checks | Backup tools |
| CAP_KILL | Send signals to any process | System monitoring tools |
| CAP_CHOWN | Change any file's ownership | File management services |
# Install tools
apt install libcap2-bin
# getcap: view file capabilities
getcap /usr/bin/ping
# /usr/bin/ping cap_net_raw=ep
# e=effective p=permitted i=inherited
# setcap: grant capability (requires root)
# Allow node to bind port 80 without root
setcap cap_net_bind_service=+ep /usr/bin/node
# Allow custom tool to use raw sockets
setcap cap_net_raw=+ep /usr/local/bin/myping
# Remove capability
setcap -r /usr/bin/node
# View current process capabilities
cat /proc/$$/status | grep Cap
# Decode hex capability value
capsh --decode=000001ffffffffff
# Production example: nginx as non-root user on port 80
useradd -r -s /sbin/nologin nginx
setcap cap_net_bind_service=+ep /usr/sbin/nginx
# nginx.conf: user nginx; -- no longer needs initial root launch
Why capabilities are safer than SetUID: SetUID root grants a program complete root power for its entire execution โ any vulnerability can be fully exploited. Capabilities grant only the minimum necessary privilege, so even a compromised program can do far less damage. This is the Principle of Least Privilege in practice.
11. File Attributes: chattr / lsattr
# lsattr: view extended file attributes
lsattr /etc/passwd
# ----i--------e-- /etc/passwd
# ^ i = immutable
# chattr: set/clear attributes
chattr +i /etc/passwd # immutable: even root cannot rm/mv/write
chattr -i /etc/passwd # remove immutable
chattr +a /var/log/app.log # append-only: cannot overwrite or delete
chattr -a /var/log/app.log # remove append-only
# Common attributes:
# i (immutable): no modification, deletion, rename, or hard links by anyone
# a (append-only): only appending allowed, protect log integrity
# e (extent format): ext4 default, usually automatic
# Production use cases
chattr +i /etc/resolv.conf # prevent NetworkManager from overwriting
chattr +i /etc/hosts # prevent accidental modification
chattr +a /var/log/secure # audit log append-only, tamper-resistant
# Audit: find all immutable files under /etc
lsattr -R /etc/ 2>/dev/null | grep -- "----i"
Previous
โ Ch5: Processes
Next
Ch7: Network โ