Linux: Managing sudo
TLDR;
You can control access to root level and other user commands with sudo. Use visudo to edit the /etc/sudoers file or, preferentially, use /etc/sudoers.d/01-rules
Here's the basic examples:
#For user paul to run anything as anyone:
paul ALL=(ALL:ALL) ALL
#For group webdevs to restart the nginx service:
%webdevs ALL=(ALL:ALL) /usr/bin/systemctl restart nginx
#For group webdevs to do anything in the context of the www-data user without requiring a password:
%webdevs ALL=(www-data) NOPASSWD: ALLWhat does Sudo do?
Sudo, short for SuperUser DO, executes a program in the context of "root" regardless of what user account you are using to log in. This is important because modern security practices say you should never sign in initially as your administrator account. In addition, root is often disabled or has a very long and complex password.
All POSIX systems (Unix, Linux, FreeBSD, MacOS) have a user account called "root". This is User ID #0 and Group ID #0. The account is called root because when the kernel loads, the first program executed is in the context of this user, and the kernel operations occur in the context of this user when it interacts with the user layer. This means the entire process tree comes from this user so the name root was selected.
The root user has access to every file, process, memory address, and kernel function on the system regardless of any other security setting. Because of this, if you have root access then you have full access to everything on the system so this access must be monitored and controlled. This is similar to having "Administrator" access on a Windows system.
How does Sudo Work?
Sudo allows access after consulting the configuration file(s) to make sure the user should be able to execute the requested command as the requested user/group. It also validates the user's password if required by configuration and performs other functions based on plugins. After that, it executes the requested command with appropriate parameters.
Expand this for the detailed explanation on how it works
There are multiple attributes you can set on a file in Linux. Among these, you can define who can execute the file. This can be restricted differently at the levels of Owning User, Owning Group, or Everyone Else.
There is an additional option affecting the execution of the file. If the "setuid" attribute is set (identified as an "s") then when the file is executed, it is executed as the user/group who owns it regardless of who started the file. Sudo, and programs with this option set, are potentially a large security risk so the codebases for programs that do this are heavily analyzed and searched for holes in how they determine the validity of the user.
To see everything on your system taking advantage of this feature, just run this: sudo find / -type f -perm /4000
If you have a variety of users interactively using the system then it may be necessary to monitor this because if user "paul" sets a file up with public access with this option enabled then it may allow other users to execute commands as "paul" maliciously, particularly if it is a script setup like this without proper security.
Let's look at an example of how this can be useful, apart from the system tools that use it. This example will be moved to another article later
Let's say that we have a web server and we want a web deployment script, or compiled program, that will have the ability to overwrite the web files but we don't want to give developers access directly to the files. We set the script's owning user to www-data, the owning group to webdevs, and grant executable to the group level with setuid on the user level. My user, paul, is in the webdevs group. When the script is executed, it runs in the context of www-data. That means that, on the filesystem, it cannot access files for user paul, but it CAN execute git and update from there the files located in the web folders.
Traditionally this example would be accommodated via sudo but controlling this at the file permission level bypasses the requirement for root access to edit the sudo configuration.
Configuration File
Sudo, by default, stores the configuration in /etc/sudoers. To edit this file, you MUST use the visudo command. This will use the system-configured editor, in Ubuntu that means nano. The visudo command validates the configuration of the file after saving to prevent locking yourself out of your system due to a bad config file. If the configuration is invalid, it will not update the file on the system.
The configuration is read when sudo is executed, so changes are immediate.
/etc/sudoers.d/* rather than modifying /etc/sudoers More on this further down.The Basic permission line
There are several options we will cover supported by this file, but first I want to talk about the basic permissions line. The default configuration in Ubuntu has this:
# User privilege specification
root ALL=(ALL:ALL) ALL
# Members of the admin group may gain root privileges
%admin ALL=(ALL) ALL
# Allow members of group sudo to execute any command
%sudo ALL=(ALL:ALL) ALL
Here's the breakdown of what these fields mean:<running user> <host>=(<runas user>:<runas group>) <command 1>, <command 2>
Running User
The running user field can be in one of four formats. All text is case sensitive. Here are the examples for discussion:
paul ALL=(ALL:ALL) ALL
#1001 ALL=(ALL:ALL) ALL
%webdev ALL=(ALL:ALL) ALL
%#33 ALL=(ALL:ALL) ALLThe 4 formats are:
- A plain username, like
paulwhich will match if the username is the same. - A user can be specified by uid if prefixed by #. Please note that # at the start is ALSO used for comments so don't ever start a comment in the sudoers files with a number at the beginning. The uid can be found by running
id paulor checking the/etc/passwdfile. - You can specify a group if you prefix it with %. So
%webdevapplies to everyone in the webdev group. - Finaly, you can specify a group using the gid number. The gid can be found by running
id paulor checking the/etc/groupsfile.
You should use an escape character for these. For example:
test\ user ALL=(ALL:ALL) ALL%test\ group ALL=(ALL:ALL) ALLOr, you can put them in quotes:
"test user" ALL=(ALL:ALL) ALL"%test group" ALL=(ALL:ALL) ALLYou can also negate these options. The format of the negation does not need to
- If you specify
%admin, !paul, !bobthen everyone in admin except for paul and bob can use this entry. - Perhaps more useful is to create a group called
nosudo.
Then use%admin, !%nosudoin order to exclude the group of users. - âť—According to the developers, negating ALL like with
ALL, !%nosudois "generally not effective"
Host
This is a field I've not seen used in any of the example files I've seen. It is typically left at ALL.
You can put a hostname (test-vm1), IP Address (192.168.1.2) or IP CIDR (192.168.1.0/24) here. Hostname is NOT case sensitive.
There are two cases I know of that this is useful:
- It allows for you to deploy a single sudoers file to all systems but still have one-off entries that are only applicable to specific systems by specifying their host
- It allows you to have entries that change depending on the subnet that a computer is on. For example, if your primary network is 192.168.42.0/24 then you might do this so that you don't have to re-enter your password when you are in the office but have to when away from the office:
paul 192.168.42.0/24=(ALL) NOPASSWD: ALL
paul ALL=(ALL) ALLBecause these rules are evaluated at time of execution, if you connect to a VPN that puts you on a network with a special rule that special rule will begin to apply. If you used sudo to execute a command with a host specified and then disconnect from the network that command will continue to execute.
Runas User/Group
This is the simplest section of the entire line. Here you can specify which user or group you can run sudo as. Most people use sudo only for root, so (ALL) or (ALL:ALL) is really saying (root:root). It is a good idea to be more specific in which user someone can run as.
To understand this better, let's look at a few scenarios related to websites of the right way and the wrong way.
paul ALL=(www-data) ALL
bob ALL=(:www-dev) ALL
alice ALL=(ALL:www-dev) ALL
luke ALL=(www-data:ALL) ALLConsider first that all permissions in POSIX systems (FreeBSD, Linux, Mac, etc) can be thought of as filesystem permissions. It's more complicated than that, but it's the easy way to understand it.
Both sides accept the ALL keyword and if you specify a user you don't have to specify a group and vice versa.
- In the first example here, paul can run
sudo -u www-data -iin order to get an interactive prompt in the context of the www-data user. That would allow him to run commands and access files that www-data (and its groups) can access. This is especially useful for php sites that often require running commands as the web server's user account. - In the second example, we've defined bob who can run
sudo -g www-dev -iin order to get an interactive prompt in the context of the www-dev group. Please note bob is still the user context, this new context simply has the www-dev group added to him. This is useful if he sometimes needs access to the web files but you want to log that access separately from the user logins. Or if specific scripts need that access and need to be logged. - The third example looks great. We're trying to say alice can run in the context of all users who have the www-dev group. But there is a problem. The two parts are treated separately so alice can run in the context of www-dev group and can run in the context of any user on the system.
- The fourth example may look harmless. luke can run in the context of the www-data user and run in any group's context. That second half is the issue. Consider the
/etc/shadowfile where only root and the shadow group can access it. It contains password hashes, so that makes sense that access is restricted. With "ALL" group specified, luke can runsudo -g shadow cat /etc/shadowand view the contents of that file.
In short, be VERY careful with the ALL keyword in this context. If you don't want the user to have full access to the system, don't use ALL anywhere in the ( ) section.
Commands
The command is what the user is allowed to do. There are a few ways to limit access:
- ALL - The ALL keyword simply means everything.
- /usr/bin/systemctl - This would allow the user to run systemctl and run it with everything it can do.
- /usr/bin/systemctl restart nginx - This would allow the user to restart the service nginx
- /usr/bin/systemctl * nginx - This would allow the user to run any parameters against nginx. Get status, enable, disable, etc. But consider that this is also allowed:
systemctl disable ssh nginxBecause of how the parameters work and how the wildcard works. - /usr/bin/systemctl restart nginx, /usr/bin/systemctl start nginx - This would allow the user to restart the service nginx or start it if it is stopped. status is natively allowed for a non-privileged user. This is an example of multiple commands on one line.
- /*/reboot - This would allow the program reboot to be run regardless of the path. This is generally a bad idea because they can rename anything to reboot and execute it.
You can prefix each command with one or more options. Options are automatically carried to the next command:
- NOPASSWD: - This skips the requirement of the user to enter a password.
- MAIL: - Specifies that a notification should be emailed that this command has been executed.
- sha512: - There are other supported hash types and sizes. This allows you to specify the hash of the executable being run to verify no tampering has occurred with the executable. The hash changes when an update is done, so use this carefully.