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:

  1. sudo asks 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.
  2. With your identity confirmed, sudo checks the policy file /etc/sudoers to 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).
  3. If sudoers authorizes it, sudo runs 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.
  4. So as not to ask for your password on every line, sudo caches 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, bash can 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 chown almost always requires root privileges while chgrp sometimes does not, and use both to fix a file with the wrong owner;
  • describe exactly what sudo checks when it asks for your password, and read a sudo -l line to know what you are allowed to do and what you are not;
  • identify a case of reflex-sudo just by looking at a file or directory's ls -l output, and fix it with chown -R instead of repeating sudo forever;
  • explain in your own words why sudo -i is preferable to su on a machine several people share.

Resources