Right, so you’ve decided you don’t want to hand over the keys to the entire kingdom. Smart. Giving a user full, unfettered sudo access is like giving them a bazooka to open a walnut: effective, but the cleanup is going to be spectacular. The real power of sudo isn’t in giving someone all of root’s power; it’s in the surgical precision of delegating only the commands they absolutely need to do their job. Nothing more.

Let’s get our hands dirty. The sudoers file, typically /etc/sudoers, is the holy grail here. You never edit this file with a regular text editor. You use visudo. This isn’t a suggestion; it’s the law of the land. visudo locks the file, checks for syntax errors before saving, and saves you from the utter nightmare of a botched sudoers file that locks you out of all root access. If you edit it directly with vim and make a typo, you’ll get to enjoy the process of booting into a recovery shell to fix it. Trust me, it’s not fun.

The Anatomy of a Restricted Sudoers Entry

The basic syntax for restricting a user to a specific command is straightforward, but the devil is in the details. Here’s the pattern:

username hostlist = (runas_user:runas_group) command_list

Let’s break that down with a concrete example. Say we want to allow our deploy user, deploybot, to restart the nginx service on the server named web01. The entry would look like this:

# Allow deploybot to restart nginx
deploybot web01 = (root) /usr/bin/systemctl restart nginx

Now, deploybot can run only that exact command by prefacing it with sudo:

sudo systemctl restart nginx

But here’s the first “gotcha”: try to run sudo systemctl status nginx. It will fail. You’ve given them the restart command, not the status command. This is often the desired behavior, but it’s a common source of confusion for the user on the other end who thinks their sudo is broken. They’re not wrong, in a way.

Taming the Wildcards and Paths

What if you need to grant access to a command with arguments? This is where most people shoot themselves in the foot. Let’s say you want a user to be able to use apt update but not apt upgrade or, heaven forbid, apt install neofetch.

A naive approach would be:

# WARNING: This is DANGEROUS!
user1 web01 = (root) /usr/bin/apt update

Why is this dangerous? Because a user can easily break out of this restriction by setting the PATH or using a simple shell escape. Observe:

sudo apt update                 # This works.
sudo APT_GET=/bin/sh apt update # This might let them set an env var for an exploit
sudo apt update /bin/sh        # If apt had a flaw, this could be bad

The safer, more robust method is to use the full path to the command and, crucially, to quote the command and its arguments to prevent shell escapes. This is non-negotiable for security.

# This is the way.
user1 web01 = (root) /usr/bin/apt update

Notice the quotes? This means the user can run /usr/bin/apt update and only that exact string of characters. They can’t slip in extra flags or arguments. It’s airtight.

Grouping Commands and Using Aliases

Typing out every single command for every user gets old fast. This is where sudo’s alias system comes in. You can define aliases for Users (User_Alias), Hosts (Host_Alias), Runas users (Runas_Alias), and most importantly for us, Commands (Cmnd_Alias).

Let’s create a Cmnd_Alias for common web server commands for our webadmins group.

# Define a group of commands
Cmnd_Alias WEB_CMDS = /usr/bin/systemctl restart nginx, \
                       /usr/bin/systemctl status nginx, \
                       /usr/bin/tail -f /var/log/nginx/error.log

# Apply it to the webadmins group
%webadmins web01 = (root) WEB_CMDS

Now any user in the webadmins Unix group can run any of those three commands. The backslashes (\) are just line continuations to make the file more readable. This is where sudo transitions from a necessary evil to a genuinely powerful management tool.

The Number One Pitfall: Shell Escapes

I need to be very clear about this. Granting sudo access to an interactive command, especially a text editor or a shell, is almost always a terrible idea. Why? Because nearly all of them have a built-in shell escape function.

Never, ever do this:

# This is basically granting full root access. Don't.
user1 web01 = (root) /usr/bin/vi

A user granted sudo vi can then simply type :! /bin/bash from within vi and—poof—they’re now in a root shell, completely bypassing your carefully crafted restriction. This applies to less, more, man, emacs, and most other interactive utilities. If you absolutely need to allow someone to edit a specific file as root, your best bet is to write a small wrapper script that copies the file, lets them edit it as themselves, and then validates the changes before using sudo to move it back. It’s a pain, but security often is.

The golden rule is this: be as specific as humanly possible. If a user needs to run one command, give them the exact path to that one command, quoted. Test it. Then test it again by trying to break out of it. Your future self, who isn’t dealing with a compromised server, will thank you.