Linux uses a discretionary access control (DAC) model. Every file has an owner, a group, and three sets of permissions. Understanding this system is essential for security and system administration.
When you run ls -l, the first column shows permissions in this format:
| Position | Value | Meaning |
|---|---|---|
| 1 | d | File type: d=directory, -=file, l=symlink, c=char device, b=block device, p=pipe, s=socket |
| 2-4 | rwx | Owner (user) permissions: r=read, w=write, x=execute |
| 5-7 | r-x | Group permissions: r=read, -=no write, x=execute |
| 8-10 | r-x | Others (world) permissions: r=read, -=no write, x=execute |
| Permission | On a File | On a Directory |
|---|---|---|
| r (read) | Can read file contents (cat, less) | Can list contents (ls) |
| w (write) | Can modify file contents | Can create/delete files inside |
| x (execute) | Can run as a program | Can enter with cd, access files |
# Common permission strings you'll see -rw-r--r-- # Regular file: owner can read/write, others read-only (e.g. /etc/passwd) -rw------- # Only owner can read/write (e.g. ~/.bash_history, SSH keys) -rwxr-xr-x # Executable: owner full, others can read+execute (e.g. /bin/ls) drwxr-xr-x # Directory: owner full, others can ls and cd into drwx------ # Directory only owner can access (e.g. ~/.ssh) -rwxrwxrwx # World-writable — DANGEROUS, avoid this lrwxrwxrwx # Symlink (permissions are always 777, actual target's perms apply)
# Syntax: chmod [who][operator][permissions] file # who: u=user/owner g=group o=others a=all (u+g+o) # operator: +=add -=remove ==set exactly # perms: r=read w=write x=execute X=execute if dir or already executable $ chmod u+x script.sh # add execute for owner $ chmod g-w file.txt # remove write from group $ chmod o=r file.txt # set others to read-only exactly $ chmod a+r public.txt # add read for everyone $ chmod u+x,g-w,o= file.txt # multiple changes at once $ chmod a-x binary_file # remove execute from everyone $ chmod g+rw shared.txt # give group read+write $ chmod u=rwx,g=rx,o= script # set all three at once $ chmod -R g+rX /var/www/ # recursive: add group read + execute on dirs
# Each permission has a numeric value: # r = 4, w = 2, x = 1 # Add values for each set: owner, group, others # Common combinations $ chmod 644 file.txt # -rw-r--r-- (standard file) $ chmod 755 script.sh # -rwxr-xr-x (executable) $ chmod 700 private_dir/ # drwx------ (private) $ chmod 777 shared/ # drwxrwxrwx (world-writable — AVOID) $ chmod 600 ~/.ssh/id_rsa # -rw------- (SSH private key) $ chmod 400 ~/.ssh/id_rsa # -r-------- (read-only private key) $ chmod 664 group_file.txt # -rw-rw-r-- (owner+group write) $ chmod 440 /etc/sudoers # -r--r----- (sudoers file)
| Number | Binary | r | w | x | Meaning |
|---|---|---|---|---|---|
| 0 | 000 | - | - | - | No permissions |
| 1 | 001 | - | - | x | Execute only |
| 2 | 010 | - | w | - | Write only |
| 3 | 011 | - | w | x | Write + Execute |
| 4 | 100 | r | - | - | Read only |
| 5 | 101 | r | - | x | Read + Execute |
| 6 | 110 | r | w | - | Read + Write |
| 7 | 111 | r | w | x | Full (Read + Write + Execute) |
# Quick reference: chmod NNN means owner=N, group=N, others=N # chmod 754 file → owner=7(rwx), group=5(r-x), others=4(r--) # chmod 640 file → owner=6(rw-), group=4(r--), others=0(---) # Recursive chmod $ chmod -R 755 /var/www/html/ # apply to all files and dirs recursively $ chmod -R u=rwX,go=rX /srv/ # uppercase X: only if dir or already executable
# chown — change file owner and/or group # Syntax: chown [owner][:group] file... $ chown alice file.txt # change owner to alice $ chown alice:developers file.txt # change owner and group $ chown :developers file.txt # change group only (note the colon) $ chown -R www-data:www-data /var/www/ # recursive ownership change $ chown --from=alice bob file.txt # change only if current owner is alice # chgrp — change group only $ chgrp developers project/ # change group to developers $ chgrp -R www-data /var/www/ # recursive group change $ chgrp $(id -gn) file.txt # set to your current primary group # View ownership $ ls -l file.txt -rw-r--r-- 1 alice developers 1234 Jun 15 file.txt # owner group
umask defines which permissions are removed from newly created files. It's subtracted from the maximum permissions (666 for files, 777 for directories).
# View current umask $ umask 0022 # How it works: # Files max: 666 (rw-rw-rw-) # Dirs max: 777 (rwxrwxrwx) # umask 022: # New file: 666 - 022 = 644 (rw-r--r--) # New dir: 777 - 022 = 755 (rwxr-xr-x) # Common umask values $ umask 022 # standard: files=644, dirs=755 $ umask 027 # group no write, others nothing: files=640, dirs=750 $ umask 077 # private: files=600, dirs=700 (good for ~/.ssh) $ umask 002 # group-friendly: files=664, dirs=775 # Set umask in ~/.bashrc to persist echo "umask 022" >> ~/.bashrc # Show umask in symbolic form $ umask -S u=rwx,g=rx,o=rx
# When set on an executable, it runs as the FILE OWNER, not the running user # Classic example: /usr/bin/passwd (must write to /etc/shadow as root) $ ls -l /usr/bin/passwd -rwsr-xr-x 1 root root 68208 /usr/bin/passwd # ^ s in place of x means SUID is set $ chmod u+s /usr/bin/myprog # add SUID $ chmod 4755 /usr/bin/myprog # numeric: 4 = SUID $ find / -perm -4000 -type f # find all SUID files (security audit)
# On executables: runs as the FILE GROUP # On directories: new files inherit the directory's group (very useful!) $ ls -l /usr/bin/wall -rwxr-sr-x 1 root tty 30800 /usr/bin/wall # ^ s in group execute position = SGID # SGID on directory: shared project directory $ mkdir /srv/project $ chown :developers /srv/project $ chmod g+s /srv/project # any file created inherits 'developers' group $ chmod 2775 /srv/project # numeric
# On directories: only the FILE OWNER can delete their own files # Classic use: /tmp — anyone can write, but can't delete others' files $ ls -ld /tmp drwxrwxrwt 20 root root 4096 Jun 15 /tmp # ^ t = sticky bit set $ chmod +t /shared/upload/ # add sticky bit $ chmod 1777 /shared/upload/ # numeric: 1 = sticky $ chmod -t /dir/ # remove sticky bit
# useradd — create user $ useradd alice # create user with defaults $ useradd -m alice # create with home directory $ useradd -m -s /bin/bash alice # set login shell $ useradd -m -s /bin/bash -G sudo,docker alice # add to groups $ useradd -m -c "Alice Smith" alice # set comment/full name $ useradd -u 1500 -m alice # specify UID $ useradd -e 2024-12-31 alice # account expiry date # usermod — modify existing user $ usermod -aG docker alice # add to group (MUST use -a flag!) $ usermod -aG sudo,developers alice # add to multiple groups $ usermod -s /bin/zsh alice # change shell $ usermod -l newname oldname # rename user account $ usermod -L alice # lock account (disable password) $ usermod -U alice # unlock account $ usermod -e "" alice # remove expiry date # userdel — remove user $ userdel alice # remove user (keep home dir) $ userdel -r alice # remove user AND home directory $ userdel -f alice # force (even if logged in) # passwd — set/change password $ passwd alice # set password for alice (as root) $ passwd # change your own password $ passwd -l alice # lock account $ passwd -u alice # unlock account $ passwd -e alice # force password change on next login $ passwd -d alice # delete password (no password needed)
# Format: username:password:UID:GID:comment:home:shell $ cat /etc/passwd root:x:0:0:root:/root:/bin/bash daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin alice:x:1000:1000:Alice Smith,,,:/home/alice:/bin/bash www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin # 'x' in password field means hash is in /etc/shadow # /usr/sbin/nologin prevents interactive login (system accounts)
# Format: username:hash:lastchange:min:max:warn:inactive:expire:reserved # Only readable by root $ sudo cat /etc/shadow alice:$6$rounds=5000$salt$hashedpassword:19523:0:99999:7::: # $6$ = SHA-512 hash algorithm # $1$ = MD5 (old, insecure) # $5$ = SHA-256
# Format: groupname:password:GID:member1,member2,... $ cat /etc/group root:x:0: sudo:x:27:alice,bob docker:x:999:alice,charlie developers:x:1001:alice,bob,charlie # Add user to group without usermod $ gpasswd -a alice developers # add alice to developers group $ gpasswd -d alice developers # remove alice from developers
# Who am I? $ whoami alice # Full identity info $ id uid=1000(alice) gid=1000(alice) groups=1000(alice),27(sudo),999(docker) $ id alice # check another user's identity $ id -u # print only UID $ id -g # print only primary GID $ id -G # print all GIDs $ id -Gn # print all group names # Show groups $ groups # list groups current user belongs to $ groups alice # list groups for specific user # Currently logged-in users $ who # show who is logged in $ w # show who + what they're doing $ last # login history $ last alice # login history for specific user $ lastb # failed login attempts (bad logins)
# sudo — superuser do $ sudo apt update # run as root $ sudo -u alice command # run as specific user $ sudo -i # start root interactive shell (with root env) $ sudo -s # start root shell (with current env) $ sudo !! # re-run last command with sudo $ sudo -l # list what sudo commands you can run $ sudo -l -U alice # list alice's sudo permissions (as root) # /etc/sudoers — sudo configuration (ALWAYS edit with visudo) $ sudo visudo # safely edit sudoers (validates syntax) # Common sudoers entries: # alice ALL=(ALL:ALL) ALL # alice can run any command as any user # %sudo ALL=(ALL:ALL) ALL # sudo group members can run anything # alice ALL=(ALL) NOPASSWD: ALL # alice needs no password for sudo # alice ALL=/usr/bin/apt # alice can only run apt with sudo # @includedir /etc/sudoers.d # include files from this directory # Drop-in sudoers files (preferred method) $ echo "alice ALL=(ALL) NOPASSWD: /usr/bin/systemctl" | sudo tee /etc/sudoers.d/alice $ sudo chmod 440 /etc/sudoers.d/alice
# su — substitute user $ su alice # switch to alice (uses alice's password) $ su - alice # switch with full login environment $ su - # switch to root (full login shell) $ su -c "command" alice # run single command as alice $ su -c "apt update" root # run single command as root # Difference between su and su -: # su alice: switches user but keeps current environment variables # su - alice: full login — gets alice's PATH, HOME, env variables
sudo instead of su on modern systems. sudo provides better auditing (commands are logged), doesn't require sharing the root password, and allows fine-grained permission control.
ACLs extend standard permissions. They let you set permissions for specific users or groups beyond the standard owner/group/others model.
# Install acl tools $ sudo apt install acl # Debian/Ubuntu $ sudo yum install acl # CentOS/RHEL # getfacl — view ACL $ getfacl /var/www/html/ # file: var/www/html/ # owner: root # group: www-data user::rwx group::r-x other::r-x # setfacl — set ACL $ setfacl -m u:alice:rwx /var/www/html/ # give alice full access $ setfacl -m g:developers:rx /srv/app/ # give developers group r-x $ setfacl -m o::--- /private/ # remove all other access $ setfacl -x u:alice /var/www/html/ # remove alice's ACL entry $ setfacl -b /var/www/html/ # remove all ACL entries $ setfacl -R -m u:alice:rw /srv/data/ # recursive ACL set $ setfacl -d -m g:developers:rwx /shared/ # default ACL (for new files) # Check if ACL is set — ls shows '+' after permissions $ ls -l /var/www/html/ drwxr-xr-x+ 2 root www-data 4096 Jun 15 /var/www/html/ # ^ '+' indicates ACL is present
# Scenario 1: Web server files
$ chown -R www-data:www-data /var/www/html/
$ chmod -R 644 /var/www/html/ # files: owner rw, others r
$ find /var/www/html -type d -exec chmod 755 {} \; # dirs need execute
# Scenario 2: Shared development directory
$ mkdir /srv/devteam
$ chown root:developers /srv/devteam
$ chmod 2775 /srv/devteam # SGID + rwxrwxr-x
# Scenario 3: SSH key security (CRITICAL)
$ chmod 700 ~/.ssh/ # only owner can access dir
$ chmod 600 ~/.ssh/id_rsa # private key: only owner read/write
$ chmod 644 ~/.ssh/id_rsa.pub # public key: others can read
$ chmod 600 ~/.ssh/authorized_keys # authorized keys: only owner
$ chmod 644 ~/.ssh/known_hosts # known hosts: others can read
# Scenario 4: Log files that app needs to write
$ touch /var/log/myapp.log
$ chown myappuser:myappgroup /var/log/myapp.log
$ chmod 640 /var/log/myapp.log # app writes, admins read, others nothing