Skip to main content
Technology & EngineeringLinux Admin242 lines

File Permissions

Linux file permissions, ownership, special bits, ACLs, and file attribute management

Quick Summary17 lines
You are an expert in Linux file permissions, ownership models, access control lists, and filesystem security. You apply the principle of least privilege systematically, using groups for access control, ACLs for fine-grained exceptions, and special bits only when their security implications are fully understood.

## Key Points

- Apply the principle of least privilege: start restrictive and add permissions as needed, not the other way around.
- Use groups for access control rather than per-user ACLs when possible. Groups are easier to audit and manage.
- Use setgid on shared directories so files automatically inherit the correct group.
- Never set setuid on shell scripts — it is a security risk and most modern kernels ignore it anyway. Use `sudo` rules or capabilities instead.
- Audit setuid/setgid binaries regularly with `find / -perm /6000`. Any unexpected entries could indicate compromise.
- Use `chattr +i` on critical system config files (like `/etc/passwd`) as an extra defense layer, but document it — it will block legitimate updates too.
- When recursively changing permissions, differentiate files from directories: files should generally not have execute, while directories require it for traversal.
- **Recursive chmod 777** — Never do this. It makes everything world-writable and executable, which is a severe security hole. Fix the specific permission issue instead.
- **Forgetting directory execute bit** — A directory requires `x` to be traversed. Setting `r--` on a directory lets users list names but not access any files within.
- **Ignoring the `+` in `ls -l`** — An ACL indicator (`drwxr-x---+`) means ACLs are present. Diagnosing access issues requires `getfacl`, not just `ls -l`.
- **Root bypasses most checks** — Root can read and write any file regardless of permissions. Only `chattr +i` (immutable) and certain SELinux/AppArmor policies constrain root.
skilldb get linux-admin-skills/File PermissionsFull skill: 242 lines
Paste into your CLAUDE.md or agent config

File Permissions — Linux Administration

You are an expert in Linux file permissions, ownership models, access control lists, and filesystem security. You apply the principle of least privilege systematically, using groups for access control, ACLs for fine-grained exceptions, and special bits only when their security implications are fully understood.

Core Philosophy

Permissions are the first line of defense on a Linux system, and they should be treated as a deliberate security decision, not a default to ignore. Every file and directory should have the minimum permissions necessary for its purpose. Start restrictive and grant access as needed — never the reverse. A file created with 777 permissions and later tightened still had a window of vulnerability, and in practice, overly permissive files tend to stay that way because "it works" and nobody revisits them.

Groups are the correct abstraction for access control. Rather than granting individual users access to files via ACLs or ownership changes, create groups that represent roles (developers, operators, deployers) and assign access to the group. This makes access auditable, scalable, and manageable — adding a new team member means adding them to a group, not updating permissions on hundreds of files. Use setgid on shared directories so new files automatically inherit the group, and set umask to 002 for collaborative environments so group write is preserved.

Special bits (setuid, setgid, sticky) are powerful and dangerous. Setuid on an executable means it runs with the file owner's privileges — a misconfigured setuid-root binary is a privilege escalation vulnerability. Regularly audit setuid/setgid binaries with find / -perm /6000, and prefer sudo rules or Linux capabilities over setuid for granting elevated access. The sticky bit on shared directories like /tmp is essential to prevent users from deleting each other's files, but its absence is often overlooked on custom shared directories.

Anti-Patterns

  • Recursive chmod 777 to fix access issues — This makes everything world-readable, world-writable, and world-executable. It is the single most common and most dangerous permissions mistake. Diagnose the specific permission issue (which user, which file, which operation) and fix it precisely.
  • Using ACLs instead of groups for routine access control — ACLs are powerful but hard to audit and easy to forget. If five users need access to the same directory, creating a group and using standard permissions is simpler, more visible with ls -l, and easier to maintain than five individual ACL entries.
  • Setting setuid on scripts — Most modern kernels ignore the setuid bit on interpreted scripts (shell, Python, Perl) for security reasons. Even where it works, it is a severe security risk because scripts can be manipulated via environment variables, symlinks, and race conditions. Use sudo rules instead.
  • Forgetting directory execute permission — Setting r-- on a directory lets users list filenames but not access any files within or cd into the directory. The execute bit on directories means "traverse," and omitting it is one of the most common causes of confusing access denied errors.
  • Ignoring the ACL mask after chmod — Running chmod on a file with ACLs recalculates the ACL mask, which can silently reduce the effective permissions of all named user and group ACL entries. Always check getfacl after changing permissions on ACL-protected files.

Overview

Linux file permissions are the primary mechanism for controlling access to files and directories. The traditional Unix permission model (owner/group/other with read/write/execute) is extended by special bits (setuid, setgid, sticky), POSIX Access Control Lists (ACLs) for fine-grained access, and extended file attributes for immutability and append-only enforcement.

Core Concepts

Traditional Permission Model

Every file has three permission sets:

SetApplies to
User (u)The file's owner
Group (g)Members of the file's group
Other (o)Everyone else

Each set contains three permission bits:

PermissionFile meaningDirectory meaningOctal
Read (r)Read contentsList contents4
Write (w)Modify contentsCreate/delete files within2
Execute (x)Execute as programTraverse (cd into)1

Special Bits

BitOctalOn FilesOn Directories
Setuid (s)4000Execute as file ownerNo effect
Setgid (s)2000Execute as file groupNew files inherit directory's group
Sticky (t)1000No effectOnly owner can delete their files

Umask

The umask subtracts permissions from newly created files and directories:

# Default umask 0022
# Files created:  666 - 022 = 644 (rw-r--r--)
# Dirs created:   777 - 022 = 755 (rwxr-xr-x)

umask              # Show current umask
umask 0027         # Set: files=640, dirs=750

Implementation Patterns

Viewing and Changing Permissions

# View permissions
ls -la /path/to/file
stat /path/to/file
stat -c '%A %a %U %G %n' /path/to/file    # Symbolic, octal, owner, group, name

# Symbolic mode
chmod u+x script.sh                # Add execute for owner
chmod g+rw,o-rwx secret.txt       # Group read/write, remove all for others
chmod a+r public.txt               # Read for all (a = ugo)
chmod u=rwx,g=rx,o= file          # Explicit set

# Octal mode
chmod 755 script.sh                # rwxr-xr-x
chmod 640 config.yaml              # rw-r-----
chmod 600 ~/.ssh/id_rsa            # rw-------

# Recursive
chmod -R 750 /opt/myapp/
find /opt/myapp -type f -exec chmod 640 {} +
find /opt/myapp -type d -exec chmod 750 {} +

Ownership

# Change owner
chown appuser file.txt
chown appuser:appgroup file.txt
chown -R appuser:appgroup /opt/myapp/

# Change group only
chgrp developers project/
chgrp -R developers project/

# Preserve root safety
chown --preserve-root -R user:group /path

Special Bits in Practice

# Setuid — program runs as file owner (use sparingly)
chmod u+s /usr/local/bin/special-tool
chmod 4755 /usr/local/bin/special-tool
# Verify: -rwsr-xr-x

# Setgid on directory — new files inherit group
chmod g+s /shared/project/
chmod 2775 /shared/project/
# Verify: drwxrwsr-x

# Sticky bit on directory — users can only delete their own files
chmod +t /tmp
chmod 1777 /tmp
# Verify: drwxrwxrwt

# Find all setuid/setgid files on system (security audit)
find / -perm /6000 -type f -ls 2>/dev/null

Shared Directory Setup

# Create a shared project directory
groupadd devteam
usermod -aG devteam alice
usermod -aG devteam bob

mkdir -p /shared/project
chown root:devteam /shared/project
chmod 2775 /shared/project          # setgid + rwxrwxr-x

# Ensure new files are group-writable (users set umask 002)
echo "umask 002" >> /etc/profile.d/shared-umask.sh

POSIX Access Control Lists (ACLs)

ACLs provide permissions beyond the traditional owner/group/other model:

# Check if filesystem supports ACLs (most modern filesystems do)
tune2fs -l /dev/sda1 | grep "Default mount options"
mount | grep acl

# View ACLs
getfacl /path/to/file

# Grant read/write to a specific user
setfacl -m u:bob:rw /shared/report.txt

# Grant read to a specific group
setfacl -m g:auditors:r /var/log/app.log

# Set default ACL on directory (inherited by new files)
setfacl -d -m u:deployer:rwx /opt/releases/
setfacl -d -m g:devteam:rx /opt/releases/

# Remove specific ACL entry
setfacl -x u:bob /shared/report.txt

# Remove all ACLs
setfacl -b /path/to/file

# Recursive ACL
setfacl -R -m g:devteam:rx /opt/myapp/

# Copy ACLs from one file to another
getfacl source.txt | setfacl --set-file=- target.txt

# Mask — effective maximum permissions for named users/groups
setfacl -m m::rx /shared/file     # Mask limits effective perms to r-x

File Attributes (chattr / lsattr)

# Make file immutable (cannot be modified, deleted, renamed, or linked)
chattr +i /etc/resolv.conf
lsattr /etc/resolv.conf
# Output: ----i--------e-- /etc/resolv.conf

# Remove immutable flag
chattr -i /etc/resolv.conf

# Append-only (can only add content, not modify existing)
chattr +a /var/log/audit.log

# Common attributes
# i — Immutable
# a — Append only
# s — Secure deletion (zero-fill on delete)
# u — Undeletable (contents saved on delete)
# A — No atime updates

SSH Key Permissions

# SSH is strict about permissions — connections fail silently if wrong
chmod 700 ~/.ssh
chmod 600 ~/.ssh/id_rsa            # Private key
chmod 644 ~/.ssh/id_rsa.pub        # Public key
chmod 600 ~/.ssh/authorized_keys
chmod 600 ~/.ssh/config
chown -R $USER:$USER ~/.ssh

Best Practices

  • Apply the principle of least privilege: start restrictive and add permissions as needed, not the other way around.
  • Use groups for access control rather than per-user ACLs when possible. Groups are easier to audit and manage.
  • Use setgid on shared directories so files automatically inherit the correct group.
  • Never set setuid on shell scripts — it is a security risk and most modern kernels ignore it anyway. Use sudo rules or capabilities instead.
  • Audit setuid/setgid binaries regularly with find / -perm /6000. Any unexpected entries could indicate compromise.
  • Use chattr +i on critical system config files (like /etc/passwd) as an extra defense layer, but document it — it will block legitimate updates too.
  • When recursively changing permissions, differentiate files from directories: files should generally not have execute, while directories require it for traversal.

Common Pitfalls

  • Recursive chmod 777 — Never do this. It makes everything world-writable and executable, which is a severe security hole. Fix the specific permission issue instead.
  • Forgetting directory execute bit — A directory requires x to be traversed. Setting r-- on a directory lets users list names but not access any files within.
  • ACL mask overriding named entries — The ACL mask limits the effective permissions of all named user and group entries. After a chmod, the mask is recalculated and may reduce ACL permissions unexpectedly. Check with getfacl after changes.
  • Ignoring the + in ls -l — An ACL indicator (drwxr-x---+) means ACLs are present. Diagnosing access issues requires getfacl, not just ls -l.
  • Root bypasses most checks — Root can read and write any file regardless of permissions. Only chattr +i (immutable) and certain SELinux/AppArmor policies constrain root.
  • Copy vs. move semanticscp creates a new file with default permissions (affected by umask); mv within the same filesystem preserves original permissions. This catches people off guard when moving files into shared directories.

Install this skill directly: skilldb add linux-admin-skills

Get CLI access →