Permissions & Ownership
Permissions & Ownership
Every file and directory on a Linux system carries a small but powerful security record: an owner, a group, and a set of permission bits. Get these wrong on a production server and you create either a security hole or an outage — usually both, at the worst possible moment. This lesson breaks down the entire model, from the raw bit layout to the production defaults that big-tech operations teams enforce on day one.
The Three Layers: Owner, Group, Others
Linux tracks three identities for every filesystem object:
- Owner (u — user): the account that created the file, or whoever was assigned with
chown. - Group (g): a named collection of users. Any member of the assigned group receives the group permissions.
- Others (o): everyone else — any process running as a user that is neither the owner nor in the group.
The kernel checks these layers in order and stops at the first match. If you are the owner, only the owner bits apply — even if the group bits are more permissive.
The rwx Bits — Reading the Permission String
Run ls -l on any file and the leftmost column is a 10-character string like -rwxr-xr--. Breaking it down:
For directories, the bits mean something slightly different: r lets you list the contents (ls), w lets you create or delete files inside it, and x (the "execute" bit) is the search bit — without it you cannot cd into the directory or traverse it in a path, even if you have read permission.
chmod — Changing Permissions
Two syntaxes exist: symbolic and octal. Both are used daily; know both cold.
chmod 777 in production. It grants write permission to every user on the system. A common mistake is "fixing" a web app that cannot write to storage/ by setting 777 — this gives any compromised process the ability to overwrite your application files. The correct fix is to set the right owner or group, not to open permissions to the world.
chown — Changing Ownership
Only root (or a process with CAP_CHOWN) can change the owner of a file. The syntax is chown user:group target.
www-data, nginx, postgres). The application files are owned by that account, and no other user can write to them. This limits the blast radius if the service is compromised — the attacker controls only what that account can touch.
umask — The Default Permission Filter
When a process creates a new file, the kernel starts with a base permission of 0666 for files and 0777 for directories, then subtracts the umask bits. The umask is a bitmask of permissions to deny.
The most common default is 0022: deny write for group and others. So a new file gets 0666 & ~0022 = 0644 (rw-r--r--), and a new directory gets 0777 & ~0022 = 0755 (rwxr-xr-x).
027 or 077 for system accounts. Setting 022 in a daemon's environment is the absolute minimum — it prevents group/world-writable config files from appearing after upgrades. Set umask in the service unit file (UMask=0027 in systemd) so it is not dependent on the shell environment that launched the service.
Special Bits: setuid, setgid, and Sticky
Three extra bits live above the standard rwx triplets. They appear as a fourth octal digit or as modified characters in the ls output.
Setting these bits follows the same chmod syntax, with the special bits as the leading octal digit:
chmod 4755 /usr/local/bin/myprog— setuid + normal 755chmod 2775 /srv/shared— setgid on a shared directorychmod 1777 /tmp— sticky + world-writable (the/tmpstandard)
When s appears in uppercase (S) in the ls output, it means the setuid/setgid bit is set but the execute bit is not — a useless and potentially confusing state. Always verify with ls -l after setting special bits.
find / -perm /4000 -ls 2>/dev/null, remove the bit from anything that does not genuinely need it, and use nosuid as a mount option on filesystem partitions that should never contain SUID binaries (e.g. /home, /tmp, /var).
Practical Production Checklist
These are the permission patterns you will set repeatedly across every environment:
600— private keys (~/.ssh/id_rsa), database password files,.envfiles644— static web assets, config files that daemons read as root755— binaries, web directories, scripts in/usr/local/bin700—~/.ssh, scripts with embedded credentials2775— shared team directories where group ownership must propagate1777— world-writable temporary directories
When a web application cannot write to its upload or cache directory, the answer is almost always a missed chown — not a chmod 777. Identify the process user (ps aux | grep nginx), then chown -R processuser:processuser /var/www/app/storage and set the directory to 755 or 775.
find /var/www/app -perm /o+w -ls will surface any world-writable files created by a buggy deploy script or a developer testing on the server. Make it a pipeline gate — fail the deploy if any world-writable file appears outside explicitly allowed paths.