In the previous lesson you read app.log and shipments.csv without trouble, but sooner or later you will run into a Permission denied when trying to write to /var/log/veloz or when running your first script from ~/veloz-ops/bin. Permissions are not an administrative obstacle: they are the mechanism that stops a bug in veloz-api from deleting the business data, or any user on the system from reading the payment gateway credentials. In this lesson you will learn to read them, to modify them with judgment, and to leave ~/veloz-ops and /srv/veloz/data properly protected.
Contents
- The user / group / others model
- Reading the output of
ls -lfield by field - What
xmeans on a file and what it means on a directory - Symbolic
chmod - Octal
chmod - Ownership:
chown,chgrp,idandgroups umask: the default permissions- Special bits: sticky, setgid and setuid
sudoand the principle of least privilege- Hands-on case: securing the Veloz Envíos toolkit and data
- The user / group / others model
Every file and every directory in Linux has one owner (a user) and one group. From there, permissions are defined for three classes of subject:
| Class | Letter | Who it is |
|---|---|---|
| User (user) | u |
The owner of the file |
| Group (group) | g |
The members of the group assigned to the file |
| Others (others) | o |
Every other user on the system |
And for each class there are three permissions:
| Permission | Letter | On a file | On a directory |
|---|---|---|---|
| Read | r |
See the content | List the names it contains |
| Write | w |
Modify the content | Create, delete and rename entries |
| Execute | x |
Run it as a program | Traverse it to reach what is inside |
The evaluation rule is important and surprises a lot of people: the system checks only the first class that matches. If you are the owner, the u permissions apply and the g and o ones are never looked at, even if they were more permissive. A file with permissions ----rwxrwx is unreadable for its owner and accessible to everyone else.
- Reading the output of
ls -l field by field
ls -l field by fieldThe fields, in order, are: type and permissions (-rw-r-----), number of hard links (1, a topic for 05-01), owner (veloz), group (veloz), size in bytes (21504), last modification date and name. The interesting part is the first ten characters:
- Type
-: regular file. It would bedfor a directory andlfor a symbolic link. u=rw-: the uservelozcan read and write, but not execute.g=r--: the members of thevelozgroup can only read.o=---: the rest of the system cannot even open it.
That is exactly the design we want for business data: the application writes, the operations team reads, nobody else sees anything.
- What
x means on a file and what it means on a directory
x means on a file and what it means on a directoryThis distinction is the number one source of confusion with permissions.
On a file, x means "the system may attempt to run it as a program". Without x, a perfectly written script fails with bash: ./daily-report.sh: Permission denied.
On a directory, x means something completely different: permission to traverse it. Without x on a directory you cannot cd into it, nor reach anything inside it, not even files you do have read permission on.
From that, three combinations follow that are worth being clear about:
| Directory permissions | What you can do |
|---|---|
r-- |
List the names with ls, but not see details or go in |
--x |
Go in and open files if you know their exact name, but not list |
r-x |
The normal case: list and access |
The --x combination is the one websites use for configuration directories: you can get in if you know the path, but you cannot browse. And the most useful practical consequence: if a read permission on a file does not work, check the x on every directory along the path. It only has to be missing on one for the whole path to be inaccessible.
- Symbolic
chmod
chmodchmod (change mode) modifies permissions. In its symbolic form it reads almost like a sentence: chmod [who][operator][permissions] file.
| Who | Operator | Permissions |
|---|---|---|
u user, g group, o others, a all |
+ add, - remove, = set exactly |
r, w, x |
chmod u+x ~/veloz-ops/bin/daily-report.sh # make it executable for the owner
chmod go-w /srv/veloz/data # remove write from group and others
chmod a=r /srv/veloz/data/archive/*.csv # everyone read ONLY (sets, does not add)
chmod u+rw,go-rwx ~/veloz-ops/etc/veloz-ops.conf # several rules separated by commasThe difference between + and = is crucial: +r adds read while keeping everything else, whereas =r sets that class's permissions to exactly read, removing write and execute if it had them. When you want a deterministic state, use =.
chmod accepts -R to apply recursively, but use it carefully: chmod -R 755 over a tree turns data files that should not be executable into executables. Later on, with find (05-01), you will learn to apply different permissions to files and directories in a single pass.
- Octal
chmod
chmodThe octal form is the one you will see in documentation and scripts, because it is compact and deterministic. Each permission has a numeric value and they add up:
r is worth 4, w is worth 2 and x is worth 1. Adding those values gives the eight possible digits:
| Digit | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
|---|---|---|---|---|---|---|---|---|
| Permissions | --- |
--x |
-w- |
-wx |
r-- |
r-x |
rw- |
rwx |
You write three digits, one per class: user, group, others. The cases that cover 95 % of practice:
| Octal | Symbolic | Typical use |
|---|---|---|
644 |
rw-r--r-- |
Normal files: data, logs, documents |
755 |
rwxr-xr-x |
Scripts and directories: executable by everyone |
600 |
rw------- |
Files with secrets: keys, credentials |
700 |
rwx------ |
Private directories and personal scripts |
640 |
rw-r----- |
Data the group reads and nobody else sees |
A quick mental rule for converting: think of rwx as three switches worth 4, 2 and 1. rw- = 4+2 = 6; r-x = 4+1 = 5; r-- = 4.
- Ownership:
chown, chgrp, id and groups
chown, chgrp, id and groupsBefore changing ownership, check who you are:
id shows your user, your primary group and all the secondary groups you belong to. groups gives the short version. The fact that joan is in the veloz group is what lets him read the 640 files owned by veloz.
sudo chown veloz /srv/veloz/data/shipments.csv # change the owner
sudo chgrp veloz /srv/veloz/data/shipments.csv # change the group
sudo chown veloz:veloz /srv/veloz/data/shipments.csv # both at once
sudo chown -R veloz:veloz /srv/veloz/data # recursiveTwo important notes:
- Changing the owner requires
root. A normal user cannot "gift" a file to someone else, because that would allow disk quotas to be bypassed and files to be slipped into other people's space. - Changing the group can be done by the owner, as long as he himself belongs to the destination group.
Adding a user to a group is done with sudo usermod -aG veloz joan. The -a (append) is mandatory: without it, usermod replaces the list of secondary groups instead of adding to it, a classic mistake that leaves someone locked out of sudo. Group changes do not affect sessions that are already open: you have to log out and log back in.
umask: the default permissions
umask: the default permissionsWhen you create a file, why does it come out with 644 and not 777? Because the system starts from a base set of permissions and subtracts the bits indicated in the umask.
The base permission is 666 (rw-rw-rw-) for files and 777 for directories; the umask turns off bits on that base (it does not subtract arithmetically). Notice that files are never born executable: their base does not include x. That is deliberate, and that is why you have to run chmod +x explicitly on every new script.
umask |
Files | Directories | Scenario |
|---|---|---|---|
022 |
644 | 755 | The default on most systems |
002 |
664 | 775 | Teams working on a shared directory |
077 |
600 | 700 | Machines with sensitive data: nobody else sees anything |
You change it with umask 077, and to make it permanent you add it to ~/.bashrc (lesson 01-02). Careful: the umask only affects files created afterwards; it does not modify existing ones.
- Special bits: sticky, setgid and setuid
Besides the nine rwx bits there are three special bits, which show up as a fourth octal digit on the left.
Sticky bit (1000) — On a directory that is writable by everyone, it stops one user from deleting another's files. It is what protects /tmp:
ls -ld /tmp shows drwxrwxrwt. That trailing t is the sticky bit: without it, any user could delete everyone else's temporary files. It is applied with chmod 1777 dir or chmod +t dir.
Setgid (2000) — On a directory, it makes everything created inside inherit the directory's group instead of the primary group of whoever creates it. It is the key piece of shared directories:
The s in the position of the group's x indicates setgid. From now on, any CSV that veloz-api or any member of the team leaves there will belong to the veloz group and will be readable by the whole team with no manual intervention.
Setuid (4000) — On an executable, it makes it run with the privileges of its owner, not of whoever launches it. It is how passwd can modify /etc/shadow while you are a normal user.
Serious warning:
setuidis a first-order attack vector. Aroot-owned setuid executable with a programming bug hands over the entire system. Never set setuid on a shell script —many systems ignore it precisely for that reason— and do not apply it to your own programs unless you know exactly what you are doing. Defense in depth is covered in 08-03.
sudo and the principle of least privilege
sudo and the principle of least privilegesudo runs one specific command with root privileges, leaving a record in /var/log/auth.log of who did what and when. That trail is the main reason it is preferred over logging in as root.
The principle of least privilege says that every process and every person should have exactly the permissions they need, not one more. Translated into your day-to-day:
- Always work as a normal user; use
sudoonly for the command that requires it. - Avoid
sudo su -and long-running root sessions: you lose the audit trail and the safety net disappears. - Solve access problems with groups, not by handing out
sudo. Operations being able to read/srv/veloz/datais a question ofvelozgroup membership, not of administrator privileges. - Be suspicious of
chmod 777. It is the quick answer to aPermission deniedand almost always the wrong one: it leaves the file writable by any process on the machine. The correct solution is to adjust the owner or the group.
- Hands-on case: securing the Veloz Envíos toolkit and data
# 1. The script must be executable by its owner and readable by the rest
chmod 755 ~/veloz-ops/bin/daily-report.sh
# 2. The configuration will contain gateway credentials: owner only
chmod 600 ~/veloz-ops/etc/veloz-ops.conf
# 3. The data directory belongs to the veloz group and inherits it (setgid)
sudo chgrp -R veloz /srv/veloz/data
sudo chmod 2775 /srv/veloz/data
sudo chmod 640 /srv/veloz/data/shipments.csv
# 4. Check the result
ls -ld /srv/veloz/data && ls -l /srv/veloz/data/shipments.csvdrwxrwsr-x 3 root veloz 4096 Aug 3 11:32 /srv/veloz/data -rw-r----- 1 veloz veloz 21504 Aug 3 09:14 /srv/veloz/data/shipments.csv
The reasoning behind each decision:
755on the script: it needsxto run. It holds no secrets, sor-xfor group and others is harmless and makes it easy for a colleague to audit it.600on the configuration: as soon as it stores an API key, any read permission for "others" is equivalent to publishing it.600is non-negotiable on files with credentials.2775on the directory:775gives thevelozgroup control over the content; the leading2(setgid) guarantees that new files inherit that group automatically.640on the CSV: the owner (the application) writes, the team reads, nobody else sees the business data.
Common Mistakes and Tips
- Answering a
Permission deniedwithchmod 777. It works, and it opens a hole. Ask yourself first who should have access and fix it with owner or group. - Forgetting the
xon the directories along the path. Ifcat /srv/veloz/data/shipments.csvfails despite you having read permission on the file, checkls -ld /srvandls -ld /srv/veloz. - Using
chmod -R 755over a mixed tree. It turns data files into executables. Apply different permissions to files and directories (withfind, 05-01). - Confusing
+rwith=r. The first adds, the second sets the exact state. usermod -Gwithout-a. It replaces the secondary groups instead of adding and can lock you out ofsudo.- Expecting a group change to take effect immediately. You have to log out and log back in.
- Leaving a credentials file at
644. It is the most frequent security flaw and the easiest one to avoid.
Exercises
Exercise 1 — Reading permissions. Interpret these three ls -l lines by saying, for each one, what the owner can do, what the group can do and what everyone else can do, and express its permissions in octal:
-rwxr-x--- 1 joan veloz 512 Aug 3 11:20 daily-report.sh drwxrwsr-x 3 root veloz 4096 Aug 3 11:32 data -rw------- 1 joan joan 84 Aug 3 11:22 veloz-ops.conf
Exercise 2 — The script that will not start. You have created ~/veloz-ops/bin/shipment-summary.sh with an editor and when you launch it you get Permission denied. ls -l shows -rw-rw-r--. Explain the cause, fix it in two ways (symbolic and octal) and justify why the file was born without x.
Exercise 3 — Designing the permissions of a shared directory. The operations team (group veloz, three people) needs a /srv/veloz/reports directory where everyone can create reports and read everyone else's, but where nobody can delete somebody else's report and where users outside the group see nothing. Write the commands and explain each bit.
Solutions
Solution to Exercise 1
| File | Octal | Owner | Group | Others |
|---|---|---|---|---|
daily-report.sh |
750 |
rwx: reads, modifies and executes |
r-x: reads and executes, does not modify |
---: nothing |
data |
2775 |
rwx: lists, creates and enters |
rws: the same, and inherits the group (setgid) |
r-x: lists and enters, does not create |
veloz-ops.conf |
600 |
rw-: reads and writes |
---: nothing |
---: nothing |
On data, the s takes the place of the group's x: the x is still active (if it were not, you would see an uppercase S) and on top of that there is the setgid bit, which in octal is the leading 2.
Solution to Exercise 2
chmod u+x ~/veloz-ops/bin/shipment-summary.sh # symbolic: the bare minimum
chmod 755 ~/veloz-ops/bin/shipment-summary.sh # octal: executable by the team too
ls -l ~/veloz-ops/bin/shipment-summary.shCause: the file has no x bit in any class, so the kernel refuses to run it, even though its content is a valid script. Why it was born without x: files are created with base permissions 666 (rw-rw-rw-), with no execute in any case, and the umask 022 additionally removes write from group and others, leaving 644. The system never marks a file as executable on its own: it is a conscious decision that a person has to take, and that is precisely the protection.
Solution to Exercise 3
sudo mkdir -p /srv/veloz/reports
sudo chgrp veloz /srv/veloz/reports
sudo chmod 3770 /srv/veloz/reports
ls -ld /srv/veloz/reportsBreakdown of 3770:
770: owner and thevelozgroup haverwx(list, create, enter); others have nothing, which meets the invisibility requirement for outside users.3=2+1, that is, two special bits at once:2(setgid): the reports created inside will belong to thevelozgroup regardless of the primary group of whoever creates them, so the whole team can read them.1(sticky): even though everyone haswon the directory, each user can only delete or rename their own files. It is the same mechanism that protects/tmp.
The trailing uppercase T indicates a sticky bit without x permission for others; if others had x you would see a lowercase t. This directory is the canonical example of how the special bits solve a collaboration requirement that the nine rwx bits cannot express.
Conclusion
You can now read an ls -l line field by field, tell apart the role of the x bit on files and on directories —the hidden cause of so many "permission denied" messages— and modify permissions in both symbolic and octal form, with the four patterns that solve almost everything: 644, 755, 600 and 700. You control ownership with chown and chgrp, you know how to check your groups with id, you understand why the umask decides the permissions of every new file, and you know the three special bits, including the setgid that makes a real shared directory work. And above all, you have the judgment: least privilege, groups before sudo, and never 777.
~/veloz-ops is left with its script executable and its configuration locked down to 600, and /srv/veloz/data with the veloz group and automatic inheritance. Defense in depth —command injection, safe temporary files, secret management— is tackled in 08-03.
In lesson 02-04 comes the piece that multiplies everything you have learned: redirection and pipes. You will learn to separate normal output from errors, to save results into files, to discard noise with /dev/null and to chain the Module 2 filters into pipelines that answer complex questions about access.log in a single line.
Bash Programming Course
Module 1: Introduction to Bash
- What Is Bash?
- Setting Up Your Environment
- Basic Command Line Navigation
- Understanding the Shell
- Finding Help: man, help and --help
Module 2: Basic Bash Commands
- File and Directory Operations
- Text Processing Commands
- File Permissions and Ownership
- Redirection and Piping
- Wildcards and Path Expansion
- History and Keyboard Shortcuts
Module 3: Scripting Fundamentals
- Creating and Running a Script
- Variables and Constants
- Basic Operators
- Conditional Statements
- Arguments and User Input
- Quoting, Expansion and Substitution
Module 4: Intermediate Scripting
- Loops in Bash
- Functions in Bash
- Arrays and Associative Arrays
- String Manipulation
- The case Statement and Interactive Menus
- Arithmetic and Numeric Calculations
Module 5: Advanced Scripting Techniques
- Advanced File Operations
- Process Management
- Error Handling and Debugging
- Regular Expressions
- Advanced I/O: Descriptors and Here-Documents
- Modular Scripts and Reusable Libraries
Module 6: Working with External Tools
Module 7: Automation and Scheduling
- Cron Jobs
- Automating Tasks
- Backup and Restore Scripts
- Monitoring and Logging
- Services and Timers with systemd
- Remote Automation with SSH
Module 8: Best Practices and Optimization
- Writing Readable Code
- Optimizing Bash Scripts
- Security Considerations
- Version Control with Git
- Static Analysis with ShellCheck and shfmt
- Automated Testing with Bats
- Portability: POSIX sh versus Bashisms
