1. Introduction

Superusers have full control over a system. Because of this, commands like sudo enable regular users to temporarily gain the benefits of such control without introducing a general security risk.

In this tutorial, we explain how to edit the sudo configuration in /etc/sudoers from within scripts. First, we look at the configurations of both sudo and its sudoers plugin. Next, we discuss editing /etc/sudoers manually and explain why it’s not recommended. Finally, we dive into the proper way to modify a sudoers file and employ that in a script.

We tested the code in this tutorial on Debian 11 (Bullseye) with GNU Bash 5.1.4. It should work in most POSIX-compliant environments.

2. sudo Configuration

Before changing any settings, it’s best to understand them.

To begin with, sudo is the main tool, which supports other plugins:

  • sudoers_policy sudoers.so
  • sudoers_io sudoers.so
  • sudoers_audit sudoers.so

The sudoers plugin is the default unless we specify otherwise. Moreover, this fact is in the main configuration file /etc/sudo.conf along with descriptions of most other options sudo supports:

$ cat /etc/sudo.conf
#
# Default /etc/sudo.conf file
#
# Sudo plugins:
#   Plugin plugin_name plugin_path plugin_options ...
#
# The plugin_path is relative to /usr/lib/sudo unless
#   fully qualified.
# The plugin_name corresponds to a global symbol in the plugin#   that contains the plugin interface structure.
# The plugin_options are optional.
#
# The sudoers plugin is used by default if no Plugin lines are present.
#Plugin sudoers_policy sudoers.so
#Plugin sudoers_io sudoers.so
#Plugin sudoers_audit sudoers.so
[...]

Lines that don’t begin with #, Plugin, Path, Debug, or Set do not produce an error but have no effect.

3. The sudoers Plugin Configuration

As a default, the sudoers plugin has its own separate configuration file at /etc/sudoers:

$ cat /etc/sudoers
#
# This file MUST be edited with the 'visudo' command as root.
#
# Please consider adding local content in /etc/sudoers.d/ instead of
# directly modifying this file.
#
# See the man page for details on how to write a sudoers file.
#
Defaults        env_reset
Defaults        mail_badpass
Defaults        secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"

# Host alias specification

# User alias specification

# Cmnd alias specification

# User privilege specification
root    ALL=(ALL:ALL) ALL

# Allow members of group sudo to execute any command
%sudo   ALL=(ALL:ALL) ALL

# See sudoers(5) for more information on "@include" directives:

@includedir /etc/sudoers.d

At the bottom, we see /etc/sudoers also includes the whole directory at /etc/sudoers.d/.

As with other such files, comment lines, which take up most of the content, start with #. Apart from the @includedir and the actual permissions settings, there are three lines of interest:

  • Defaults env_reset to reduce the environment (variables) of sudo-executed commands to the bare minimum
  • Defaults mail_badpass to send mails (by default to root) for wrong password entries
  • Defaults secure_path to replace the value of $PATH for security purposes

Of course, other options are available as well. In fact, we can even negate options with an ! exclamation point prefix and set Defaults for given users by appending a : colon and the relevant user designation:

Defaults:baeldung        !requiretty

Finally, the core of the configuration is the permissions settings. As the comments suggest, there are four types of aliases:

  • Host alias, which is an IP address, hostname, or network
  • User alias, which is a username or group name
  • Cmnd alias, which consists of commands and directories
  • Runas alias, which is similar to user aliases but also allows user IDs

Each alias identifies areas that a privilege specification affects. To specify all, we can use ALL. For example, we often have the following two rules by default:

# User privilege specification
root    ALL=(ALL:ALL) ALL

# Allow members of group sudo to execute any command
%sudo   ALL=(ALL:ALL) ALL

In these, let’s define what each ALL means, from left to right:

  1. ALL= defines who is allowed to use the sudo command
  2. (ALL defines which hosts allow sudo usage
  3. :ALL) defines which user to run the command as
  4. ALL defines which commands are allowed

Considering the above, the default rules allow root and any sudo user to execute any command from any terminal as any user.

4. Manual Editing of /etc/sudoers

As with any other file, we can just directly modify /etc/sudoers with any text ed****itor.

Of course, this is suboptimal because it can result in an error at the syntax level. Similar to the /etc/passwd file, /etc/sudoers is critical for user access and privileges. Any error in such files can lead to serious consequences:

$ echo 'BAD_SUDOERS' > /etc/sudoers
$ sudo echo Test.
/etc/sudoers:1:12: syntax error
BAD_SUDOERS
           ^

Alternatively, we can heed the suggestion in the comments about adding any desired changes in new files under /etc/sudoers.d/. While this preserves the main file intact, it may still introduce problems.

Even better, to catch and prevent errors, we can use a standard POSIX tool.

5. Using visudo

At the top of the /etc/sudoers file, we see an important warning comment about the (strongly) recommended command to edit the configuration: visudo.

visudo is a safe way to edit the /etc/sudoers file, as it follows a strict procedure by default:

  1. Verify /etc/sudoers is not already open for editing
  2. Lock the file against multiple simultaneous edits
  3. Run a prespecified editor preloaded with the file for manual editing
  4. Perform basic validity and syntax error checks on the file
  5. Install the edited file

Of course, we can modify this behavior with flags. Let’s see how.

5.1. Simultaneous Edits

By default, concurrent edits are not allowed. Let’s see what happens when we try to edit an already open sudoers file:

$ visudo
[...]
[1]+  Stopped                 visudo
$ visudo
visudo: /etc/sudoers busy, try again later

After running and backgrounding visudo, we can’t run it again, as the file is already busy.

5.2. Specify File

While /etc/sudoers is the default, we can use visudo with any file by supplying its path to the –file (-f) flag:

$ visudo --file /etc/sudoers.test

Naturally, opening two different files concurrently is allowed. Further, we can combine –file with other flags.

5.3. Specify Editor

By either assigning a value to the SUDO_EDITOR, VISUAL, or EDITOR environment variables or the Defaults editor option in /etc/sudoers, we can change the default editor for visudo:

$ cat /etc/sudoers
[...]
Defaults        editor="/usr/bin/vi"
[...]
$ EDITOR=/usr/bin/vi visudo

Both options above are more or less equivalent in their final result. Importantly, we should specify a full path in editor, while EDITOR can also contain anything reachable in PATH.

Let’s explore some options which make visudo suitable for use in scripts.

5.4. Check-Only Mode

By using the –check (-c) flag, we can run visudo just to validate a sudoers file:

$ visudo --check
/etc/sudoers: parsed OK
/etc/sudoers.d/README: parsed OK

Moreover, we can increase the strictness of the checks via the –strict (-s) flag. By default, visudo also checks the contents of the /etc/sudoers.d/ directory. With the –file flag, we can validate a single file:

$ visudo --check --file /etc/sudoers.test
/etc/sudoers.test:9:19: syntax error
Defaulst        env_reset
                  ^

Here, we see the detection of a trivial error. In this case, the command returns a non-zero exit code.

5.5. Quiet Checks

With the –quiet (-q) flag, we can silence visudo when performing checks:

$ visudo --quiet --check --file /etc/sudoers.test
$ echo $?
1

By using the ? question mark variable, we can confirm whether a given sudoers file is valid.

Finally, let’s see how we can use visudo without user interaction.

6. Scripting visudo

Naturally, we can use constructs like visudo –check –quiet && echo ‘Correct.’ or similar if statements to perform actions based on the validity of a sudoers file. However, changing the file via visudo is a bit more complex.

First, let’s write an example executable script cudo.sh to edit and validate /etc/sudoers:

#!/usr/bin/env bash
echo "$1" | (EDITOR="tee -a" visudo)

In this case, we pipe the value of the first script argument, $1, to a subshell. Basically, the latter does two things:

  1. Set the editor for visudo to tee –append
  2. Launch the newly assigned editor, reading stdin

Importantly, using tee –append as its editor means anything we pipe in goes directly to the end of the sudoers file. After that, visudo performs its checks.

Let’s see our script in action:

$ ./cudo.sh '# Comment'
# Comment
$ tail --lines=1 /etc/sudoers
# Comment
$ ./cudo.sh 'Error'
Error
/etc/sudoers:29:6: syntax error
Error
     ^
$ tail --lines=1 /etc/sudoers
# Comment

With a correct addition to the file, we can find the content at the end via tail. However, introducing an error means no changes are made to /etc/sudoers. Thus, visudo offers protection from incorrect syntax when editing.

7. Summary

In this article, we looked at sudo, its sudoers plugin, its configuration, and various ways to change it.

In conclusion, using visudo to change a sudoers file ensures proper and safe handling of this sensitive file.