Reading and setting permissions
rwx, octal modes, directories and umask.
Every file and directory on a Linux system carries permissions that answer one question: who may read it, change it, or run it? This lesson teaches you to read those permissions at a glance, set them precisely with chmod in both its number and letter forms, understand why execute means something different on a directory, and see where a new file's permissions come from. Reading permissions correctly is most of understanding why a command is refused and why a server is or is not safe.
Reading the nine switches
The command that shows a file's permissions is ls -l (list, long format). The first field it prints is ten characters. The first character is the type: - for an ordinary file, d for a directory, l for a symbolic link. The other nine are three groups of three, always in the same order: the owner, then the group, then everyone else (often called "others" or "the world"). Inside each group the order is always read, write, execute. A letter means the permission is on; a dash means off.
Read -rwxr-x--- in three blocks. The owner deploy has rwx, all three. The group deploy (the second name on the line) has r-x: read and execute, but not write. Everyone else has ---, nothing. So the owner can read, change and run this script, members of the group can read and run it but not change it, and no other account can touch it.
The same permissions as a number: octal chmod
Writing rwxr-x--- in full is tedious, so the same thing is written as a three-digit octal number (base 8, each digit 0 to 7). Give each switch a value: read is 4, write is 2, execute is 1. Add up the switches that are on in a group to get one digit. rwx is 4+2+1 = 7, r-x is 4+1 = 5, rw- is 4+2 = 6, r-- is 4. So rwxr-x--- is 750 and rw-r----- is 640. The command that sets permissions is chmod (change mode).
The file was created -rw-rw-r-- (664, the reason is the umask section below), and chmod 640 closed it to -rw-r-----: the owner reads and writes, the group reads, others get nothing. A few numbers cover most daily work: 644 for a file the world may read, 640 for one only a trusted group may read, 600 for a private file, 755 for a program or directory others may use, 700 for a directory only you may enter. A leading fourth digit, as in 0644, holds the special bits covered in the SUID, SGID and sticky-bit lesson; a plain 0 means none are set, so 0644 and 644 describe the same file.
Changing one switch at a time: symbolic chmod
The number form rewrites all nine switches at once. When you want to flip a single switch and leave the rest alone, use the letter form. Name a class (u for the owner, g for group, o for others, a for all), then + to add, - to remove or = to set exactly, then the switches.
chmod u+x,go-w turned on the owner's execute bit and turned off the write bit for both group and others, in one step, leaving every other switch as it was. Separate several changes with commas.
On a directory, execute is the key to the door
This is where people are caught out. On a file, execute means run it as a program. On a directory it means something different: it is permission to enter the directory and reach the files inside by name. Read on a directory is a separate power: it lets you list the names inside. The two come apart. Make a directory with one file in it, then give the directory read but not execute (mode 600):
ls lists the name data.csv because you can read the directory, but cd into it is denied because you cannot enter it. Now the opposite, execute but not read (mode 100):
Now ls cannot list the contents (cannot open directory, and it exits with status 2), but you can still enter the directory and open a file whose name you already know. This split is common: a directory set to 701 or 711 lets programs reach a file by its exact path without letting anyone list what else is there. Give the practice directory an ordinary mode again before you move on; a directory you cannot list is also one rm -r cannot empty:
w and x) can remove files inside it, even files they do not own or cannot read. That is why a directory writable by everyone is dangerous, and why /tmp carries the sticky bit, which the lesson on SUID, SGID and the sticky bit covers.Where new files get their permissions: the umask
You never set the permissions of every file you create by hand, so where do the defaults come from? From the umask (user file-creation mask), a set of switches to withhold from every new file. A program asks the kernel for a file with a starting mode, usually 666 for a file (read and write for all, never execute) or 777 for a directory; the umask then clears the bits it names. On an Ubuntu 26.04 login the umask is 0002, which withholds only write from others. umask on its own prints the current value; then watch what new files get:
The file is 664 and the directory 775: the owner and group may write, others may only read. This is safe on Ubuntu because of user private groups. When useradd makes an account, it also makes a group of the same name whose only member is that user, and that becomes the primary group. A umask of 0002 therefore opens a new file to a group that contains only you, unless you deliberately place the file in a shared group's directory.
Root uses 0022, and so does a normal login on RHEL 10, which does not apply the user-private-group rule to the umask. (On Ubuntu the login umask comes from /etc/login.defs and the PAM login stack; the hardening course shows where to change it.) Setting the umask in the shell shows the difference:
A umask of 0022 gives files 644 and directories 755, the classic default that makes new files world-readable. On a server holding other people's data, 0027 is a quiet, real improvement: it withholds write from the group and every permission from others, so new files come out 640 and nothing is world-readable by accident. A umask set in a shell lasts only for that shell and the programs it starts.
Services are a separate case. A service started by systemd does not inherit anyone's login umask: it gets the UMask= setting of its unit, which is 0022 unless the unit says otherwise.
So setting 0027 in your ~/.profile makes the files you create private, but not the files a web server or a backup service writes. The hardening course sets UMask= per service.
The failures permissions produce
Two kinds of refusal come straight from permissions. The first is a file you may not read:
With mode 000 even the owner is refused, and cat reports Permission denied and exits with status 1. The owner is not special here: the kernel checks the owner bits, and they are empty. As the owner you can always run chmod to give yourself access back, which is the usual fix. The second is a script without its execute bit:
The file exists and holds a valid script, but without execute permission the kernel refuses to run it, and bash reports Permission denied with status 126 (the status a shell uses for a command that was found but could not run). Adding the execute bit with chmod +x lets it run. This is the daily reason a freshly written script "does nothing".
The mirror image of a readable secret is a writable file the world can rewrite. A world-writable file that a privileged job later runs or trusts is a real risk. You can sweep for them, and the sweep should come back empty on a healthy tree:
-perm -0002 matches any file whose "others" write bit is on. The leading - means "at least these bits": other bits may be set as well. Without it, -perm 0002 would match only a file whose mode is exactly 0002. Set sensitive files to 600 or 640, and never leave a file that a service reads at a mode that lets others write it.
Try this
In a practice directory, create a file and set it to 640 with chmod, then confirm the string reads -rw-r----- with ls -l. Make a directory, put a file in it, and set the directory to 600: check that ls on it still prints the name while cd into it is refused, then set it back to 755. In a new shell (a umask you set earlier lasts until that shell exits), run umask to see your login value (0002 on Ubuntu, 0022 on RHEL), then set it to 0027, create a new file, and confirm it comes out 640. Finally write a one-line script, run it and read the Permission denied and status 126, then chmod +x it and run it again. Remove the practice directory with rm -r when you are done, and open a new shell so the umask is back to normal.
Takeaway
Read the ten characters as type plus three blocks of owner, group, others, and remember that execute on a directory is permission to enter it, not to list it. Set permissions with the narrowest number that still works (640 and 600 for anything sensitive), and let the umask keep new files off the world-readable list rather than fixing each one by hand.