Skip to content

Least Privilege

Every account, process, and service on a server needs some access to do its job — but "some" and "as much as is convenient to grant" are very different amounts. The principle of least privilege says a subject should have exactly the access it needs to perform its function, and nothing more. It sounds obvious stated that way; in practice it's the single principle most often abandoned under time pressure, because granting broad access is faster than scoping it correctly, right up until something goes wrong.

This article doesn't re-derive the mechanics of file permissions, ACLs, or SELinux contexts — those are covered in depth in File Permissions, File Permission Security, and SELinux and AppArmor. What it does is connect those mechanisms to the single principle behind nearly every article in this module, and explain why "least privilege" and "convenient" are usually in tension.

What You Will Learn

By the end of this article you will be able to:

  • state the principle of least privilege and explain what "blast radius" means in this context;
  • identify the four places least privilege gets applied on a Linux server: accounts, processes, the file system, and the network;
  • explain why least privilege is enforced by default deny, not by removing access after the fact;
  • recognize the specific trade-off that causes least privilege to erode over time in practice.

Blast Radius: The Practical Reason This Matters

The value of least privilege isn't abstract — it's about limiting blast radius: the amount of damage a single compromised credential, process, or account can do. If a web application's service account can only read its own configuration and write to its own upload directory, an attacker who exploits a vulnerability in that application inherits exactly those two capabilities — not the ability to read every other user's home directory, not sudo, not access to the database server on the same host. If that same service ran as root "because it was simpler to set up," the same vulnerability now means full compromise of the machine.

ps -eo user,pid,cmd | grep nginx
www-data   1842 nginx: worker process
www-data   1843 nginx: worker process
root       1840 nginx: master process

The master process needs root briefly, to bind to port 80/443 and then drop privileges; the worker processes that actually parse untrusted client input run as the unprivileged www-data account. This is least privilege applied at the process level, and it's why a web server compromise rarely means an immediate root shell — the worker process simply doesn't have one to hand over.

The Four Places Least Privilege Applies

Layer Mechanism Where it's covered
Accounts Service accounts with no login shell, scoped group membership Users and Groups
Privilege escalation sudo rules scoped to specific commands, not ALL sudo Security, Root and sudo
File system Ownership, mode bits, ACLs, mandatory access control File Permission Security, SELinux and AppArmor
Network Firewall rules that default-deny and open only what's needed Firewall and UFW, iptables and nftables

The pattern repeats at every layer: default to no access, then grant exactly what's needed, rather than granting broad access and trying to remember to narrow it later. This is worth stating explicitly, because the second approach — starting broad and narrowing — almost never actually happens once the system works.

Default Deny, Not Selective Removal

A firewall configured to allow all outbound and inbound traffic except a list of blocked ports is not applying least privilege — it's applying a blocklist, which only stops threats someone already thought to name. A firewall that denies everything by default and explicitly allows only the ports a service actually needs (covered in Firewall and UFW) is applying least privilege, because anything not explicitly permitted is refused, including things nobody thought to block.

sudo ufw status verbose
Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), disabled (routed)

The same logic applies to sudoers: a rule like deploy ALL=(ALL) NOPASSWD: ALL grants everything and relies on the account holder's good judgment to not misuse it; a rule like deploy ALL=(www-data) NOPASSWD: /usr/bin/systemctl restart myapp grants exactly one action, and anything else the deploy account might want to do requires a separate, deliberate grant. sudo Security covers scoping sudoers rules this precisely.

Interview angle: least privilege vs. usability

An interviewer might ask why least privilege isn't simply the default everywhere, given how clearly beneficial it is. The honest answer is that it has a real cost: scoping a sudo rule to one command takes longer than granting ALL, and it needs revisiting every time the job actually changes — a narrowly scoped rule that no longer covers a legitimate new task will visibly break, forcing someone to update it, whereas an overly broad rule silently continues to "work" no matter what changes. Naming this trade-off explicitly, rather than pretending least privilege is free, is what separates a real answer from a slogan.

Where Least Privilege Erodes in Practice

Three patterns account for most of the drift away from least privilege on a running server:

  • "Just for now" grants that outlive their reason. A developer is given temporary root access to debug a production issue, and the grant is never revoked once the issue is fixed.
  • Broad grants used to avoid diagnosing a permission error. chmod 777 or sudo ALL=(ALL) NOPASSWD: ALL "fixes" the immediate symptom without anyone ever determining what access was actually missing — a pattern covered directly in File Permission Security.
  • Accounts and grants that survive past their purpose. A contractor's account, a temporary firewall rule opened for a one-time data migration, a sudo rule written for a service that has since been decommissioned — each one remains a live grant with no active justification.

The common thread is that granting access is a single, visible action, while removing access once it's no longer needed requires someone to notice it's stale — which rarely happens without a deliberate, recurring audit. Permission Audit, later in this module, builds exactly that recurring process.

Practical Scenario: Scoping a Deployment Account

Problem. A CI/CD pipeline needs to restart the myapp service on a server after every deployment. The account it uses currently has full sudo access, granted early on "to avoid permission issues while setting things up."

Check the current grant:

sudo -l -U deploy
User deploy may run the following commands on prod-01:
    (ALL) NOPASSWD: ALL

Determine the actual requirement. The pipeline runs exactly one command, systemctl restart myapp, as root (service management requires it), and nothing else.

Scope the rule (see sudo Security for the full syntax and validation workflow with visudo):

deploy ALL=(root) NOPASSWD: /usr/bin/systemctl restart myapp

Verify the new grant matches, and only matches, the intended command:

sudo -l -U deploy
User deploy may run the following commands on prod-01:
    (root) NOPASSWD: /usr/bin/systemctl restart myapp
sudo -u deploy sudo -n /usr/bin/systemctl status myapp
Sorry, user deploy is not allowed to execute '/usr/bin/systemctl status myapp' as root on prod-01.

Conclusion. The pipeline's actual job — restarting one service — still works exactly as before. Every other action, including reading the service's status as root, now requires a separate, deliberate grant. If the deploy account's credentials leak, the blast radius is one command against one service, not the entire machine.

Common Mistakes

  • Treating least privilege as a one-time setup decision. Access requirements change as a system evolves; a grant that was correctly scoped a year ago may now be too broad, or may be missing something the job legitimately needs — either way, it needs periodic review, not a single pass.
  • Scoping the account but not the command's own behavior. A sudo rule restricted to one script is only as narrow as that script — if the script itself accepts arbitrary arguments or can be tricked into running other commands, the restriction is cosmetic. sudo Security covers this failure mode directly.
  • Confusing "least privilege" with "no privilege." The goal isn't to prevent an account or process from doing its job — an over-restricted service that can't do what it legitimately needs just gets a broader grant thrown at it out of frustration, which defeats the purpose. The goal is a grant that matches the actual requirement, not the smallest grant that technically compiles.

Exercises

  1. Pick a service running on a machine you manage (or a lab VM) and identify which account it runs as. Is that account able to do anything beyond what the service itself needs? How would you check?
  2. Find one sudo rule, firewall rule, or file permission on a system you have access to that grants more than is currently used, and describe the minimal change that would scope it down without breaking the legitimate use case.
  3. Explain, in your own words, why a default-deny firewall policy applies least privilege more effectively than a default-allow policy with a list of blocked ports.

Summary

Least privilege means granting exactly the access a subject needs — no more — at every layer of the system: accounts, privilege escalation, the file system, and the network. It's enforced through default-deny policies rather than after-the-fact removal, and it erodes over time through temporary grants that outlive their purpose and broad grants used to sidestep diagnosing a permission problem. The next article, sudo Security, applies this principle to the single most common way an ordinary account becomes root.

References