Understanding syntax, arguments, aliases used in sudoers file with examples (visudo)

This article contains overview on the various syntax and aliases which are used in sudoers file

IMPORTANT POINT: It is strongly recommended to use "visudo" to edit the sudoers file rather than using normal editor and directly modifying the original file /etc/sudoers as that way you will not be able to do any syntax check.

Any incorrect syntax in the sudoers file can block you for accessing your node and can be very risky.

"visudo" will not allow you to exit the editor with an incorrect syntax in the file.

The most commonly used syntax used can be summarised as
USER_SPACE  HOST=RunAs(User)  COMMANDS

OR

USER_SPACE  HOST=RunAs(User:Group)  COMMANDS
You can also use NOPASSWD variable if you do not want sudo to prompt for password while executing the assigned task

The syntax here would be:
USER_SPACE  HOST=RunAs(User)  NOPASSWD:COMMANDS

OR

USER_SPACE  HOST=RunAs(User:Group)  NOPASSWD:COMMANDS
Let's understand the different fields here separately

USER_SPACE : this is the first column of the syntax which can contain username, %group or User_Alias
HOST : this will contain hostname detail on which the sudo will perform the assigned task
RunAs : This optional clause controls the target user (and group) sudo will run the Command as.
COMMAND : Give the list of command(s) which you want the user to execute

Aliases in sudoers file

Various Aliases defined in the sudoers files, these are sort of VARIABLES we use in scripts which can contain multiples entries that fit the same domain (users, hosts, commands). The aliases we have in sudoers are as below
User_Alias
Host_Alias
Cmnd_Alias
Let's go through each of the available options one by one with some examples to understand more on them.

User

You can assign sudo based permission for individual user by using the below syntax

Syntax:
user HOST=RunAs COMMAND
For example I want user "deepak" to be allowed to restart crond service so below would be the syntax I would use

deepak ALL=(ALL) /usr/bin/systemctl restart crond.service
If I want to allow him to run more commands then add below content in your sudoers file using visudo
deepak ALL=(ALL) /usr/bin/systemctl restart crond.service, /usr/bin/systemctl status crond.service
Validate the same
[deepak@golinuxhub ~]$ sudo systemctl status crond.service
[sudo] password for deepak:
● crond.service - Command Scheduler
   Loaded: loaded (/usr/lib/systemd/system/crond.service; enabled; vendor preset: enabled)
   Active: active (running) since Sat 2017-12-30 19:04:36 IST; 4min 18s ago
 Main PID: 1768 (crond)
   CGroup: /system.slice/crond.service
           └─1768 /usr/sbin/crond -n

Dec 30 19:04:36 golinuxhub.lab systemd[1]: Started Command Scheduler.
Dec 30 19:04:36 golinuxhub.lab systemd[1]: Starting Command Scheduler...
Dec 30 19:04:36 golinuxhub.lab crond[1768]: (CRON) INFO (RANDOM_DELAY will be scaled with factor 10% if used.)
Dec 30 19:04:36 golinuxhub.lab crond[1768]: (CRON) INFO (running with inotify support)
Dec 30 19:04:36 golinuxhub.lab crond[1768]: (CRON) INFO (@reboot jobs will be run at computer's startup.)

User Alias (User_Alias)

If we have multiple list of users for whom we want to allow certain list of commands with sudo then we can use "User_Alias" which is an internal variable of sudoers file.

Syntax:
User_Alias  ALIAS_NAME = user1, user2, ...

ALIAS_NAME
  HOST = RunAs COMMAND
For example:
I have user "deepak" and "ankit" to whom I want to allow execute crond service related commands for restart and status using systemctl then my syntax would look like below

Look out for below section in your sudoers file using visudo
## User Aliases
## These aren't often necessary, as you can use regular groups
## (ie, from files, LDAP, NIS, etc) in this file - just use %groupname
## rather than USERALIAS
# User_Alias ADMINS = jsmith, mikem
# The content may look little different based on your distribution, below this you can # create a new line add below content

User_Alias CROND_USER = deepak, ankit
NOTE: There is no hard and fast rule to create new rule below this section, although this helps keep every variable under its respective section so looks neat and clean which is always good

Lastly add a new line at the end of sudoers file
CROND_USER ALL = (ALL) /usr/bin/systemctl restart crond.service, /usr/bin/systemctl status crond.service
here as you see instead of giving name of individual user I created a variable CROND_USER and added all the list of users for whom I want to give common sudo access

%Group

Assuming you already have a system group which has a list of users for whom you want to allow certain commands or tasks then instead of using User_Alias you can use the group argument using the below syntax

Syntax:
%groupname  HOST=RunAs COMMAND
For example I have a group "mycompany" whose members are user "deepak" and "ankit" for whom again I want to give access to execute restart and check status of crond service

Add the below line in the sudoers file /etc/sudoers using visudo
%mycompany ALL=(ALL) /usr/bin/systemctl restart crond.service, /usr/bin/systemctl status crond.service

NOPASSWD

By default, sudo requires that a user authenticate him or herself before running a command.  This behavior can be modified via the NOPASSWD tag

Syntax for individual user:
User  HOST=RunAs NOPASSWD:COMMAND

Syntax for individual group:
%group  HOST=RunAs NOPASSWD:COMMAND

Syntax for User_Alias:
ALIAS_VARIABLE  HOST=RunAs NOPASSWD:COMMAND
For example I have a user deepak for whom I want to give permission to restart and check status of crond service

Add the below content at the end of the line in your sudoers file using visudo
deepak ALL=(ALL) NOPASSWD:/usr/bin/systemctl restart crond.service, /usr/bin/systemctl status crond.service
Suppose I have a set of commands out of which I want to allow deepak to run only few of the commands passwordless while for some I want the password prompt then in that case we will use below rule

deepak ALL=(ALL) NOPASSWD:/usr/bin/systemctl restart crond.service, PASSWD:/usr/bin/systemctl status crond.service

So here "deepak" will be allowed to restart crond service without password but he will be prompted for password while checking the status of the crond service

HOST

Why do we need "Host" when I know I am going to run from my machine?
Well actually the question is absolutely correct but what if you have a big network with 1000s of servers wherein each group of server have their own role for eg some can be a DNS server, some can be an apache server and we have to assign sudo roles accordingly to respective users.
Now you wouldn't want a user "deepak" who is a domain administrator having sudo access to domain server is also getting sudo access to other servers?

So I will use the "Host" section in the sudo and will make sure that user deepak to allowed to run all the needed commands as root but only on the "Domain Server"

Syntax:
User  HOST=RunAs COMMAND
For example if my domain server hostname is "golinuxhub" then my sudoers would have something like
deepak  golinuxhub=(ALL) <List of commands>
with this user "deepak" can run all the allowed commands on "golinuxhub" but not on other servers

For example I want "deepak" to allow restart of crond service but only on golinuxhub server
deepak golinuxhub=(ALL) /usr/bin/systemctl restart crond.service
If I want him to allow restart this server on all the hosts then this would become
deepak ALL=(ALL) /usr/bin/systemctl restart crond.service

Host_Alias

If you want to allow a user execute some tasks with sudo permission on multiple hosts then you can use the Host_Alias variable which is an sudoers based internal variable.

Syntax:
Host_Alias  MAILSERVERS = smtp, smtp2, ...
Look out for below section in your sudoers file using visudo and append a new line to create a new variable for your  requirement

For example I want "deepak" to run some command on dnsserver and dhcpserver then
## Host Aliases
## Groups of machines. You may prefer to use hostnames (perhaps using
## wildcards for entire domains) or IP addresses instead.
# Host_Alias     FILESERVERS = fs1, fs2
# Host_Alias     MAILSERVERS = smtp, smtp2
Host_Alias DEEPAK_SERVER = dnsserver, dhcpserver
deepak  Host_Alias=(ALL) /usr/bin/systemctl restart crond.service
Here dnsserver and dhcpserver are the hostname of my servers, you can replace this with the hostname based on your setup

NOTE: There is no hard and fast rule to create new rule below this section, although this helps keep every variable under its respective section so looks neat and clean which is always good

RunAs(User:Group)

  • A Runas_Spec determines the user and/or the group that a command may be run as.  
  • A fully-specified Runas_Spec consists of two Runas_Lists (as defined above) separated by a colon (‘:’) and enclosed in a set of parentheses.  
  • The first Runas_List indicates which users the command may be run as via sudo's -u option.  
  • The second defines a list of groups that can be specified via sudo's -g option.  
  • If both Runas_Lists are specified, the command may be run with any combination of users and groups listed in their respective Runas_Lists. 
  • If only the first is specified, the command may be run as any user in the list but no -g option may be specified.  
  • If the first Runas_List is empty but the second is specified, the command may be run as the invoking user with the group set to any listed in the Runas_List.  
  • If both Runas_Lists are empty, the command may only be run as the invoking user.  

With this argument we tell sudo to accept "-u" and "-g" option where "-u" will run the command/script as the respective user and "-g" will do the same as respective group.

Syntax if only "user" section is used:
USER_SPACE  HOST = RunAs(User) COMMAND
Syntax if only "group" section is used:
USER_SPACE  HOST = RunAs(:Group) COMMAND
Syntax if both "user" and "group" sections are used:
USER_SPACE  HOST = RunAs(User:Group) COMMAND
Let's have a look at some example

I have a test script which is owned by "deepak" user
[root@golinuxhub ~]# ls -l /tmp/deepak_script.sh
-rwxr-xr--. 1 deepak deepak 47 Dec 30 17:14 /tmp/deepak_script.sh
As you see both user and group of this script is "deepak:deepak" and both have executable permission for this script

If I run thos script as user "deepak"
[deepak@golinuxhub ~]$ /tmp/deepak_script.sh
Hello This is Deepak's fIle
So this was properly executed. Let's try to run this as some other user "ankit"
[ankit@golinuxhub ~]$ /tmp/deepak_script.sh
-bash: /tmp/deepak_script.sh: Permission denied
First thing it says is permission denied, well why not buddy since deepak is smart as he has not given executable permission to this script so there is no way so gonna mesh with his script

Now ankit attempts to run the script with sudo access hoping that might work
[ankit@golinuxhub ~]$ sudo /tmp/deepak_script.sh

We trust you have received the usual lecture from the local System
Administrator. It usually boils down to these three things:

    #1) Respect the privacy of others.
    #2) Think before you type.
    #3) With great power comes great responsibility.

[sudo] password for ankit:
ankit is not in the sudoers file.  This incident will be reported.
Oops that also didn't worked.

But for the sake of this article let's give Ankit "sudo" permission to execute Deepak's script (I hope he doesn't get mad!!) in the sudoers file using visudo

ankit  golinuxhub=(deepak) /tmp/deepak_script.sh
Save and exit the file.

Now if you notice here I have given RunAs access to "deepak" which means if user "ankit" runs the script as "deepak" then he will be allowed to run the script.

Now let's try to execute the same file as Ankit but with sudo permission
[ankit@golinuxhub ~]$ sudo /tmp/deepak_script.sh
[sudo] password for ankit:
Sorry, user ankit is not allowed to execute '/tmp/deepak_script.sh' as root on golinuxhub.lab.
Since here ankit is trying to run deepak's script as root (if there is no -u specified then the sudo works as root user) hence the permission was denied, let's try again with "-u deepak"
[ankit@golinuxhub ~]$ sudo -u deepak /tmp/deepak_script.sh
[sudo] password for ankit:
Hello This is Deepak's fIle
So it worked this time.

Similar to this we can also give RunAs "Group" level permission

For example if I give below arguments in the sudoers file
ankit  golinuxhub=(deepak:deepak) /tmp/deepak_script.sh
If you observe I have given access for both User:deepak and Group:deepak so using either Ankit can run the script
[ankit@golinuxhub ~]$ sudo -g deepak /tmp/deepak_script.sh
Hello This is Deepak's fIle
OR he can run the script as either user and group
[ankit@golinuxhub ~]$ sudo -u deepak -g deepak /tmp/deepak_script.sh
Hello This is Deepak's fIle

COMMANDS

I think I have already been giving multiple examples for this in the above sections. In the last column of sudoers syntax we provide the list of commands which we want to be allowed for a user/group.

Syntax:
USER_SPACE   HOST=RunAs COMMANDS
For example I want "deepak" to allow restart of crond service
deepak ALL=(ALL) /usr/bin/systemctl restart crond.service
You can give multiple commands separated by a comma "," and followed by a whitespace.

Cmnd_Alias

If you have a set of commands for which you want to provide access so it makes sense to add them into a single variable i.e. Cmn_Alias which is an internal sudoers owned variable.

Syntax:
Cmnd_Alias VARIABLE_NAME = /path/to/command1, /path/to/command2, /path/to/command3, ...
Look out for below section in your sudoers file and append a new line to create a new variable for your requirement in the sudoers file using visudo

For example I want "deepak" to run some set of commands using a variable CROND_SERVICE
## Command Aliases
## These are groups of related commands...
Cmnd_Alias  CROND_SERVICE = /usr/bin/systemctl restart crond.service, /usr/bin/systemctl status crond.service

deepak ALL = (ALL) CROND_SERVICE
If you want to disable password prompt for the set of commands then use below line
deepak ALL = (ALL) NO_PASSWD:CROND_SERVICE

I will write a separate article with more possible examples and scenarios for sudo use case on ground zero.

I hope this article was helpful.