Module 4: Permissions, Processes, and Environment
4. Ownership, sudo, and root: dangerous power
Description
By the end of this lesson you will be able to change a file's owner and group with chown
and chgrp, use sudo to borrow root's privilege for a single command without settling in to
live there, read exactly what you are allowed to do in sudoers with sudo -l, and inspect a
script before running it with privileges instead of blindly trusting a curl | sudo bash. And,
above all, you will recognize the exact moment sudo stops solving the problem and starts
manufacturing a new one you are going to inherit three days from now.
This is exactly what separates someone who installs a dependency with sudo on instinct from
someone who understands what they just authorized. A Docker container writing files into a
mounted volume, a deployment script that once ran with too many privileges, a coworker who
"fixed" a Permission denied by adding sudo to the front of the line: all three leave files
with the wrong owner, and that file is going to fail again — for someone else, at another
moment — until someone understands why.
Connection to the module: in the previous lesson you learned to read and change a file's
permissions with chmod — what each class of user can do with something that already
exists. Today you answer an earlier and more dangerous question: who owns that file, and
what happens when you need to act with another user's authority — typically root — for one
specific task. chmod is adjusting the rules of your own house. chown and sudo are, each
in its own way, asking a higher authority for permission to change something that is not
entirely yours.
Ownership: why you cannot give away a file all by yourself (chown and chgrp)
Think about a car's title. chmod's permissions are like lending someone the keys: you decide
who can drive it today, and you can change your mind tomorrow with nobody else involved. But
transferring the title — saying the car is no longer yours, but someone else's — is not
something you decide alone with a form you fill out: it goes through an authority that
registers the change, precisely so nobody can give away cars that are not theirs or hide who a
vehicle really belongs to. A file's ownership in Unix works the same way: anyone with write
permission can modify a file, but deciding that file now belongs to another user is a change
of a different order, and that is why it almost always requires root privileges.
chown (change owner) changes a file's owner; chgrp (change group) changes its group.
The syntax:
chown ana file.txt # changes only the owner
chown ana:staff file.txt # changes owner and group at once
chown :staff file.txt # changes only the group (equivalent to chgrp staff)
chgrp staff file.txt # changes only the group
The reason a regular user cannot give away one of their files to someone else is not
arbitrary: if you could, you could dodge your account's disk quota by transferring your heavy
files to another user, or pass off a file as if someone else had created it. That is why
chown almost always requires root privileges. chgrp is a bit less strict: you can change a
file you already own's group, but only to a group you yourself are a member of — you cannot
assign your file to just any group you do not belong to. The asymmetry makes sense: switching
between groups you already belong to dodges no quota and hides no authorship; giving away the
entire file does.
Worked example
A deployment script ran last night with sudo and left a log file with the wrong owner. You
confirm it:
ls -l deploy.log
What to expect:
-rw-r--r-- 1 root staff 4096 Jul 20 23:58 deploy.log
The file belongs to root, not you. You try to fix it directly:
chown ana deploy.log
What to expect:
chown: deploy.log: Operation not permitted
Makes sense: changing the owner of a file that is not yours is exactly the operation a regular user cannot do alone. You need to borrow root's privilege for this one task:
sudo chown ana:staff deploy.log
It asks for your password — yours, not root's — and prints nothing if everything went fine. You verify:
ls -l deploy.log
What to expect:
-rw-r--r-- 1 ana staff 4096 Jul 20 23:58 deploy.log
The file is yours again. From here on you can read, edit, or delete it with no need for sudo
again for this particular file.
sudo: borrowed privilege for one command, not a master key
Imagine a building with a restricted room. Instead of giving you a master key that opens any
door indefinitely, there is a guard who checks your credential against a list — this person
can enter this specific room, at these hours, for this task — and lets you through only for
that visit. You finish, you leave, and the next time you want to enter, the guard checks the
list again. That is sudo: it does not hand you the building's keys, it walks you to a
specific door, verifies you are authorized, and lets you do one thing with privileges you
normally do not have.
Specifically, when you type sudo some-command, this is what happens:
sudoasks you for your own password — your user's, not root's. This confirms it really is you sitting at the keyboard right now, not that you know root's secret.- With your identity confirmed,
sudochecks the policy file/etc/sudoersto see whether your user (or a group you belong to) has permission to run that specific command, as that specific target user (normally root, but not always). - If
sudoersauthorizes it,sudoruns only that command with elevated privileges, and the moment it finishes, you go back to being your normal user for the next line you type. - So as not to ask for your password on every line,
sudocaches your authentication by default for five minutes, tied to that specific terminal. That is why sometimes it asks for your password and sometimes it does not: it depends on how long ago you last used it in that same terminal.
The /etc/sudoers file never gets edited with just any editor: it gets edited with
visudo, which validates the syntax before saving. A syntax error in a hand-edited
sudoers can leave the file unusable — and with it, everyone unable to use sudo, including
you — until someone repairs it from a recovery mode. visudo exists precisely so that does not
happen.
You can see exactly what you are allowed to do, with no guessing, with sudo -l.
Worked example
sudo -l
What to expect, on a machine where your user has full access:
Matching Defaults entries for ana on webserver01:
env_reset, mail_badpass,
secure_path=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
User ana may run the following commands on webserver01:
(ALL : ALL) ALL
(ALL : ALL) ALL reads right to left: you can run any command (ALL), as any user (the
first ALL in parentheses), and any group (the second ALL). It is root-equivalent access,
just gated behind your own password.
Compare it to a deliberately narrower setup, typical of a shared server where nobody needs full access:
User diego may run the following commands on webserver01:
(root) NOPASSWD: /usr/bin/systemctl restart nginx, /usr/bin/systemctl status nginx
Here diego can restart and check nginx's status as root, with no password prompt
(NOPASSWD) precisely for those two commands — not one more. If diego tries sudo apt install something, sudoers rejects it even though he technically knows how to invoke
sudo. This is sudo working as intended: it is not "all or nothing," it is a surgical list
of what each person can do, with what privilege, on which machine.
The reflex-sudo antipattern
This is where the principle of least privilege you already know from the permissions lesson
gets genuinely tested. The pattern is always the same: Permission denied shows up, the
reflex is to prepend sudo to the line, the command "works," and you move on with your day.
The problem is sudo does not solve the symptom without leaving a trace: the command ran as
root, and any file it created or modified now belongs to root. Three days later, when you — or
a coworker, or a CI script running as a regular user — try to touch that same file with no
sudo, it is going to fail with exactly the same Permission denied you thought you had
already solved.
A typical case: you install dependencies with npm install and it fails.
npm install
What to expect:
npm error code EACCES
npm error syscall mkdir
npm error path /home/ana/project/node_modules/lodash
npm error errno -13
npm error Error: EACCES: permission denied, mkdir '/home/ana/project/node_modules/lodash'
The reflex is to repeat the command with sudo:
sudo npm install
And it works: the installation finishes with no errors. But the real problem was never that
npm needed root privileges — it never needs them to install dependencies in your own
project. The real problem is node_modules/ already belonged to root, probably because
someone ran sudo npm install before, again. You confirm the real cause:
ls -ld node_modules
What to expect:
drwxr-xr-x 42 root root 4096 Jul 18 10:02 node_modules
The correct fix is not repeating sudo npm install forever — it is fixing the directory's
ownership, once, and not needing sudo for this project again:
sudo chown -R ana:ana node_modules
npm install
What to expect: the first line prints nothing; the second installs normally, with no
sudo and no errors, because node_modules/ belongs to you again.
The same pattern shows up with sudo nano file or sudo vim file when the file already
belonged to you and was perfectly editable with no sudo — the habit of prepending sudo
"just in case" leaves that file owned by root from that moment on, waiting to break the next
process that tries to write to it as a regular user.
The real risk of curl ... | sudo bash
It is the install instruction you see in half of all projects: one single line, copy and paste, done.
curl -fsSL https://get.example.com/install.sh | sudo bash
That line downloads a script and hands it straight to bash, running as root, with no chance
for you to look at its content before it runs. The concrete risks, not theoretical:
- No content verification at all. You trust completely whatever that server decides to hand you at this exact moment, and you run it with the machine's maximum authority. If the server was compromised, or the domain changed hands, there is no intermediate step protecting you.
- HTTPS protects the channel, not the content. The URL starting with
https://guarantees nobody intercepted the download along the way — but it says absolutely nothing about whether what the server sent you is safe. A compromised server serves the malicious script over a perfectly valid HTTPS connection. - The server can serve different content depending on who is asking. There are documented
techniques where the server detects the request is coming from a piped
curl— instead of a browser or a normal file download — and responds with a different script from the one someone auditing the installer by downloading the file first would see. - An interrupted download can run half a script. If the connection cuts off before the
full script finishes arriving,
bashcan end up running only half of the instructions that made it through — and a destructive instruction cut in half does not always fail safely.
The alternative, using exactly what you already know how to read from module 2, is downloading first and inspecting before running:
curl -fsSL https://get.example.com/install.sh -o install.sh
less install.sh
With less you scan the whole script looking for red flags: another curl | bash hidden
inside that downloads something else, encoded (base64) text blocks that get decoded and run
with no visibility into what they do, or commands touching paths outside what the installer
claims it is going to install. If the project publishes a checksum, you verify it before
trusting the file:
shasum -a 256 install.sh
Only once the content has been reviewed do you run it explicitly — with sudo only if it
genuinely needs it, not out of habit:
bash install.sh
The difference is not distrust for distrust's sake: it is that running something with root privileges should always be a deliberate decision, never an invisible step hidden inside a pipe.
su versus sudo -i: why you do not live inside a root session
Imagine the difference between someone lending you their master credential so you can keep working from the security office for the rest of the day, versus letting you in with your own credential every time you need something specific from that office. The first option erases which user did what once you are inside; the second leaves a record that it was you, on every visit.
su (switch user, with no argument it switches to root) asks for the target user's
password — to su to root you need to know root's actual password. Many Linux distributions,
and macOS by default, do not even have a root password configured, so plain su simply does
not work until someone sets one up — which, right off the bat, is already a sign this is not
the recommended path. Once inside, you have a full shell session as root that lasts until you
explicitly leave with exit.
sudo -i (or sudo -s) does something different: it gives you a shell as root, but using
your own password, checked against sudoers just like any other sudo command. Every time
you use it, it gets logged against your real username, not a generic "someone logged in as
root."
The practical reason for preferring sudo — whether command by command, or with sudo -i
when you genuinely need several operations in a row — over su is twofold: nobody on the team
needs to know or share a root password, and the system logs show exactly who did what, when,
on a machine several people probably share. Living settled into a root session all day is
also, on top of that, the easiest way to forget what privilege level you are standing at and
carelessly run something you thought you were going to run as a regular user.
Common mistakes
1. Thinking sudo "turns you into" root for the rest of the session (conceptual). What
happens: you run sudo mkdir folder, and on the next line you type another command assuming
you are still root, only to find it denies you permission again. Why: sudo elevates
privileges only for the specific command that follows it on that same line; the moment it
finishes, you go back to being your normal user — unless you explicitly entered with sudo -i
or sudo -s. How to spot it: you are surprised by a Permission denied right after a sudo
command that did work. How to fix it: prepend sudo to every command that actually needs it,
or use sudo -i deliberately when you genuinely need to do several tasks in a row as root —
and leave that session the moment you finish.
2. Believing HTTPS in a curl | bash guarantees the script is safe (conceptual). What
happens: someone justifies running a one-line installer by saying "it's safe, the URL uses
HTTPS." Why: HTTPS protects the channel — nobody intercepting or altering the bytes while they
travel from server to your machine — but it says nothing about whether the content the server
decided to send you is trustworthy. A compromised server, or one serving different content
depending on who is asking, delivers the malicious script just the same over a perfectly valid
HTTPS connection. How to spot it: the justification for trusting a script rests solely on the
transport protocol, not on having read a single line of what it does. How to fix it: download
the script to a file, read it with less or cat, and only then run it explicitly — with
sudo only if it genuinely needs it.
3. Editing /etc/sudoers with just any editor instead of visudo. What happens: you open
sudoers directly with nano or vim, make a syntax mistake without noticing, and from that
moment sudo stops working for everyone on that machine — including you. Why: sudoers
controls who has elevated privileges across the whole system; a syntax error can leave it in a
state no user can use sudo to recover control from. How to spot it: after hand-editing
sudoers, any sudo — including sudo -l — starts failing with a syntax error. How to fix
it: always edit sudoers with sudo visudo, which validates the syntax before saving and
rejects the file if it has an error, instead of letting you save a broken version.
Exercises
1. You ran sudo pip install -r requirements.txt inside a virtual environment that
already existed and belonged to you. Now, with no sudo, any pip install new-package fails
with Permission denied. What is the root cause, and what command fixes it without having to
recreate the virtual environment from scratch?
See solution
The root cause is that sudo pip install ran as root, and any file it wrote inside the virtual
environment (the packages, the site-packages folder) ended up owned by root instead of your
user. The fix is giving the entire virtual environment's ownership back to your user,
recursively:
sudo chown -R ana:ana venv/
After this, pip install new-package works again with no sudo, because every file in the
virtual environment belongs to you again.
Why it works: the problem was never that pip needed root privileges — it never needs them
to install packages inside your own virtual environment; the problem was the ownership an
earlier sudo left in the wrong place. Fixing the ownership once avoids repeating sudo on
reflex every time you install something.
2. You run sudo -l on a shared server and see this line:
User diego may run the following commands on webserver01:
(root) NOPASSWD: /usr/bin/systemctl restart nginx, /usr/bin/systemctl status nginx
What exactly can diego do, and what happens if he tries sudo apt update on that same
machine?
See solution
diego can run only those two commands — systemctl restart nginx and systemctl status nginx — as root, and sudoers will not even ask him for a password for those two specific
commands (that is what NOPASSWD means). If he tries any other command with sudo, including
sudo apt update, sudoers rejects it: it is not on the list of allowed commands for his user
on that machine, even though he technically knows how to invoke sudo.
Why it works: sudoers is not an all-or-nothing key — it lets you define exactly which
command, as which user, and with what password requirement, for each person. That is what
makes it possible to grant specific access without handing over full access.
3. You get this install instruction in a new tool's documentation:
curl -sSL https://install.example.dev/setup.sh | sudo bash
Before running it on your work machine, what would you do differently, and why?
See solution
I would download the script to a file first, without running it, and read it before running anything:
curl -sSL https://install.example.dev/setup.sh -o setup.sh
less setup.sh
I would look for red flags — another curl | bash hidden inside, base64-encoded blocks that
get decoded and run, commands touching paths outside what the installer claims it installs. If
the project publishes a checksum, I would verify it against the downloaded file before trusting
it. Only once the content has been reviewed would I run it explicitly (bash setup.sh, with
sudo only if it genuinely needs it).
Why it works: the URL using HTTPS only guarantees nobody intercepted the download along the way — it says nothing about whether the content the server decided to send you is safe, and a compromised server can serve a malicious script over a perfectly valid HTTPS connection. Inspecting before running is the only way to know what you authorized to run with root privileges.
4. A sysadmin on a server with six different engineers having access explicitly forbids
using su and requires sudo -i for any administrative task. What does the team gain from
that rule that they would not have if everyone used su?
See solution
With su, everyone would need to know and share the same root password, and once inside that
session, the system logs would not distinguish which of the six did each action — everything
would show up as "root did this." With sudo -i, every engineer authenticates with their
own password, checked against sudoers, and every invocation gets logged against their real
username.
Why it works: the team gains two things su does not give — nobody needs to know or share
a master password, and there is an auditable record of who ran what as root, instead of an
anonymous session where any of the six could have been responsible.
Summary and next step
You now know how to change a file's owner and group with chown and chgrp, and why that
operation — unlike chmod — almost always requires root privileges. You understand exactly
what happens when you type sudo: it asks you for your own password, not root's; it checks
sudoers to see whether you have permission for that specific command; and it elevates
privileges only for that line, not for the rest of your session. You know how to read with
sudo -l exactly what you are allowed to do, you recognize the reflex-sudo antipattern that
leaves files with the wrong owner waiting to break something down the line, you know how to
inspect a script before handing it to bash with privileges, and you understand why sudo -i
is preferable to living settled into an su session.
Everything you saw in this lesson is ways of borrowing a privilege for a specific task — a
file, a command, an installer. What comes next is what that privilege ends up launching on the
machine: processes. Every command you run, with or without sudo, turns into a live process
with its own identifier, and knowing how to find it, read it, and end it with judgment —
instead of kill -9 as a first resort — is the other half of diagnosing instead of guessing.
Before moving on you should be able to:
- explain why
chownalmost always requires root privileges whilechgrpsometimes does not, and use both to fix a file with the wrong owner; - describe exactly what
sudochecks when it asks for your password, and read asudo -lline to know what you are allowed to do and what you are not; - identify a case of reflex-
sudojust by looking at a file or directory'sls -loutput, and fix it withchown -Rinstead of repeatingsudoforever; - explain in your own words why
sudo -iis preferable tosuon a machine several people share.
Resources
- sudo(8) — Linux manual page — official
reference for
sudo: why it asks for your own password, the five-minute credential cache, and the-l,-i, and-soptions. - sudoers(5) — Linux manual page — full
syntax for the
/etc/sudoersfile, including theNOPASSWDtag and examples of restrictive per-user and per-command rules. - visudo(8) — Linux manual page — why
sudoersalways gets edited withvisudoand never with a direct editor. - chown(1) — Linux manual page — full
syntax for
chown, including theuser:groupvariants and the-Roption. - su(1) — Linux manual page — exactly which
password
suasks for, and the difference betweensuandsu -(login shell). - Friends don't let friends curl | bash — Sysdig — the concrete risks of one-line installers: no verification, interrupted downloads, and content served differently depending on who is asking.