SUDO.CONF(5)

๐ŸŽ€ sudo.conf(5) ๐ŸŽ€

File Formats Manual ๐Ÿฆ‹

๐Ÿ’– NAME ๐Ÿ’–

okay so sudo.conf โ€” configuration for the sudo front-end โœจ

๐ŸŒธ DESCRIPTION ๐ŸŒธ

bestie, the sudo.conf file is where you set up the sudo front-end ๐Ÿ’… You use it to configure sudo plugins, plugin-agnostic path names, debug flags, and other settings. it's giving control ๐Ÿ‘‘

The sudo.conf file supports the following directives, which we'll go through in detail below:

  • Plugin โ€” an approval, audit, I/O logging, or security policy plugin ๐Ÿ”Œ
  • Path โ€” a plugin-agnostic path ๐Ÿ›ฃ๏ธ
  • Set โ€” a front-end setting, like disable_coredump or group_source ๐ŸŽ›๏ธ
  • Debug โ€” debug flags to help you debug sudo, sudoreplay, visudo, and the sudoers plugin ๐Ÿ›

The pound sign (#) marks a comment ๐Ÿ’ฌ The comment character and anything after it, up to the end of the line, get ignored. literally invisible to sudo ๐Ÿ™ˆ

Got a long line? You can continue it with a backslash (\) as the very last character on the line ๐Ÿงต Leading white space is stripped from the start of lines even when you use a continuation character, so indent away ๐Ÿซถ

Any non-comment line that doesn't start with Plugin, Path, Debug, or Set is silently ignored. no error, no drama, just vibes ๐Ÿซ 

The sudo.conf file is always parsed in the C locale ๐ŸŒˆ

๐Ÿงš Plugin configuration ๐Ÿงš

okay this part is so cool โœจ sudo has a plugin architecture for security policies and input/output logging, which means third parties can build and distribute their own policy and I/O logging plugins that work seamlessly with the sudo front-end ๐Ÿฆ‹ Plugins get dynamically loaded based on what's in sudo.conf ๐Ÿ’œ

A Plugin line is the Plugin keyword, followed by the symbol_name and the path to the dynamic shared object that holds the plugin. The symbol_name is the name of the struct approval_plugin, struct audit_plugin, struct io_plugin, or struct policy_plugin defined by the plugin. If a plugin implements multiple plugin types, you need a Plugin line for each unique symbol name ๐Ÿ“ The path can be fully qualified or relative. If it isn't fully qualified, it's relative to the directory set by the plugin_dir Path setting, which defaults to /usr/libexec/sudo. In other words:

Plugin sudoers_policy sudoers.so

is equivalent to:

Plugin sudoers_policy /usr/libexec/sudo/sudoers.so

If the plugin was compiled statically into the sudo binary instead of being installed as a dynamic shared object, you should specify the path without a leading directory, since it doesn't actually exist in the file system ๐Ÿง  For example:

Plugin sudoers_policy sudoers.so

On AIX systems, the plugin can be either a shared object ending in .so, or an archive file containing a shared object ending in .a with the name of the shared object in parentheses at the end ๐Ÿค“

Starting with sudo 1.8.5, any extra parameters after the path get passed as arguments to the plugin's open function โœจ For example, to override the compile-time default sudoers file mode:

Plugin sudoers_policy sudoers.so sudoers_mode=0440

See the sudoers(5) manual for a list of supported arguments ๐Ÿ“–

The same dynamic shared object can contain multiple plugins, each with a different symbol name ๐Ÿ’… The file must be owned by user-ID 0 and only writable by its owner. Because of the ambiguities that come up with composite policies, only a single policy plugin may be specified. This limitation does not apply to I/O plugins ๐Ÿ™…โ€โ™€๏ธ

โš ๏ธ Heads up: if there's no sudo.conf file at all, or it has no Plugin lines, the sudoers plugin is used as the default security policy, for I/O logging (if the policy enables it), and for auditing. It's equivalent to this: Plugin sudoers_policy sudoers.so Plugin sudoers_io sudoers.so Plugin sudoers_audit sudoers.so

Starting with sudo version 1.9.1, some of the logging functionality of the sudoers plugin moved from the policy plugin to an audit plugin ๐Ÿ”„ To keep things compatible with sudo.conf files from older sudo versions, if sudoers is configured as the security policy, it'll be used as an audit plugin too. This guarantees the logging behavior matches sudo versions 1.9.0 and below. so consistent, no cap ๐Ÿซถ

For more on the sudo plugin architecture, check out the sudo_plugin(5) manual ๐Ÿฆ‹

๐Ÿ›ฃ๏ธ Path settings ๐Ÿ›ฃ๏ธ

A Path line is the Path keyword, followed by the name of the path you want to set and its value. For example:

Path intercept /usr/libexec/sudo/sudo_intercept.so
Path noexec /usr/libexec/sudo/sudo_noexec.so
Path askpass /usr/bin/ssh-askpass

If you don't specify a path name, features that rely on that setting get disabled ๐Ÿ™ˆ Disabling Path settings is only supported in sudo version 1.8.16 and higher.

The following plugin-agnostic paths can be set in the /etc/sudo.conf file ๐Ÿ’–

askpass : The fully-qualified path to a helper program that reads the user's password when no terminal is available ๐Ÿ”‘ That can happen when sudo runs from a graphical app instead of a text-based one. The program set by askpass should display the argument passed to it as the prompt and write the user's password to standard output. The SUDO_ASKPASS environment variable can override the value of askpass โœจ

devsearch : An ordered, colon-separated search path of directories to look in for device nodes ๐Ÿ” This gets used when mapping the process's tty device number to a device name on systems that don't provide another way to do that. Sudo will not recurse into sub-directories. So if terminal devices live in a sub-directory of /dev, that path has to be explicitly listed in devsearch ๐Ÿซ  The default value is /dev/pts:/dev/vt:/dev/term:/dev/zcons:/dev/pty:/dev

This option is ignored on systems that support either the devname() or _ttyname_dev() functions, for example BSD, macOS, and Solaris ๐ŸŽ

intercept : The path to a shared library full of wrappers for the execve(2), execl(3), execle(3), execlp(3), execv(3), execvp(3), execvpe(3), and system(3) library functions ๐Ÿ•ต๏ธโ€โ™€๏ธ These wrappers intercept attempts to run further commands and do a policy check before letting them execute. This is how the intercept and log_subcmds functionality works on systems that support LD_PRELOAD or the equivalent.

The intercept path can be set to a single fully-qualified path or, on systems that support separate LD_PRELOAD environment variables for 32-bit and 64-bit executables, it can optionally be set to two fully-qualified paths separated by a colon (:). The first path should be the 32-bit version and the second the 64-bit version. This two-path form is currently only supported on AIX and Solaris systems. The default value is /usr/libexec/sudo/sudo_intercept.so.

noexec : The path to a shared library full of wrappers for the execve(2), execl(3), execle(3), execlp(3), exect(3), execv(3), execveat(3), execvP(3), execvp(3), execvpe(3), fexecve(3), popen(3), posix_spawn(3), posix_spawnp(3), system(3), and wordexp(3) library functions ๐Ÿšซ These wrappers stop further commands from executing. This is how the noexec functionality works on systems that support LD_PRELOAD or the equivalent.

The noexec path can be set to a single fully-qualified path or, on systems that support separate LD_PRELOAD environment variables for 32-bit and 64-bit executables, it can optionally be set to two fully-qualified paths separated by a colon (:). The first path should be the 32-bit version and the second the 64-bit version. This two-path form is currently only supported on AIX and Solaris systems. The default value is /usr/libexec/sudo/sudo_noexec.so.

plugin_dir : The default directory to search when looking for plugins that are specified without a fully-qualified path name ๐Ÿ“‚ The default value is /usr/libexec/sudo.

sesh : The fully-qualified path to the sesh binary ๐Ÿ” This setting is only used when sudo is built with SELinux support. The default value is /usr/libexec/sudo/sesh.

to be continued in part 2 ๐Ÿ’…โœจ

dynamic Always ask the group database directly. ๐Ÿ’… It's "dynamic" because any changes made to the group database after the user logs in will show up in the group list. โœจ On some systems, looking up all of a user's groups can take forever when the group database lives on the network ๐Ÿฅฒ, but most operating systems have an efficient way to do these queries. Right now sudo supports efficient group queries on AIX, BSD, Linux, macOS, and Solaris. ๐ŸŒธ This is the default on macOS in sudo 1.9.6 and higher.

adaptive Only query the group database if the static group list the kernel gives back is maxed out (it has the maximum number of entries). ๐Ÿงš This is the default on everything except macOS in sudo 1.8.7 and higher.

For example, if you want sudo to only use the kernel's static list of groups for the user:

Set group_source static

This setting is only available in sudo version 1.8.7 and higher. ๐Ÿซถ

๐Ÿ”ข max_groups ๐Ÿ”ข

The maximum number of user groups sudo will retrieve from the group database. ๐Ÿ“ Values less than one or larger than 1024 get ignored, so don't try it, bestie. ๐Ÿ™ˆ This only matters when sudo is querying the group database directly. It's meant for systems where there's no way to detect when the array that gets filled with group entries is too small. By default, sudo allocates four times the system's maximum number of groups (see above), and if the group database query fails, it retries with double that number.

This setting is only available in sudo version 1.8.7 and higher. It shouldn't be needed in sudo versions 1.8.24 and higher, and it may be removed in a later release. ๐Ÿ’€

๐ŸŒ probe_interfaces ๐ŸŒ

By default, sudo probes the system's network interfaces and hands the IP address of each enabled interface to the policy plugin. ๐Ÿฆ‹ This lets the plugin match rules by IP address without having to query DNS, which is so smart. On Linux systems with a huge number of virtual interfaces, this can take a non-negligible amount of time. โณ If you don't need IP-based matching, you can turn off the interface probing like this:

Set probe_interfaces false

This setting is only available in sudo version 1.8.10 and higher. ๐Ÿ’œ


๐Ÿ› Debug settings ๐Ÿ›

sudo versions 1.8.4 and higher come with a flexible debugging framework that can log what sudo is doing internally when something goes wrong. ๐Ÿ•ต๏ธโ€โ™€๏ธ It's giving detective energy.

A Debug line is made up of the Debug keyword, then the name of the program, plugin, or shared object you want to debug, then the debug file name, and finally a comma-separated list of debug flags. The debug flag syntax used by sudo, the sudoers plugin, and its associated programs and shared objects is subsystem@priority. Third-party plugins are free to use a different format, as long as it doesn't include a comma (,). ๐ŸŽ€

On AIX systems, a Debug line will match a plugin specified as any of these: the name of an SVR4-style shared object file ending in .so, an archive file ending in .a, or an archive file ending in .a with the name of the shared object in parentheses. ๐ŸŒˆ

Examples:

Debug sudo /var/log/sudo_debug all@warn,plugin@info

This would log all debugging statements at the warn level and higher, plus the ones at the info level for the plugin subsystem. ๐Ÿ“

Debug sudo_intercept.so /var/log/intercept_debug all@debug

This would log all debugging statements, no matter the level, for the sudo_intercept.so shared library, which implements sudo's intercept functionality on some systems. ๐Ÿ”

Debug sudoers.so /var/log/sudoers_debug all@debug

This would log all debugging statements, no matter the level, for the sudoers plugin. โœจ Check out sudoers(5) for the full list of subsystems the sudoers plugin supports. ๐Ÿ“š

As of sudo 1.8.12, you can specify multiple Debug entries per program. Older versions of sudo only supported a single Debug entry per program, so bestie, that's a glow up. ๐Ÿฆ‹ Plugin-specific Debug entries are also supported starting with sudo 1.8.12, and they're matched either by the base name of the loaded plugin (for example sudoers.so) or by the plugin's fully-qualified path name. Before that, the sudoers plugin shared the same Debug entry as the sudo front-end and couldn't be configured separately. ๐Ÿ˜ญ

The following priorities are supported, in order of decreasing severity: crit, err, warn, notice, diag, info, trace, and debug. Each priority, when specified, also includes every priority higher than it. For example, a priority of notice includes debug messages logged at notice and higher. ๐Ÿ“Š

The trace and debug priorities also include function call tracing, which logs when a function is entered and when it returns. ๐Ÿ•ฐ๏ธ For example, here's a trace for the get_user_groups() function, located in src/sudo.c:

sudo[123] -> get_user_groups @ src/sudo.c:385
sudo[123] <- get_user_groups @ src/sudo.c:429 := groups=10,0,5

When the function is entered, marked by a right arrow ->, the program, process ID, function, source file, and line number get logged. When the function returns, marked by a left arrow <-, the same info gets logged along with the return value. In this case, the return value is a string. ๐Ÿงต

The following subsystems are used by the sudo front-end:

  • all โ€” matches every subsystem ๐ŸŒŸ
  • args โ€” command line argument processing ๐Ÿงพ
  • conv โ€” user conversation ๐Ÿ’ฌ
  • edit โ€” sudoedit โœ๏ธ
  • event โ€” event subsystem ๐ŸŽ‰
  • exec โ€” command execution ๐Ÿš€
  • main โ€” sudo main function ๐Ÿ‘‘
  • netif โ€” network interface handling ๐ŸŒ
  • pcomm โ€” communication with the plugin ๐Ÿ“ก
  • plugin โ€” plugin configuration ๐Ÿ”Œ
  • pty โ€” pseudo-terminal related code ๐Ÿ–ฅ๏ธ
  • selinux โ€” SELinux-specific handling ๐Ÿ›ก๏ธ
  • util โ€” utility functions ๐Ÿงฐ
  • utmp โ€” utmp handling ๐Ÿชช

The sudoers(5) plugin includes support for additional subsystems, so go check those out too. ๐Ÿ’…


๐Ÿ“ FILES ๐Ÿ“

  • /etc/sudo.conf โ€” sudo front-end configuration ๐ŸŽ€

โœจ EXAMPLES โœจ

okay, here's the default /etc/sudo.conf file, and it's honestly a whole vibe ๐Ÿ’… every line starting with # is a comment, so nothing happens until you uncomment it ๐Ÿ™ˆ

#
# Default /etc/sudo.conf file
#
# Sudo plugins:
#   Plugin plugin_name plugin_path plugin_options ...
#
# The plugin_path is relative to /usr/libexec/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

Okay so the Plugin lines are how you pick your plugins ๐Ÿ”Œ The plugin_path is relative to /usr/libexec/sudo unless you write out the full path, and the plugin_name has to match a global symbol inside the plugin that holds its interface structure. The plugin_options part is totally optional โœจ And if you don't have any Plugin lines at all, sudo quietly falls back to the sudoers plugin, which is super iconic of it ๐Ÿ’œ


#
# Sudo askpass:
#   Path askpass /path/to/askpass
#
# An askpass helper program may be specified to provide a graphical
# password prompt for "sudo -A" support.  Sudo does not ship with its
# own askpass program but can use the OpenSSH askpass.
#
# Use the OpenSSH askpass
#Path askpass /usr/bin/ssh-askpass
#
# Use the Gnome OpenSSH askpass
#Path askpass /usr/libexec/openssh/gnome-ssh-askpass

For the askpass helper ๐Ÿซถ you can point it at a program that gives you a graphical password prompt for sudo -A. Sudo doesn't come with its own askpass program, but it can totally borrow the OpenSSH one, so you can pick /usr/bin/ssh-askpass or the Gnome flavour at /usr/libexec/openssh/gnome-ssh-askpass ๐ŸŽ€


#
# Sudo device search path:
#   Path devsearch /dev/path1:/dev/path2:/dev
#
# A colon-separated list of paths to check when searching for a user's
# terminal device.
#
#Path devsearch /dev/pts:/dev/vt:/dev/term:/dev/zcons:/dev/pty:/dev

The devsearch path is a colon-separated list of places sudo checks when it's hunting for a user's terminal device ๐Ÿ” Think of it as a little scavenger hunt through /dev ๐Ÿฆ‹


#
# Sudo command interception:
#   Path intercept /path/to/sudo_intercept.so
#
# Path to a shared library containing replacements for the execv(),
# execve() and fexecve() library functions, which perform a policy
# check to verify whether the command is allowed and simply return
# an error if it is not.  This is used to implement the "intercept"
# functionality on systems that support LD_PRELOAD or its equivalent.
#
# The compiled-in value is usually sufficient and should only be changed
# if you rename or move the sudo_intercept.so file.
#
#Path intercept /usr/libexec/sudo/sudo_intercept.so

Next up is intercept ๐Ÿงš This is a shared library full of replacement versions of execv(), execve() and fexecve(). Each one runs a policy check first to see if the command is allowed, and if it isn't, it just returns an error. It's how the "intercept" feature works on systems that support LD_PRELOAD or something equivalent ๐ŸŒˆ The built-in path is usually fine, so only change it if you rename or move sudo_intercept.so ๐Ÿ™ˆ


#
# Sudo noexec:
#   Path noexec /path/to/sudo_noexec.so
#
# Path to a shared library containing replacements for the execv()
# family of library functions that just return an error.  This is
# used to implement the "noexec" functionality on systems that support
# LD_PRELOAD or its equivalent.
#
# The compiled-in value is usually sufficient and should only be changed
# if you rename or move the sudo_noexec.so file.
#
#Path noexec /usr/libexec/sudo/sudo_noexec.so

Same energy for noexec ๐Ÿ’€ This shared library has replacements for the execv() family that simply return an error. It's how the "noexec" functionality works on systems with LD_PRELOAD or its equivalent. Again, the compiled-in value is usually perfect, so only touch it if you rename or move sudo_noexec.so โœจ


#
# Sudo plugin directory:
#   Path plugin_dir /path/to/plugins
#
# The default directory to use when searching for plugins that are
# specified without a fully-qualified path name.
#
#Path plugin_dir /usr/libexec/sudo

The plugin_dir is the default folder sudo searches when a plugin is listed without a fully-qualified path ๐Ÿ“‚ The example shows /usr/libexec/sudo, which is the classic spot ๐Ÿ“


#
# Core dumps:
#   Set disable_coredump true|false
#
# By default, sudo disables core dumps while it is executing (they
# are re-enabled for the command that is run).
# To aid in debugging sudo problems, you may wish to enable core
# dumps by setting "disable_coredump" to false.
#
#Set disable_coredump false

Core dumps are next ๐ŸŒธ By default, sudo turns core dumps off while it's running, and they get turned back on for the command that sudo runs. If you're debugging a sudo problem, you can set disable_coredump to false so core dumps stay on ๐Ÿซถ


#
# User groups:
#   Set group_source static|dynamic|adaptive
#
# Sudo passes the user's group list to the policy plugin.
# If the user is a member of the maximum number of groups (usually 16),
# sudo will query the group database directly to be sure to include
# the full list of groups.
#
# On some systems, this can be expensive so the behavior is configurable.
# The "group_source" setting has three possible values:
#   static   - use the user's list of groups returned by the kernel.
#   dynamic  - query the group database to find the list of groups.
#   adaptive - if user is in less than the maximum number of groups.
#              use the kernel list, else query the group database.
#
#Set group_source static

Now the group_source setting ๐Ÿ‘‘ Sudo hands the user's group list to the policy plugin. If the user is in the maximum number of groups (usually 16), sudo asks the group database directly so the full list makes it through. That can be pricey on some systems, which is why it's configurable. You've got three choices:

  • static โ€” use the user's group list returned by the kernel
  • dynamic โ€” query the group database for the list of groups every time
  • adaptive โ€” if the user is in fewer than the maximum number of groups, use the kernel list, otherwise query the group database

The example uses static ๐Ÿ’…


#
# Sudo interface probing:
#   Set probe_interfaces true|false
#
# By default, sudo will probe the system's network interfaces and
# pass the IP address of each enabled interface to the policy plugin.
# On systems with a large number of virtual interfaces this may take
# a noticeable amount of time.
#
#Set probe_interfaces false

probe_interfaces is up next ๐Ÿฆ‹ By default, sudo checks the system's network interfaces and passes the IP address of every enabled one to the policy plugin. If you have tons of virtual interfaces, this can take a noticeable amount of time, so you can turn it off with false ๐ŸŒˆ


#
# Sudo debug files:
#   Debug program /path/to/debug_log subsystem@priority[,subsyste@priority]
#
# Sudo and related programs support logging debug information to a file.
# The program is typically sudo, sudoers.so, sudoreplay, or visudo.
#
# Subsystems vary based on the program; "all" matches all subsystems.
# Priority may be crit, err, warn, notice, diag, info, trace, or debug.
# Multiple subsystem@priority may be specified, separated by a comma.
#
#Debug sudo /var/log/sudo_debug all@debug
#Debug sudoers.so /var/log/sudoers_debug all@debug

Last but not least, Debug lines ๐Ÿ” Sudo and its friends can log debug info to a file. The program is usually sudo, sudoers.so, sudoreplay or visudo. Subsystems depend on the program, and all matches every one of them. Priorities can be crit, err, warn, notice, diag, info, trace or debug, and you can stack several subsystem@priority pairs with a comma between them ๐Ÿงš

โœจ SEE ALSO โœจ

Check out sudo_plugin(5), sudoers(5) and sudo(8) for more of the good stuff ๐Ÿ“š

โœจ AUTHORS โœจ

Lots of people have worked on sudo over the years ๐Ÿซถ This version is mostly code written by:

Todd C. Miller

Want the full list of everyone who's helped out? See the CONTRIBUTORS.md file in the sudo distribution, at https://www.sudo.ws/about/contributors/ ๐Ÿ’–

โœจ BUGS โœจ

Think you found a bug in sudo.conf? ๐Ÿ™ˆ You can file a report in the sudo bug database at https://bugzilla.sudo.ws/, or open an issue at https://github.com/sudo-project/sudo/issues. If email is more your thing, you can send messages to the sudo-workers mailing list at https://www.sudo.ws/mailman/listinfo/sudo-workers (public) or to <sudo@sudo.ws> (private).

Please, please do NOT report security vulnerabilities through public GitHub issues, Bugzilla or mailing lists ๐Ÿ˜ญ Instead, email them to <Todd.Miller@sudo.ws>. You can encrypt your message with PGP if you want, using the key at https://www.sudo.ws/dist/PGPKEYS.

โœจ SUPPORT โœจ

There's limited free support through the sudo-users mailing list ๐Ÿ’œ To subscribe or search the archives, head to https://www.sudo.ws/mailman/listinfo/sudo-users.

โœจ DISCLAIMER โœจ

sudo comes "AS IS", so any express or implied warranties, including the implied warranties of merchantability and fitness for a particular purpose, are disclaimed ๐Ÿ™ƒ For the full details, check the LICENSE.md file that ships with sudo, or visit https://www.sudo.ws/about/license/.


And that's a wrap on SUDO.CONF(5) ๐ŸŽ€ Sudo 1.9.17p2, dated November 6, 2023, and you're officially a sudo.conf expert now ๐Ÿ’…โœจ bestie, you did amazing ๐Ÿซถ๐ŸŒˆ

๐Ÿ“ฆ where this page comes from