The most surprising thing about sudo is that it’s not primarily about granting power, but about limiting it.

Imagine a web server process. It needs to bind to port 80, which requires root privileges. Instead of running the whole web server as root (a massive security hole), we can use sudo to let the web server process temporarily and specifically run a command like setcap 'cap_net_bind_service=+ep' /usr/sbin/nginx as root, and then drop back to its normal user. This is the core idea: just enough privilege, just in time.

Let’s see this in action. We have a user, appuser, who needs to restart a service, say nginx. We don’t want appuser to have a full root shell.

First, we need to define what appuser can do. This goes into /etc/sudoers (or preferably, a file in /etc/sudoers.d/).

# /etc/sudoers.d/nginx_restart
appuser ALL=(ALL) NOPASSWD: /usr/sbin/service nginx restart

Now, appuser can run:

sudo /usr/sbin/service nginx restart

This command will execute /usr/sbin/service nginx restart as root, but appuser can’t do anything else with sudo. No sudo su, no sudo bash, no editing other files.

But that’s not enough. What if nginx restart script itself is compromised or has a vulnerability? We need to restrict it further. We can use visudo to edit /etc/sudoers and specify exactly what arguments are allowed.

Let’s refine that rule:

# /etc/sudoers.d/nginx_restart_specific
appuser ALL=(ALL) NOPASSWD: /usr/sbin/service nginx restart

This still allows service nginx restart. What if we want to be even more granular? We can allow specific commands that service might call, or even better, allow systemctl for modern systems.

# /etc/sudoers.d/nginx_systemctl
appuser ALL=(ALL) NOPASSWD: /usr/bin/systemctl restart nginx.service

Now appuser can run:

sudo /usr/bin/systemctl restart nginx.service

This is better, but still allows restarting nginx.service. What if we want to allow only a specific script that then restarts nginx?

# /etc/sudoers.d/nginx_wrapper
appuser ALL=(ALL) NOPASSWD: /usr/local/bin/nginx_restart_wrapper.sh

And in /usr/local/bin/nginx_restart_wrapper.sh:

#!/bin/bash
/usr/bin/systemctl restart nginx.service

This pattern of using wrapper scripts is a powerful way to encapsulate complex or sensitive operations, allowing sudo to grant permission for the wrapper to run, rather than the raw command.

The real power of sudo lies in its ability to execute commands as another user (often root) without giving away the target user’s password. The NOPASSWD: tag is crucial for automation and for specific, limited tasks where prompting for a password would be cumbersome or impossible. When NOPASSWD: is not used, the user will be prompted for their own password, not the root password, which is a key security feature.

Crucially, we need to audit everything. sudo logs its actions to /var/log/auth.log (or similar, depending on your syslog configuration). Every command executed via sudo is recorded, showing who ran what, when, and as whom.

Example log entry:

Oct 26 10:30:01 servername sudo:    appuser : TTY=pts/0 ; PWD=/home/appuser ; USER=root ; COMMAND=/usr/bin/systemctl restart nginx.service

This log tells us that appuser on terminal pts/0 in their home directory, ran /usr/bin/systemctl restart nginx.service as root at 10:30:01 on October 26th. This audit trail is invaluable for security monitoring and incident response.

One aspect often overlooked is the runas specification within the sudoers file. While we’ve shown ALL=(ALL), you can restrict commands to run as specific users or groups. For instance, to allow appuser to run a specific script only as the www-data user:

# /etc/sudoers.d/appuser_as_wwwdata
appuser ALL=(www-data) NOPASSWD: /usr/local/bin/deploy_app.sh

Now, when appuser runs sudo /usr/local/bin/deploy_app.sh, the script will execute as www-data, not root. This further reduces the blast radius if the deploy_app.sh script itself is compromised.

The Defaults directive in sudoers offers a wealth of configuration options. For example, Defaults timestamp_timeout=10 would set the sudo grace period to 10 minutes, meaning you wouldn’t be prompted for your password again for any sudo command within 10 minutes of the last one. Conversely, Defaults !timestamp_logging would disable timestamp logging for sudo commands, which is generally not recommended for security.

The most common mistake is granting too broad permissions, like appuser ALL=(ALL) ALL. This effectively gives appuser full root access and defeats the purpose of least privilege. Always strive to be as specific as possible with the command path and any allowed arguments.

The next step in securing privileged operations is often exploring capabilities (setcap) for specific executables, or using more advanced privilege separation tools like doas or even containerization for truly isolated tasks.

Want structured learning?

Take the full Cdk course →