Chapter 6

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/bash in 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 โ†’
Rate this chapter
4.7  / 5  (52 ratings)

๐Ÿ’ฌ Comments