Skip to content

sudo and least privilege

Exercise sudo-and-least-privilege-annex.pdf · PDF

Pau, the intern at Tramuntana Apps, needs to refresh the package lists on a server once a week. Should he get the root password? Should he go into the group sudo, which can do everything?

Neither. The answer to that question is one line of text, and it is fifty years old. In Users and groups on Linux you used sudo as a magic word. Today you open it and see how to give someone exactly one command — and nothing more.

What you will learn

  • State the principle of least privilege in one sentence, with an example
  • Read a line of /etc/sudoers: who, on which machine, as whom, which commands
  • Check what a user may do with sudo -l
  • Write rules for a user and for a group with visudo, and test what is allowed and refused
  • Why visudo is used instead of a normal editor
  • The trap: how a "small" rule can be the master key in disguise

Why sudo asks for your password

When you type sudo whoami, sudo asks for your password, not root's. Three good reasons:

  • Nobody has to share root's password. When a colleague leaves the company, nothing changes for everybody else.
  • The log says who did it, not just "root".
  • It proves the person at the keyboard is still you, not someone who found your session open.

A little history

sudo is older than you might think. It was written around 1980 by Bob Coggeshall and Cliff Spencer at the State University of New York at Buffalo, because handing the root password to every student helper was a bad idea.

Five years earlier, in 1975, Jerome Saltzer and Michael Schroeder at MIT had written down the rule that sudo puts into practice:

"Every program and every user of the system should operate using the least set of privileges necessary to complete the job."

That is the principle of least privilege. It is still the first rule of security.

One key for one door

Back in the kitchen. The kitchen manager (the operating system) keeps a key cabinet. The intern who takes out the rubbish gets the key of the back door, not the master key. If the intern loses it — or goes mad — the damage stops at the back door.

Accounts work the same way. The question is never "do I trust Pau?". It is "what does Pau's job need?"

Who Job Key they get
pau (intern) Refresh the package lists apt update, nothing else
The dev team Restart the scheduler systemctl restart cron
student (you, the admin) Everything All the keys

Reading the sudoers file

/etc/sudoers is the file where sudo reads who may run what, as whom.

Print it without comments and empty lines:

sudo grep -vE "^#|^$" /etc/sudoers

Among the output, three lines matter:

root    ALL=(ALL:ALL) ALL
%sudo   ALL=(ALL:ALL) ALL
@includedir /etc/sudoers.d

Cut the second line into four pieces:

Piece Question Here
%sudo Who? % means a group: every member of sudo. Without %, a single user
ALL= On which machine? Any (the same file can be shared by many machines)
(ALL:ALL) As whom? As any user and any group — root included
ALL Which commands? All of them

So the "magic" group sudo is just one line of text. The last line, @includedir, tells sudo to also read every file in the folder /etc/sudoers.d/. That is where your own rules go.

A narrow rule is written the same way, with a full path instead of the last ALL:

pau   ALL=(root) /usr/bin/apt update

"Pau, on any machine, as root, may run /usr/bin/apt update." Nothing else.

flowchart LR
    CMD["<b>pau types</b><br>sudo apt install cowsay"] --> PW["<b>sudo asks</b><br>pau's own password"]
    PW --> RULES["<b>sudo reads the rules</b><br>/etc/sudoers + sudoers.d"]
    RULES -->|"a rule matches"| RUN["<b>Runs as root</b><br>and writes it in the log"]
    RULES -->|"no rule matches"| NO["<b>Sorry, user pau is not allowed</b><br>and writes that in the log too"]

Careful: “If I can use sudo, I am root”

You are whoever the rule says, for the commands the rule lists. In deb-lab your rule happens to say ALL. Pau's will say one command.

What may I do? sudo -l

sudo -l

The last line for student in deb-lab is:

    (ALL : ALL) ALL

That is the %sudo rule at work. An administrator can also ask about another user without becoming them, with -U:

sudo -l -U joan
User joan is not allowed to run sudo on deb-lab.

Writing rules safely with visudo

Never open /etc/sudoers with a normal editor. Use visudo: it opens the file in an editor and checks the grammar before saving. Why does this matter so much? With a broken sudoers file, sudo refuses to work for everybody — and sudo is exactly what you would need to repair it.

Create a separate file for the company's rules:

sudo visudo -f /etc/sudoers.d/tramuntana

Type the rules (visudo usually opens nano: save with Ctrl+O, Enter, leave with Ctrl+X):

# Tramuntana Apps - least privilege
pau   ALL=(root) /usr/bin/apt update
%dev  ALL=(root) /usr/bin/systemctl restart cron

Then check every file:

sudo visudo -c

It must print parsed OK for /etc/sudoers and for /etc/sudoers.d/tramuntana.

When visudo says “What now?”

If you make a typo, visudo shows syntax error with the line number and asks What now?. Type e to edit again. Never type Q: it saves the broken file anyway.

Three things that trip everybody

  • Full paths. apt update without /usr/bin/ is a syntax error. which apt tells you the path.
  • No dots in the file name. Files in /etc/sudoers.d/ whose name contains a dot are ignored: tramuntana, not tramuntana.conf.
  • A safety net. Before you start, open a second terminal with sudo -i and leave it open. If the file ends up broken, delete it from there.

Test as the intern

Become Pau and look at the prompt: it must say pau@deb-lab.

su - pau
sudo -l

Pau sees two commands: (root) /usr/bin/apt update from his own line, and (root) /usr/bin/systemctl restart cron because he is in the group dev. Now try:

sudo apt update
sudo apt install cowsay

The first one works. The second one answers:

Sorry, user pau is not allowed to execute '/usr/bin/apt install cowsay' as root on deb-lab.

sudo whoami gets the same refusal. One key, one door. Notice that sudo asked for Pau's password: he is the one asking.

Who did what?

Every use of sudo, allowed or refused, is written to the system log:

sudo journalctl -t sudo -n 10

Each line shows the user, the folder and COMMAND=.... Pau's refused attempts say command not allowed.

Think before you grant

The boss asks: "Aina should be able to create accounts for new employees." Someone proposes:

aina  ALL=(root) /usr/sbin/adduser

Looks harmless. But with it, Aina can run sudo adduser aina sudo, log in again, and she is in the group sudo: full control. One "small" permission was the master key in disguise.

The same goes for any program that can change users, edit any file or start a shell: adduser, nano, find... Before granting a command, ask: "what is the worst thing this program can do as root?"

The guard can have holes too

In January 2021 the security company Qualys published a bug in sudo itself, known as Baron Samedit (CVE-2021-3156). It had been in the code since July 2011. With it, any local user could become root — even one with no rule at all. That is why systems must be updated.

Least privilege is not about trusting people less. It is about making every mistake — yours, theirs, the program's — as small as possible.

Key takeaways

  • The principle of least privilege (Saltzer and Schroeder, 1975): give each account only what its job needs.
  • sudo asks for your password, so root's is never shared and the log records who did what.
  • A sudoers rule answers four questions: who (% = group), on which machine, as whom, which commands (full paths).
  • Write your rules in /etc/sudoers.d/ with visudo, which checks the grammar; sudo -l (and sudo -l -U user) shows what is allowed.
  • A rule for one "small" program can be a master key in disguise if that program can change users, edit files or open a shell.

Quick check

1. In %dev ALL=(root) /usr/bin/systemctl restart cron, what does the % mean?
  • The line is a comment and is ignored
  • The name dev is a group, not a user
  • The name dev is the name of a machine
  • The command runs without asking a password

In sudoers, % in front of a name means a group: every member of dev gets the rule. Without % the rule is for a single user. Comments start with #.

2. Why are the rules edited with visudo and not directly with nano?
  • Because nano cannot open files in that folder
  • Because visudo does not ask for any password
  • Because visudo saves the file much faster
  • Because visudo checks the grammar before saving

visudo refuses to save a file with a syntax error. A broken sudoers file stops sudo for everybody — and then you cannot use sudo to repair it.

3. What does the principle of least privilege say?
  • Give each account only what its job needs
  • Give nobody any rights beyond reading files
  • Give everybody sudo, but keep a full log
  • Change every password at least once a month

The principle of least privilege, written by Saltzer and Schroeder in 1975: every user and program should work with the smallest set of rights that does the job. Mistakes and attacks then stay small.

4. Why does sudo ask for your own password instead of root's?
  • Because root has no password on Debian
  • So root's password is never shared and the log shows who acted
  • Because your password is longer than root's password
  • So that the command runs as you instead of as root

Asking for your password means nobody needs root's, so nothing changes when someone leaves. The log records your name, and it proves you are still the person at the keyboard. The command itself still runs as root.

5. Pau has only the rule pau ALL=(root) /usr/bin/apt update. What happens with sudo apt install cowsay?
  • It works, because apt is in his rule
  • It works, but only after asking root's password
  • It is refused and the attempt is written to the log
  • It installs the package only for the user pau

The rule lists the exact command apt update. Anything else, including apt install, gets Sorry, user pau is not allowed to execute..., and journalctl -t sudo shows the refusal with command not allowed.

6. Joan must restart the print service cups from time to time. What is the best solution?
  • Add joan to the group sudo
  • Tell joan the root password
  • joan ALL=(root) /usr/bin/systemctl restart cups
  • joan ALL=(ALL:ALL) ALL

Least privilege: one key for one door. The rule lets Joan restart cups and nothing else. The group sudo, the root password and (ALL:ALL) ALL all give her the master key.

7. A colleague proposes aina ALL=(root) /usr/sbin/adduser so Aina can create accounts. Why is it dangerous?
  • Because adduser can only be run by root itself
  • Because the rule is missing the % in front of aina
  • Because Aina could add herself to the group sudo
  • Because adduser deletes the old accounts first

adduser can also put users into groups: sudo adduser aina sudo makes her a full administrator. A rule for a "small" program can be the master key in disguise. Always ask what the worst thing the program can do as root is.

You now control who may borrow root, and for what. Next, from files and accounts to what is running: Processes.