Unix file system security: permissions, SUID, umask and mount options
Almost everything on a Unix system is a file, and the permissions on those files decide who can do what. Getting them right, and checking that they stay right, is basic security work.
How permissions work
Every file has an owner, a group and three sets of permission bits: for the owner (user), the group and everyone else (other). Each set can allow read, write and execute.
$ ls -l /etc/passwd /etc/shadow /usr/bin/passwd
-rw-r--r-- 1 root root 2843 Oct 1 09:12 /etc/passwd
-rw-r----- 1 root shadow 1502 Oct 1 09:12 /etc/shadow
-rwsr-xr-x 1 root root 68248 Aug 4 14:02 /usr/bin/passwd
| Bit | On a file | On a directory |
|---|---|---|
r (4) | Read the contents | List the names in it |
w (2) | Change the contents | Create, delete and rename entries (even files you do not own) |
x (1) | Run it as a program | Enter it and reach files inside |
Note the directory rules: write permission on a directory lets you delete any file in it, whatever the file's own permissions say. That is why shared directories need the sticky bit (below). Change modes with chmod and owners with chown.
Sensible permissions for important files
| Path | Mode | Owner |
|---|---|---|
/etc/passwd, /etc/group | 644 | root:root |
/etc/shadow, /etc/gshadow | 640 (Debian) or 000 (RHEL) | root:shadow or root:root |
/etc/sudoers, files in /etc/sudoers.d | 440 | root:root |
/etc/ssh/ssh_host_*_key | 600 | root:root |
~/.ssh and ~/.ssh/authorized_keys | 700 and 600 | the user |
/root | 700 | root:root |
/tmp, /var/tmp | 1777 | root:root |
/etc/crontab, /etc/cron.d | 600 / 700 (or 644/755) | root:root |
Package managers can check the files they installed: rpm -Va on RHEL-type systems, and debsums -s (from the debsums package) or dpkg --verify on Debian.
umask: the default for new files
The umask removes permission bits from every file a process creates. A umask of 022 gives new files mode 644 and directories 755; 027 gives 640 and 750, so other users cannot read them.
umask # show the current value
umask 027 # set it for this shell
For services, set it in the systemd unit with UMask=0027. For logins, many distributions read UMASK in /etc/login.defs.
Set-user-ID and set-group-ID programs
A program with the set-user-ID (SUID) bit runs with the rights of its owner, not of the user who started it. That is how passwd can update /etc/shadow. It is also why a bug in a SUID-root program can give an attacker root. Know every one on your system:
sudo find / -xdev -type f \( -perm -4000 -o -perm -2000 \) -exec ls -ld {} + | sort -k9
Save the list and compare it after updates; a new SUID file that no package installed is a red flag. Uninstall packages you do not need (for example at). A chmod u-s on a packaged file is undone by the next update; on Debian make such a change permanent with dpkg-statoverride --update --add root root 0755 /usr/bin/at. On Linux, many programs that used to be SUID now use file capabilities instead; list those with getcap -r / 2>/dev/null.
The sticky bit and world-writable files
On a directory, the sticky bit (t in drwxrwxrwt) means users can only delete their own files. Every world-writable directory needs it. World-writable files are almost always a mistake.
# world-writable directories without the sticky bit
sudo find / -xdev -type d -perm -0002 ! -perm -1000 -print
# world-writable files
sudo find / -xdev -type f -perm -0002 -print
# files owned by no existing user or group
sudo find / -xdev \( -nouser -o -nogroup \) -print
Files without an owner are usually left over from deleted accounts. If a new account later gets the same numeric ID, it inherits them.
Access control lists
When owner, group and other are not enough, POSIX ACLs give extra users or groups access to one file or directory, without changing its group:
setfacl -m u:anna:rx /srv/reports # anna may read and enter
setfacl -d -m g:auditors:r /srv/reports # default ACL for new files
getfacl /srv/reports
A + at the end of the mode in ls -l shows that a file has an ACL. Remember to check ACLs when you audit permissions.
Immutable and append-only files
On Linux ext4 and XFS, chattr +i makes a file immutable: not even root can change or delete it until the flag is removed. chattr +a allows only appending, which suits log files.
sudo chattr +i /etc/ssh/sshd_config
lsattr /etc/ssh/sshd_config
sudo chattr -i /etc/ssh/sshd_config
This stops accidents and simple scripts, but root can remove the flag, so it is not protection against an intruder with root. The BSDs offer the stronger schg flag together with a raised securelevel.
Mount options
Mount options limit what can be done on a whole file system:
nosuid: SUID and SGID bits are ignored.nodev: device files are not interpreted.noexec: programs cannot be run directly from it.
Use all three on removable media and on /tmp, /var/tmp and /dev/shm where your software allows. Some installers and package scripts run programs from /tmp, so test noexec before you rely on it.
# /etc/fstab
tmpfs /tmp tmpfs defaults,nosuid,nodev,size=2G 0 0
tmpfs /dev/shm tmpfs defaults,nosuid,nodev,noexec 0 0
findmnt -o TARGET,OPTIONS /tmp /dev/shm
See mount for more on mounting.
Encryption at rest
Permissions protect files only while the operating system is running. Anyone who removes the disk can read everything. Encrypt laptops and any system that could be stolen or sent away for repair:
- Linux: LUKS2, chosen in the installer, unlocked with a passphrase or the TPM (
systemd-cryptenroll). - FreeBSD: GELI or native ZFS encryption.
- Solaris 11: ZFS encryption (
zfs create -o encryption=on). - AIX: Encrypted File System (EFS) or encrypted logical volumes.
Keep the recovery passphrase somewhere safe and separate from the machine.
Detect changes
A file integrity checker records checksums of system files and reports anything that changes. AIDE is packaged for every major distribution:
sudo apt install aide # or: sudo dnf install aide
sudo aideinit # Debian/Ubuntu
# RHEL: sudo aide --init && sudo mv /var/lib/aide/aide.db.new.gz /var/lib/aide/aide.db.gz
sudo aide --check # run daily, for example from cron
Keep a copy of the AIDE database somewhere the server cannot write, or an intruder can simply update it.