SUDOERS(5)
๐ sudoers(5) ๐
File Formats Manual, the bestie of all sudo docs ๐
โจ NAME โจ
okay so the name is sudoers ๐ธ and it's the default sudo security policy plugin! it's literally the one that decides who gets to use sudo ๐
โจ DESCRIPTION โจ
okay bestie, so the sudoers policy plugin figures out a user's sudo privileges ๐
it's the default sudo policy plugin, so it just shows up. the policy is driven by the /etc/sudoers file or, optionally, in LDAP โ๏ธ the policy format gets the full breakdown in the "SUDOERS FILE FORMAT" section, and if you want to store sudoers policy in LDAP, check out sudoers.ldap(5) ๐ฆ
โจ Configuring sudo.conf for sudoers โจ
so sudo checks the sudo.conf(5) file to see which plugins it should load ๐ง if there's no sudo.conf(5) file, or it has no Plugin lines in it, sudoers gets used for auditing, policy decisions and I/O logging. like, it's the default girlie for everything ๐ if you want to explicitly tell sudo.conf(5) to use the sudoers plugin, you can do this:
Plugin sudoers_audit sudoers.so
Plugin sudoers_policy sudoers.so
Plugin sudoers_io sudoers.so
starting with sudo 1.8.5, you can pass optional arguments to the sudoers plugin in the sudo.conf(5) file ๐ plugin arguments (if you have any) go after the path to the plugin, so after sudoers.so. these arguments only do something for the plugin that opens (and parses) the sudoers file, so they're kinda picky ๐
for sudo version 1.9.1 and higher, that plugin is sudoers_audit. for older versions, it's sudoers_policy โจ you can throw in multiple arguments, just separate them with white space. for example:
Plugin sudoers_audit sudoers.so sudoers_mode=0400 error_recovery=false
these are the plugin arguments that are supported, bestie ๐ซถ
error_recovery=boolโ this one controls whether sudoers tries to bounce back from syntax errors in the sudoers file ๐ when it's set totrue(the default), sudoers will try to recover by tossing out the part of the line with the error, all the way to the end of that line. setting it tofalseturns error recovery off. before version 1.9.3, no error recovery was done at all, so it was a whole vibe ๐ญignore_perms=boolโ this one can disable the security checks when the sudoers file is loaded ๐ if you turn it on, the sudoers file gets loaded no matter who owns it or what its file mode is. this one is meant for testing only, so please don't turn it on on production systems, it's giving danger ๐ldap_conf=pathnameโ use this to override the default path to theldap.conffile ๐บ๏ธldap_secret=pathnameโ use this to override the default path to theldap.secretfile ๐sudoers_file=pathnameโ use this to override the default path to the sudoers file ๐sudoers_uid=user-IDโ use this to override the default owner of the sudoers file ๐ it should be a numeric user-ID.sudoers_gid=group-IDโ use this to override the default group of the sudoers file ๐ซง it must be a numeric group-ID, not a group name, so don't get it twisted ๐sudoers_mode=modeโ use this to override the default file mode for the sudoers file ๐ it should be an octal value.
for more on configuring sudo.conf(5), go check out its manual ๐
โจ User Authentication โจ
the sudoers security policy says most users have to authenticate themselves before they can use sudo ๐ a password isn't needed if the invoking user is root, if the target user is the same as the invoking user, or if the policy has turned off authentication for that user or command. like, no password needed, it's giving free pass ๐ฆ unlike su(1), when sudoers needs authentication, it checks the invoking user's credentials, not the target user's (or root's) credentials ๐ this can be changed with the rootpw, targetpw and runaspw flags, which we'll get into later.
if someone who isn't listed in the policy tries to run a command with sudo, mail gets sent to the proper authorities ๐ง the address for that mail is configurable through the mailto Defaults entry (also later!) and it defaults to root.
no mail gets sent if an unauthorized user tries to run sudo with the -l or -v option, unless there's an authentication error and either the mail_always or mail_badpass flags are enabled ๐ this lets users figure out on their own whether they're allowed to use sudo, which is honestly so considerate. by default, every attempt to run sudo gets logged (successful or not), no matter whether mail was sent or not ๐
if sudo is run by root and the SUDO_USER environment variable is set, the sudoers policy uses that value to figure out who the actual user is ๐ต๏ธโโ๏ธ this lets someone log commands through sudo even when a root shell has been invoked, which is iconic. it also keeps the -e option useful even when it's invoked via a sudo-run script or program โจ just note that the sudoers file lookup is still done for root, not for the user in SUDO_USER ๐
sudoers uses per-user time stamp files for credential caching ๐ง once a user has been authenticated, a record gets written with the user-ID that was used to authenticate, the terminal session ID, the start time of the session leader (or parent process), and a time stamp (using a monotonic clock if one is available). then the user can use sudo without a password for a short period of time: 15 minutes, unless you override it with the timestamp_timeout option โณ by default, sudoers uses a separate record for each terminal, which means a user's login sessions get authenticated separately ๐
the timestamp_type option can be used to pick the type of time stamp record sudoers will use.
the tsdump utility, which comes with the sudo source distribution, can display the contents of a time stamp file ๐ see sudoers_timestamp(5) for all the details on the time stamp file format.
โจ Logging โจ
by default, sudoers logs both successful and unsuccessful attempts (plus errors) ๐ the log_allowed and log_denied flags control this behaviour. messages can go to syslog(3), a log file, or both ๐ the default is syslog(3), but you can change that with the syslog and logfile settings. check out "EVENT LOGGING" for a description of the log file format ๐ท
sudoers can also run a command in a pseudo-terminal and log input and/or output ๐ฌ standard input, standard output, and standard error can all be logged, even when they're not connected to a terminal. for more about I/O logging, see the "I/O LOGGING" section ๐
starting with version 1.9, the log_servers setting can be used to send event and I/O log data to a remote server running sudo_logsrvd, or to another service that implements the protocol described by sudo_logsrv.proto(5) ๐ฐ๏ธ
โจ Command environment โจ
okay this part is so important, bestie. environment variables can totally influence how a program behaves, so sudoers gives you a way to restrict which variables from the user's environment get inherited by the command being run ๐ง there are two distinct ways sudoers can handle environment variables ๐
by default, the env_reset flag is enabled ๐งผ this makes commands run with a brand new, minimal environment. on AIX (and on Linux systems without PAM), the environment gets initialized with the contents of the /etc/environment file ๐ the HOME, MAIL, SHELL, LOGNAME and USER environment variables get set based on the target user, and the SUDO_* variables get set based on the invoking user ๐
other variables, like DISPLAY, PATH and TERM, are kept from the invoking user's environment if the env_check or env_keep options allow it โจ a few variables get special treatment though. if PATH and TERM aren't kept from the user's environment, they get set to default values ๐ซถ LOGNAME and USER are handled as a single entity: if one of them is kept (or removed) from the user's environment, the other one is too ๐ฏ if LOGNAME and USER are supposed to be kept but only one of them is in the user's environment, the other gets set to the same value. this stops you from ending up with an inconsistent environment where one of the user name variables says the invoking user and the other says the target user ๐ and environment variables whose value begins with โ()โ get removed unless both the name and value parts are matched by env_keep or env_check, because bash may read them as functions ๐ before version 1.8.11, those were always removed.
but if the env_reset flag is disabled, any variables that aren't explicitly denied by the env_check and env_delete options get allowed, and their values are inherited from the invoking process ๐ฌ before version 1.8.21, environment variables with a value beginning with โ()โ were always removed. starting with version 1.8.21, a pattern in env_delete is used to match bash shell functions instead ๐งฉ since it's impossible to block every potentially dangerous environment variable, using the default env_reset behaviour is strongly encouraged ๐ซถ
environment variables specified by env_check, env_delete, or env_keep can include one or more * characters, which match zero or more characters ๐ no other wildcard characters are supported.
by default, environment variables are matched by name โจ however, if the pattern includes an equal sign (=), both the variable's name and value must match ๐ฏ for example, a bash shell function could be matched like this:
env_keep += "BASH_FUNC_my_func%%=()*"
without the =()* suffix, this wouldn't match, because bash shell functions aren't preserved by default ๐
the complete list of environment variables that are preserved or removed, as modified by global Defaults parameters in sudoers, gets displayed when sudo is run by root with the -V option ๐ the list of environment variables to remove depends on the operating system sudo is running on.
other settings can influence the command environment too, bestie ๐
- sudoers options such as
always_set_home,secure_path,set_logname,set_home, andsetenv. - Command tags, such as
SETENVandNOSETENV. note thatSETENVis implied if the command matched isALL๐ - sudo options, such as
-Eand-i.
Okay so the PAM stuff is next, and bestie, it's giving "who's the priority, PAM or sudo?" ๐
On systems where the pam_env module is enabled for sudo, variables in the PAM environment can get merged into the environment. If a variable is already in the user's environment, the PAM value only overrides it when sudoers didn't preserve that variable. ๐ซถ When env_reset is enabled, variables kept from the invoking user's environment through the env_keep list win over the PAM ones. When env_reset is disabled, the invoking user's variables win over PAM ones, unless they match a pattern in the env_delete list. Main character energy for env_keep โจ
The dynamic linker on most operating systems strips variables that control dynamic linking from the environment of set-user-ID executables, and that includes sudo ๐ญ Depending on the OS that can mean _RLD*, DYLD_*, LD_*, LDR_*, LIBPATH, SHLIB_PATH, and others. These get removed before sudo even starts running, so sudo literally cannot preserve them. Not me being locked out of my own variables ๐
Now the special case: if you use the -i option (initial login), sudoers sets up the environment no matter what env_reset is set to ๐ธ DISPLAY, PATH and TERM stay unchanged, while HOME, MAIL, SHELL, USER, and LOGNAME get set based on the target user. On AIX, and on Linux systems without PAM, the contents of /etc/environment get included too. Everything else gets removed unless env_keep or env_check lets it through, which we just covered ๐
Last thing: the restricted_env_file and env_file files get applied if they exist ๐ฆ The variables in restricted_env_file go first and have the same restrictions as the invoking user's environment we talked about. The variables in env_file go last and are NOT subject to those restrictions, so it's the VIP lane. In both cases, a variable from the file only gets its specified value if it wouldn't clash with an existing environment variable. โจ
โจ SUDOERS FILE FORMAT โจ
Okay the sudoers file is made of two kinds of entries: aliases (basically variables) and user specifications, which decide who may run what ๐ฅ
When multiple entries match a user, they get applied in order. If several match, the last one wins, which is not always the most specific one, so read carefully bestie ๐ง
The sudoers file grammar gets explained below in Extended Backus-Naur Form (EBNF). Don't panic if EBNF is new to you ๐ It's honestly pretty simple, and the definitions below come with notes.
Resource limits
By default, sudoers uses the operating system's native way of setting resource limits for the target user ๐ On Linux systems that's usually handled by the pam_limits.so PAM module. On some BSD systems, the /etc/login.conf file sets the resource limits for the user. On AIX, resource limits are configured in the /etc/security/limits file. If there's no system mechanism for per-user resource limits, the command runs with the same limits as the invoking user. The one exception is the core dump file size, which sudoers sets to 0 by default. Turning off core dumps by default helps avoid potential security problems where the core file gets treated as trusted input. Iconic safety move ๐ก๏ธ
Resource limits can also be set in the sudoers file itself, and when they are, they override the ones from the system. Check out the rlimit_as, rlimit_core, rlimit_cpu, rlimit_data, rlimit_fsize, rlimit_locks, rlimit_memlock, rlimit_nofile, rlimit_nproc, rlimit_rss, rlimit_stack options described below ๐ Resource limits in sudoers can be written in one of these formats:
"value"โ Both the soft and hard limits get set to the same value. The special value"infinity"means unlimited, no cap, iykyk ๐"soft,hard"โ Two comma-separated values. The first one sets the soft limit and the second one sets the hard limit. Both values need to be wrapped in double quotes, or the comma needs a backslash escape (\). The special value"infinity"can be used in place of either value โจ"default"โ The user's default resource limit gets used. This could be a user-specific value (see above), or the value the resource limit had when sudo was invoked, on systems that don't support per-user limits ๐"user"โ The invoking user's resource limits get kept when the command runs ๐
For example, if you want the old-school core dump file size behaviour back, you can add a line like this:
Defaults rlimit_core=default
Resource limits in sudoers are only supported in version 1.8.7 or higher. Be careful with older versions bestie ๐ญ
Quick guide to EBNF
EBNF is a concise and exact way of describing the grammar of a language ๐ฆ Every EBNF definition is made up of production rules. For example:
symbol ::= definition | alternate1 | alternate2 ...
Each production rule references others, and together they make up a grammar for the language. EBNF also has some operators that lots of people already know from regular expressions. Don't mix them up with "wildcard" characters though, because those mean something different ๐
?โ The preceding symbol (or group of symbols) is optional. It may appear once or not at all.*โ The preceding symbol (or group of symbols) may appear zero or more times.+โ The preceding symbol (or group of symbols) may appear one or more times.
You can use parentheses to group symbols together. To keep things clear, verbatim character strings (as opposed to symbol names) will be wrapped in single quotes ('') ๐
Aliases
There are four kinds of aliases: User_Alias, Runas_Alias, Host_Alias and Cmnd_Alias. Starting with sudo 1.9.0, Cmd_Alias can be used in place of Cmnd_Alias if you like ๐
Alias ::= 'User_Alias' User_Alias_Spec (':' User_Alias_Spec)* |
'Runas_Alias' Runas_Alias_Spec (':' Runas_Alias_Spec)* |
'Host_Alias' Host_Alias_Spec (':' Host_Alias_Spec)* |
'Cmnd_Alias' Cmnd_Alias_Spec (':' Cmnd_Alias_Spec)* |
'Cmd_Alias' Cmnd_Alias_Spec (':' Cmnd_Alias_Spec)*
User_Alias ::= NAME
User_Alias_Spec ::= User_Alias '=' User_List
Runas_Alias ::= NAME
Runas_Alias_Spec ::= Runas_Alias '=' Runas_List
Host_Alias ::= NAME
Host_Alias_Spec ::= Host_Alias '=' Host_List
Cmnd_Alias ::= NAME
Cmnd_Alias_Spec ::= Cmnd_Alias '=' Cmnd_List
NAME ::= [AโZ]([AโZ][0โ9]_)*
Each alias definition looks like this:
Alias_Type NAME = item1, item2, ...
Where Alias_Type is one of User_Alias, Runas_Alias, Host_Alias, or Cmnd_Alias. A NAME is a string of uppercase letters, numbers, and underscores (_). A NAME has to start with an uppercase letter ๐ You can put several alias definitions of the same type on one line by separating them with a colon (:). For example:
Alias_Type NAME = item1, item2, item3 : NAME = item4, item5
Redefining an existing alias is a syntax error, so don't do it bestie ๐ซ You can reuse the same name for aliases of different types, but it's not recommended. Messy ๐
The rules for what counts as a valid alias member come next ๐ธ
User_List ::= User |
User ',' User_List
User ::= '!'* user name |
'!'* #user-ID |
'!'* %group |
'!'* %#group-ID |
'!'* +netgroup |
'!'* %:nonunix_group |
'!'* %:#nonunix_gid |
'!'* User_Alias
A User_List is made of one or more user names, user-IDs (prefixed with #), system group names and IDs (prefixed with % and %# respectively), netgroups (prefixed with +), non-Unix group names and IDs (prefixed with %: and %:# respectively), and User_Aliases. Each list item can be prefixed with zero or more ! operators. An odd number of ! negates the item, and an even number just cancels out ๐ User netgroups are matched using only the user and domain members. The host member isn't used when matching.
A user name, user-ID, group, group-ID, netgroup, nonunix_group or nonunix_gid can be wrapped in double quotes so you don't have to escape special characters. You can also write special characters in escaped hex mode, for example \x20 for a space. When you use double quotes, any prefix characters have to go inside the quotes too โจ
The actual nonunix_group and nonunix_gid syntax depends on the group provider plugin being used. For instance, the QAS AD plugin supports these formats:
- Group in the same domain:
"%:Group Name" - Group in any domain:
"%:Group Name@FULLY.QUALIFIED.DOMAIN" - Group SID:
"%:S-1-2-34-5678901234-5678901234-5678901234-567"
Check out โGROUP PROVIDER PLUGINSโ for more info ๐ง
Quotes around group names are optional. Unquoted strings need a backslash (\) to escape spaces and special characters. See โOther special characters and reserved wordsโ for a list of characters that need escaping ๐ฆ
Runas_List ::= Runas_Member |
Runas_Member ',' Runas_List
Runas_Member ::= '!'* user name |
'!'* #user-ID |
'!'* %group |
'!'* %#group-ID |
'!'* %:nonunix_group |
'!'* %:#nonunix_gid |
'!'* +netgroup |
'!'* Runas_Alias |
'!'* ALL
A Runas_List is similar to a User_List, except that instead of User_Aliases it can hold Runas_Aliases. User names and groups get matched as strings, which means two users (or groups) with the same user (or group) ID are considered distinct ๐ญ If you want to match every user name that shares a user-ID (like root and toor), you can use a user-ID instead of a name (#0 in the example given). The user-ID or group-ID in a Runas_Member doesn't need to be listed in the password or group database ๐
Host_List ::= Host |
Host ',' Host_List
Host ::= '!'* host name |
'!'* ip_addr |
'!'* network(/netmask)? |
'!'* +netgroup |
'!'* Host_Alias |
'!'* ALL
Okay so the host stuff keeps going, bestie ๐ A Host_List is one or more host names, IP addresses, network numbers, netgroups (prefixed with +), and other aliases ๐ฆ Same deal as before: you can negate any item with the ! operator ๐
โโ๏ธ
Netgroups get matched using the host (qualified and unqualified) and domain members only. The user member is NOT used when matching ๐
If you give a network number without a netmask, sudo checks each of the local host's network interfaces. If the network number matches one of them, sudo uses that interface's netmask โจ The netmask can be written in standard IP address notation (like 255.255.255.0 or ffff:ffff:ffff:ffff::) or CIDR notation (number of bits, like 24 or 64) ๐ธ
A host name can use shell-style wildcards (see the โWildcardsโ section below) ๐ BUT, unless the hostname command on your machine returns the fully qualified host name, you'll need the fqdn flag for wildcards to actually be useful ๐ญ
Also, sudo only inspects actual network interfaces, so IP address 127.0.0.1 (localhost) will never match ๐ And the host name โlocalhostโ only matches if that's the actual host name, which is usually only the case for non-networked systems ๐ง
Now let's get into the digest grammar, it's giving formal ๐ค
digest ::= [AโFaโf0โ9]+ |
[AโZaโz0โ9\+/=]+
Digest_Spec ::= "sha224" ':' digest |
"sha256" ':' digest |
"sha384" ':' digest |
"sha512" ':' digest
Digest_List ::= Digest_Spec |
Digest_Spec ',' Digest_List
Cmnd_List ::= Cmnd |
Cmnd ',' Cmnd_List
command name ::= regex |
file name
command ::= command name |
command name args |
command name regex |
command name '""' |
ALL
Edit_Spec ::= "sudoedit" file name+ |
"sudoedit" regex |
"sudoedit"
List_Spec ::= "list"
Cmnd ::= Digest_List? '!'* command |
'!'* directory |
'!'* Edit_Spec |
'!'* List_Spec |
'!'* Cmnd_Alias
A Cmnd_List is a list of one or more commands, directories, or aliases ๐ซถ A command is a fully qualified file name (wildcards allowed, see โWildcardsโ), or a regular expression that starts with ^ and ends with $ (see the โRegular expressionsโ section below) ๐
A directory is a fully qualified path name ending in a / ๐ When you put a directory in a Cmnd_List, the user can run any file inside that directory (but NOT in any sub-directories in there) ๐
If no command line arguments are specified, the user may run the command with whatever arguments they want ๐ Command line arguments can include wildcards or be a regular expression that starts with ^ and ends with $ โจ If the command line arguments are "", the command can only run with NO arguments ๐
โโ๏ธ
If a Cmnd has command line arguments, the arguments in the Cmnd must match what the user types on the command line ๐ If the arguments in a Cmnd begin with the ^ character, they're treated as a regular expression and matched that way. Otherwise, shell-style wildcards are used ๐ฆ Unless you're using a regular expression, these characters must be escaped with a \ if they show up in command arguments: ,, :, =, \ ๐ And if you want an argument starting with ^ to NOT be treated as a regular expression, escape the ^ with a \ ๐ง
There are two built-in commands in sudo itself: โlistโ and โsudoeditโ ๐ Unlike other commands, these two must be written in the sudoers file WITHOUT a leading path ๐ โโ๏ธ
The โlistโ built-in lets a user list another user's privileges with sudo's -U option, like โsudo -l -U otheruserโ ๐ธ A user with the โlistโ privilege can list another user's privileges even if they aren't allowed to run commands as that user ๐ By default, only root, or a user who can run any command as root or the specified user on the current host, may use the -U option ๐ No command line arguments may be specified with the โlistโ built-in ๐ซ
The โsudoeditโ built-in lets a user run sudo with the -e option (or as sudoedit) โจ It can take command line arguments just like a normal command ๐ Unlike other commands, โsudoeditโ is built into sudo itself and must be written in the sudoers file WITHOUT a leading path ๐ฆ If there IS a leading path, like /usr/bin/sudoedit, the path name gets silently converted to โsudoeditโ ๐คซ A fully-qualified path for sudoedit is treated as an error by visudo ๐ญ
A command may be preceded by a Digest_List, which is a comma-separated list of one or more Digest_Spec entries ๐ฅ If there's a Digest_List, the command only matches if it can be verified using one of the SHA-2 digests in the list ๐ Starting with version 1.9.0, the ALL reserved word can be used together with a Digest_List ๐ The supported digest formats are sha224, sha256, sha384, and sha512 ๐ The string can be in hex or base64 format (base64 is more compact) ๐ There are several utilities that can generate SHA-2 digests in hex format, like openssl, shasum, sha224sum, sha256sum, sha384sum, and sha512sum ๐ง
For example, using openssl:
$ openssl dgst -sha224 /bin/ls
SHA224(/bin/ls)= 118187da8364d490b4a7debbf483004e8f3e053ec954309de2c41a25
You can also use openssl to generate base64 output:
$ openssl dgst -binary -sha224 /bin/ls | openssl base64
EYGH2oNk1JC0p9679IMATo8+BT7JVDCd4sQaJQ==
๐จ Warning, bestie: if the user has write access to the command itself (directly or via a sudo command), it may be possible for them to replace the command AFTER the digest check has been done but BEFORE the command runs. A similar race condition exists on systems that lack the fexecve(2) system call when the directory the command lives in is writable by the user. See the description of the fdexec setting for more info on how sudo executes commands that have an associated digest ๐ญ
Command digests are only supported by version 1.8.7 or higher ๐ฅบ
โจ Defaults โจ
Certain configuration options can be changed from their default values at run-time via one or more Default_Entry lines ๐ These can affect all users on any host (Defaults), all users on a specific host (Defaults@host), a specific user (Defaults:user), a specific command (Defaults!cmnd), or commands being run as a specific user (Defaults>runasuser) ๐
No white space is allowed between Defaults and the @, :, !, or > characters ๐
โโ๏ธ You can use a comma-separated list in place of a single value after the @, :, !, or > character, but using an alias instead of a list is often improve readability ๐
Per-command entries may NOT include command line arguments. If you need arguments, define a Cmnd_Alias and reference that instead ๐ง
Default_Type ::= 'Defaults' |
'Defaults@' Host_List |
'Defaults:' User_List |
'Defaults!' Cmnd_List |
'Defaults>' Runas_List
Default_Entry ::= Default_Type Parameter_List
Parameter_List ::= Parameter |
Parameter ',' Parameter_List
Parameter ::= Parameter '=' Value |
Parameter '+=' Value |
Parameter '-=' Value |
'!'* Parameter
Parameters can be flags, integer values, strings, or lists ๐ Flags are implicitly boolean and can be turned off with the ! operator ๐ Some integer, string, and list parameters can also be used in a boolean context to disable them ๐ซ Values may be wrapped in double quotes ("") when they contain multiple words โจ Special characters can be escaped with a backslash (\) ๐ฆ
To include a literal backslash in a command line argument, you have to escape the backslash TWICE ๐คฏ For example, to match \n as part of a command line argument, you must use \\\\n in the sudoers file ๐ญ This is because there are two levels of escaping: one in the sudoers parser itself, and another when command line arguments are matched by the fnmatch(3) or regexec(3) function ๐ง
Lists have two extra assignment operators, += and -= ๐ These are used to add to and delete from a list, respectively ๐ธ It is NOT an error to use the -= operator to remove an element that doesn't exist in a list ๐
In general, Defaults settings are applied in order, so later entries override earlier ones ๐ However, command-specific Defaults settings are applied later, once the command's path is known ๐ Also, the following Defaults settings must be applied before all others, since they may affect subsequent entries: fqdn, group_plugin, ignore_unknown_defaults, match_group_by_gid, runas_default, sudoers_locale ๐ซถ
See โSUDOERS OPTIONSโ for a list of supported Defaults parameters ๐
โจ User specification โจ
User_Spec ::= User_List Host_List '=' Cmnd_Spec_List \
(':' Host_List '=' Cmnd_Spec_List)*
Cmnd_Spec_List ::= Cmnd_Spec |
Cmnd_Spec ',' Cmnd_Spec_List
Cmnd_Spec ::= Runas_Spec? Option_Spec* (Tag_Spec ':')* Cmnd
Runas_Spec ::= '(' Runas_List? (':' Runas_List)? ')'
Option_Spec ::= (SELinux_Spec | Date_Spec | Timeout_Spec | Chdir_Spec | Chroot_Spec)
SELinux_Spec ::= ('ROLE=role' | 'TYPE=type')
AppArmor_Spec ::= 'APPARMOR_PROFILE=profile'
Date_Spec ::= ('NOTBEFORE=timestamp' | 'NOTAFTER=timestamp')
Timeout_Spec ::= 'TIMEOUT=timeout'
Chdir_Spec ::= 'CWD=directory'
Chroot_Spec ::= 'CHROOT=directory'
Tag_Spec ::= ('EXEC' | 'NOEXEC' | 'FOLLOW' | 'NOFOLLOW' |
'LOG_INPUT' | 'NOLOG_INPUT' | 'LOG_OUTPUT' |
'NOLOG_OUTPUT' | 'MAIL' | 'NOMAIL' | 'INTERCEPT' |
'NOINTERCEPT' | 'PASSWD' | 'NOPASSWD' | 'SETENV' |
'NOSETENV')
A user specification decides which commands a user may run (and as WHO) on specified hosts ๐ By default, commands run as root (unless runas_default has been set to something else), but you can change that on a per-command basis ๐
The basic structure of a user specification is โwho where = (as_whom) whatโ ๐ Let's break that down into its constituent parts:
๐ Runas_Spec ๐
okay so a Runas_Spec decides who a command is allowed to run as ๐ A full one is two Runas_Lists (as defined above) separated by a colon (:) and wrapped in parentheses. The first list says which users the command can run as with the -u option. The second is a list of groups you can pick with -g (on top of any of the target user's groups) ๐
here's how the combos shake out โจ
- if you've got both lists, the command can run with any combo of users and groups in them ๐ซถ
- if you only have the first, it can run as any user in that list and, optionally, with any group that target user belongs to
- if the first is empty but the second isn't, it runs as the invoking user with the group set to whatever's in the Runas_List ๐ธ
- if both are empty, it can only run as the invoking user, and the group (if you specify one) has to be one the invoking user is a member of ๐ฆ
- if there's no Runas_Spec at all, it can only run as the runas_default user (root by default), and the group (if specified) has to be one the runas_default user is in ๐ญ
a Runas_Spec sets the default for every command after it in the entry, so this:
dgb boulder = (operator) /bin/ls, /bin/kill, /usr/bin/lprm
means user dgb can run /bin/ls, /bin/kill, and /usr/bin/lprm on the host boulder, but only as operator ๐ like:
$ sudo -u operator /bin/ls
you can also override a Runas_Spec partway through an entry ๐ฅ if we change it to this:
dgb boulder = (operator) /bin/ls, (root) /bin/kill, /usr/bin/lprm
then dgb can run /bin/ls as operator, but /bin/kill and /usr/bin/lprm as root ๐
we can also let dgb run /bin/ls with either the user or the group set to operator ๐
dgb boulder = (operator : operator) /bin/ls, (root) /bin/kill,\
/usr/bin/lprm
the group part of a Runas_Spec lets the user run as that group, but it doesn't force them to. if no group is given on the command line, the command runs with the group from the target user's password database entry. all of these are allowed by the sudoers entry above:
$ sudo -u operator /bin/ls
$ sudo -u operator -g operator /bin/ls
$ sudo -g operator /bin/ls
in this one, user tcm can run commands that touch a modem device file with the dialer group ๐ง
tcm boulder = (:dialer) /usr/bin/tip, /usr/bin/cu,\
/usr/local/bin/minicom
in this case only the group gets set, and the command still runs as user tcm:
$ sudo -g dialer /usr/bin/cu
a Runas_Spec can also hold multiple users and groups, and then the user can pick any combo with -u and -g ๐ง here, alan can run any command as either root or bin, and optionally set the group to operator or system:
alan ALL = (root, bin : operator, system) ALL
๐ Option_Spec ๐
a Cmnd can have zero or more options attached ๐ options can be SELinux roles and/or types, AppArmor profiles, start and/or end dates, and command timeouts โญ once you set an option on a Cmnd, every Cmnd after it in the Cmnd_Spec_List inherits that option, unless another option overrides it ๐ซถ heads up: option names are reserved words in sudoers, so none of the valid option names (see below) can be used when you declare an alias ๐
๐ฆ SELinux_Spec ๐ฆ
on systems with SELinux support, sudoers entries can optionally have an SELinux role and/or type tied to a command. this can be used for a form of role-based access control (RBAC) ๐ฅ a role or type set in sudoers overrides any default values, but a role or type given on the command line supersedes the ones in sudoers ๐
๐ง AppArmor_Spec ๐ง
on systems that support AppArmor, sudoers entries can optionally name an AppArmor profile to confine a command โจ if a profile is given with the command, it overrides any default values in sudoers. you also need proper profile transition rules defined to support the profile change for a user ๐ก๏ธ
AppArmor profiles can be written any way that follows the rules of aa_change_profile(2). for example, in this entry:
alice ALL = (root) APPARMOR_PROFILE=my-profile ALL
user alice can run any command as root under confinement by the profile my-profile ๐ you can also stack profiles, or let someone run commands unconfined by any profile:
bob ALL = (root) APPARMOR_PROFILE=foo//&bar /usr/bin/vi
cathy ALL = (root) APPARMOR_PROFILE=unconfined /bin/ls
so user bob can run /usr/bin/vi as root under the stacked profiles foo and bar, and user cathy can run /bin/ls with zero confinement at all ๐ธ
๐ Date_Spec ๐
sudoers rules can have a start and end date with the NOTBEFORE and NOTAFTER settings โฐ the time stamp has to be in "Generalized Time" as defined by RFC 4517. the format is basically yyyymmddHHMMSSZ, where the minutes and seconds are optional. the Z at the end means the time is in Coordinated Universal Time (UTC) ๐ you can also give a timezone offset from UTC in hours and minutes instead of a Z. for example, -0500 would be Eastern Standard time in the US. as an extension, if you don't add a Z or a timezone offset, local time gets used ๐ซถ
these are all valid time stamps:
20170214083000Z
2017021408Z
20160315220000-0500
20151201235900
โณ Timeout_Spec โณ
a command can have a timeout attached ๐ if the timeout runs out before the command exits, the command gets terminated ๐ญ you write timeouts as combos of days, hours, minutes, and seconds, each with a single-letter suffix that tells the unit (and it's not case-sensitive) โจ for example, 7 days, 8 hours, 30 minutes, and 10 seconds is written 7d8h30m10s. if you leave off the unit, seconds are assumed ๐ you can skip any of the days, minutes, hours, or seconds. the order has to go from largest to smallest unit, and no unit can show up more than once ๐
โโ๏ธ
these are all valid timeout values: 7d8h30m10s, 14d, 8h30m, 600s, 3600 ๐
these are invalid timeout values: 12m2w1d, 30s10m4h, 1d2d3h โ
this setting is only supported by version 1.8.20 or higher. so if you're on something older, it won't work ๐ฅบ
๐ Chdir_Spec ๐
you can set the working directory the command runs in with the CWD setting ๐๏ธ the directory has to be a fully-qualified path name starting with a / or ~ character, or the special value *. a value of * means the user can pick the working directory themselves by running sudo with the -D option ๐ by default, commands run from the invoking user's current working directory, unless the -i option is given. path names like ~user/path/name are read as relative to that named user's home directory. if you leave out the user name, the path is relative to the runas user's home directory ๐ก
this setting is only supported by version 1.9.3 or higher.
๐ Chroot_Spec ๐
support for setting the root directory is deprecated and will be removed in a future version of sudo. so please don't build anything new on it ๐
you can set the root directory the command runs in with the CHROOT setting ๐ฆ the directory has to be a fully-qualified path name starting with a / or ~ character, or the special value *. a value of * means the user can pick the root directory by running sudo with the -R option ๐ซฃ this setting can run the command in a chroot(2) "sandbox", similar to the chroot(8) utility. path names like ~user/path/name are relative to the named user's home directory, and if the user name is left out, the path is relative to the runas user's home directory ๐ธ
this setting is only supported by version 1.9.3 or higher.
๐ท๏ธ Tag_Spec ๐ท๏ธ
a command can have zero or more tags attached ๐ these tag values are supported: EXEC, NOEXEC, FOLLOW, NOFOLLOW, LOG_INPUT, NOLOG_INPUT, LOG_OUTPUT, NOLOG_OUTPUT, MAIL, NOMAIL, INTERCEPT, NOINTERCEPT, PASSWD, NOPASSWD, SETENV, and NOSETENV โจ once a tag is set on a Cmnd, every Cmnd after it in the Cmnd_Spec_List inherits that tag, unless it gets overridden by the opposite tag. so PASSWD overrides NOPASSWD, and NOEXEC overrides EXEC ๐ฅ
๐ซ EXEC and NOEXEC ๐ซ
if sudo was compiled with noexec support and the underlying operating system supports it, the NOEXEC tag can stop a dynamically-linked executable from running further commands itself ๐ โโ๏ธ
in this example, user aaron can run /usr/bin/more and /usr/bin/vi on the host shanty, but shell escapes are disabled ๐
aaron shanty = NOEXEC: /usr/bin/more, /usr/bin/vi
check out the "Preventing shell escapes" section below for more details on how NOEXEC works and whether it'll work on your system ๐ง
๐ FOLLOW and NOFOLLOW ๐
starting with version 1.8.15, sudoedit won't open a file that's a symbolic link unless the sudoedit_follow flag is enabled ๐ the FOLLOW and NOFOLLOW tags override the value of sudoedit_follow, and you can use them to allow (or deny) editing symbolic links on a per-command basis ๐ฆ these tags only work for the sudoedit command and get ignored for every other command
๐ LOG_INPUT and NOLOG_INPUT ๐
these tags override the value of the log_input flag on a per-command basis ๐ for more info, see "I/O LOGGING" ๐
๐ค LOG_OUTPUT and NOLOG_OUTPUT ๐ค
these tags override the value of the log_output flag on a per-command basis ๐ for more info, see "I/O LOGGING" โจ
๐ง MAIL and NOMAIL ๐ง
these tags give you fine-grained control over whether mail gets sent when a user runs a command, by overriding the value of the mail_all_cmnds flag on a per-command basis ๐ they have no effect when sudo is run with the -l or -v options. a NOMAIL tag also overrides the mail_always and mail_no_perms options ๐ซถ for more info, check the descriptions of mail_all_cmnds, mail_always, and mail_no_perms in the "SUDOERS OPTIONS" section below ๐
๐ PASSWD and NOPASSWD ๐
By default, sudo makes you prove you're you with a password before running a command, bestie ๐ But the NOPASSWD tag can skip that step. Like a Runas_Spec, NOPASSWD sets a default for every command after it in the Cmnd_Spec_List ๐
And the PASSWD tag can flip it back if you need to undo it. Here's the vibe:
ray rushmore = NOPASSWD: /bin/kill, /bin/ls, /usr/bin/lprm
So ray can run /bin/kill, /bin/ls, and /usr/bin/lprm as root on the machine โrushmoreโ and never has to authenticate himself ๐โจ But what if we only want ray to skip the password for /bin/kill? Easy, just swap in the PASSWD tag for the rest:
ray rushmore = NOPASSWD: /bin/kill, PASSWD: /bin/ls, /usr/bin/lprm
Now, plot twist ๐ซข the PASSWD tag does nothing for anyone in the group named by the exempt_group setting. Those people are exempt, so no password for them, no matter what the tag says.
Also, if the NOPASSWD tag shows up on any of a user's entries for the current host, that user can run sudo -l without a password ๐ And they can only run sudo -v without a password if every one of their entries for the current host has the NOPASSWD tag. This behaviour can be overridden with the verifypw and listpw options, so check those if the defaults aren't giving you the energy you want ๐ฆ
โจ SETENV and NOSETENV โจ
These tags override the setenv flag on a per-command basis ๐ If SETENV is set for a command, the user can turn off the env_reset flag from the command line with the -E option. Also, environment variables set on the command line don't have to follow the rules set by env_check, env_delete, or env_keep. So honestly? Only trusted users should be allowed to set variables this way ๐จ If the command matched is ALL, the SETENV tag is implied for that command, but you can override that default with the NOSETENV tag.
๐ง INTERCEPT and NOINTERCEPT ๐ง
If sudo was compiled with intercept support and the underlying operating system supports it, the INTERCEPT tag makes programs spawned by a command get checked against sudoers and logged, just like they would be if you ran them through sudo directly ๐ต๏ธโโ๏ธ This is super useful with commands that allow shell escapes, like editors, shells, and paginators. The catch is there's extra overhead from the policy check, so running stuff like shell scripts that spin up a ton of sub-commands might add some latency. For interactive commands, like a shell or an editor, the overhead is usually not noticeable ๐ธ
Here, user chuck can run any command on the machine โresearchโ in intercept mode:
chuck research = INTERCEPT: ALL
Check out the โPreventing shell escapesโ section below for more on how INTERCEPT works and whether it'll actually work on your system ๐
๐ Wildcards ๐
okay so sudo lets you use shell-style wildcards (aka meta or glob characters) in host names, path names, and command line arguments in the sudoers file ๐ Wildcard matching is handled by the glob(3) and fnmatch(3) functions, as specified by IEEE Std 1003.1 (โPOSIX.1โ).
*โ Matches any set of zero or more characters (including white space).?โ Matches any single character (including white space).[...]โ Matches any character in the specified range.[!...]โ Matches any character not in the specified range.\xโ For any character โxโ, evaluates to โxโ. This is how you escape special characters like โ*โ, โ?โ, โ[โ, and โ]โ.
Quick reality check: these are not regular expressions ๐ โโ๏ธ Unlike a regular expression, there's no way to match one or more characters within a range.
Character classes work too, if your system's glob(3) and fnmatch(3) functions support them. But because the โ:โ character has special meaning in sudoers, it has to be escaped. For example:
/bin/ls [[\:alpha\:]]*
That would match any file name beginning with a letter ๐
A forward slash (โ/โ) won't be matched by wildcards used in the file name portion of the command. This keeps a path like this one in check:
/usr/bin/*
It matches /usr/bin/who but not /usr/bin/X11/xterm ๐ซถ
When it comes to command line arguments though, a slash does get matched by wildcards, since arguments can be arbitrary strings and not just path names.
โ ๏ธ Be careful with wildcards in command line arguments! They can match any character, including white space. In most cases it's safer to use a regular expression to match command line arguments. For more information, see โWildcards in command argumentsโ below.
๐ Exceptions to wildcard rules ๐
The following exceptions apply to the rules above:
""โ If the empty string""is the only command line argument in the sudoers file entry, that command isn't allowed to run with any arguments at all ๐ โโ๏ธsudoeditโ Command line arguments to the sudoedit built-in command should always be path names, so a forward slash (โ/โ) won't be matched by a wildcard.
๐ฎ Regular expressions ๐ฎ
Starting with version 1.9.10, you can use regular expressions for path names and command line arguments ๐ They're more expressive than shell-style wildcards and are usually safer because they give you way more control over matching. The regular expressions sudoers supports are POSIX extended regular expressions, similar to the ones used by the egrep(1) utility. They're usually documented in the regex(7) or re_format(7) manual, depending on your system. As an extra perk, if a regular expression begins with โ(?i)โ, it'll be matched case-insensitively โจ
In sudoers, regular expressions must start with a โ^โ character and end with a โ$โ. This makes it crystal clear what is and isn't a regular expression ๐ Either the path name, the command line arguments, or both can be regular expressions. Since the path name and arguments get matched separately, you can even use wildcards for the path name and regular expressions for the arguments. What you can't do is use a single regular expression to match both the command and its arguments ๐ โโ๏ธ Regular expressions in sudoers are limited to 1024 characters.
There's no need to escape sudoers special characters in a regular expression, except for the pound sign (โ#โ) ๐
In the following example, user john can run the passwd(1) command as root on any host, but he's not allowed to change root's password. This kind of rule is basically impossible to express safely using wildcards ๐ญ
john ALL = /usr/bin/passwd ^[a-zA-Z0-9_]+$,\
!/usr/bin/passwd root
You can also pair a regular expression with sudoedit rules. This one gives user bob the ability to edit the /etc/motd, /etc/issue, and /etc/hosts files only ๐
bob ALL = sudoedit ^/etc/(motd|issue|hosts)$
Regular expressions can even match the command itself! In this example, a regular expression lets user sid run the /usr/sbin/groupadd, /usr/sbin/groupmod, /usr/sbin/groupdel, /usr/sbin/useradd, /usr/sbin/usermod, and /usr/sbin/userdel commands as root ๐
sid ALL = ^/usr/sbin/(group|user)(add|mod|del)$
One downside of using a regular expression for the command name is that you can't match relative paths like ./useradd or ../sbin/useradd. That has security implications when the regular expression is combined with the negation operator, โ!โ, because rules like that can be trivially bypassed ๐ So using a negated regular expression for the command name is strongly discouraged. This doesn't apply to negated commands that only use a regular expression to match the command arguments. See โRegular expressions in command namesโ below for more info.
๐งฉ Including other files from within sudoers ๐งฉ
You can pull other sudoers files into the one currently being parsed with the @include and @includedir directives ๐ฆ For compatibility with sudo versions prior to 1.9.1, #include and #includedir are also accepted.
An include file is handy for keeping a site-wide sudoers file alongside a local, per-machine one. For the sake of this example, the site-wide sudoers file will be /etc/sudoers and the per-machine one will be /etc/sudoers.local. To include /etc/sudoers.local from within /etc/sudoers, you'd put this line in /etc/sudoers:
@include /etc/sudoers.local
When sudo hits this line, it pauses processing of the current file (/etc/sudoers) and switches to /etc/sudoers.local. Once it reaches the end of /etc/sudoers.local, the rest of /etc/sudoers gets processed. Included files can include other files too, but there's a hard limit of 128 nested include files to prevent include loops ๐
Starting with version 1.9.1, the path to the include file can contain white space if it's escaped with a backslash (โ\โ). Or you can wrap the entire path in double quotes (""), in which case no escaping is needed. To include a literal backslash in the path, use โ\โ ๐ซถ
If the path to the include file isn't fully-qualified (it doesn't begin with a โ/โ), it has to live in the same directory as the sudoers file it was included from. For example, if /etc/sudoers contains the line:
@include sudoers.local
then the file that gets included is /etc/sudoers.local ๐ธ
The file name can also include the โ%hโ escape, which stands for the short form of the host name. So if the machine's host name is โxerxesโ, then
@include /etc/sudoers.%h
will make sudo include the file /etc/sudoers.xerxes. Any path name separator characters (โ/โ) in the host name get replaced with an underbar (โ_โ) during expansion ๐ง
The @includedir directive lets you create a sudoers.d directory that your system package manager can drop sudoers rules into during package installation ๐ฆ For example, given:
@includedir /etc/sudoers.d
sudo will pause the current file and read each file in /etc/sudoers.d. It skips file names that end in โ~โ or contain a โ.โ character, so package manager and editor temp or backup files don't cause problems ๐
Okay so the sudoers files get read in sorted lexical order, bestie ๐โจ /etc/sudoers.d/01_first gets parsed before /etc/sudoers.d/10_second. BUT ๐ because it's lexical and not numeric, /etc/sudoers.d/1_whoops gets loaded after /etc/sudoers.d/10_second. Pro tip: keep a consistent number of leading zeroes in your file names so this never bites you ๐
After everything in the directory is parsed, control goes back to whichever file had the @includedir directive.
Unlike files pulled in with @include, visudo will NOT edit the files in an @includedir directory unless one of them has a syntax error ๐ญ You can still run visudo with the -f flag to edit those files directly, but it won't catch an alias being redefined in a different file. So be careful, babe ๐ซถ
๐ธ Other special characters and reserved words ๐ธ
The pound sign (#) marks a comment ๐ฌ unless it's part of a #include directive, or it shows up in a user name and is followed by one or more digits, in which case it's a user-ID instead. Either way, the comment character and everything after it, up to the end of the line, gets ignored.
The reserved word ALL is a built-in alias that always matches ๐ You can use it anywhere you'd otherwise use a Cmnd_Alias, User_Alias, Runas_Alias, or Host_Alias. Trying to define an alias called ALL gives you a syntax error. And honestly? Using ALL can be dangerous ๐ In a command context it lets the user run literally any command on the system.
These option names from an Option_Spec are also reserved words: CHROOT, ROLE, TYPE, TIMEOUT, CWD, NOTBEFORE and NOTAFTER ๐ Defining an alias with the same name as one of those options will also throw a syntax error.
An exclamation point (!) works as a logical not operator in a list or alias, and in front of a Cmnd too ๐
โโ๏ธ That's how you exclude stuff. The ! only does anything if there's something for it to exclude. So if you want to match every user except root, you'd write:
ALL, !root
If you drop the ALL, and just write:
!root
then it explicitly denies root but doesn't match any other users. This is not a true "negation" operator, so don't let it fool you ๐ฆ
Also, a ! combined with the built-in ALL alias to let someone run "all but a few" commands rarely works the way you'd hope ๐ฉ Check out "SECURITY NOTES" below for the tea on that one.
Long lines can be continued with a backslash (\) as the last character on the line ๐ง And white space between elements in a list, plus the special syntax characters in a User Specification (=, :, (, )), is totally optional. Your call, queen โจ
The following characters need a backslash (\) escape when they're part of a word, like a user name or host name: !, =, :, ,, (, ), \ ๐ Remember that one or you'll be crying later ๐ญ
๐ฎ SUDOERS OPTIONS ๐ฎ
sudo's behavior can be tweaked with Default_Entry lines, as we covered earlier ๐ Below is every supported Defaults parameter, grouped by type. Let's go, bestie ๐
๐ Boolean Flags ๐
always_query_group_pluginโ If a group_plugin is configured, sudo uses it to resolve groups of the form%group, as long as there isn't also a system group with that same name ๐ท Normally only groups written as%:groupget passed to the group_plugin. This flag is off by default.always_set_homeโ If enabled, sudo sets the HOME environment variable to the home directory of the target user (which is the runas_default user unless you use the-uoption) ๐ก This flag is largely obsolete and does nothing unless the env_reset flag has been disabled or HOME is in the env_keep list. Both of those are strongly discouraged, so ya know ๐ โโ๏ธ This flag is off by default.authenticateโ If set, users have to authenticate themselves with a password (or some other means of authentication) before they can run commands ๐ This default can be overridden with the PASSWD and NOPASSWD tags. This flag is on by default.case_insensitive_groupโ If enabled, group names in sudoers get matched case insensitively ๐ This might be necessary when users are stored in LDAP or AD. This flag is on by default.case_insensitive_userโ If enabled, user names in sudoers get matched case insensitively ๐ This might be necessary when groups are stored in LDAP or AD. This flag is on by default.closefrom_overrideโ If set, the user can use the-Coption, which overrides the default starting point where sudo begins closing open file descriptors ๐ฆ This flag is off by default.compress_ioโ If set, and sudo is configured to log a command's input or output, the I/O logs get compressed using zlib ๐๏ธ This flag is on by default when sudo is compiled with zlib support.exec_backgroundโ Okay, this one's a whole mood ๐ By default, sudo runs a command as the foreground process as long as sudo itself is running in the foreground. When exec_background is enabled and the command runs in a pseudo-terminal (because of I/O logging or the use_pty flag), the command runs as a background process instead. If it tries to read from the controlling terminal (or change terminal settings), it gets suspended with the SIGTTIN signal (or SIGTTOU when it's terminal settings). If that happens while sudo is a foreground process, the command gets the controlling terminal and resumes in the foreground with zero user intervention needed โจ The upside of starting in the background is that sudo doesn't have to read from the terminal unless the command actually asks for it. Otherwise, any terminal input has to be passed along whether the command wanted it or not, since the kernel buffers terminals and there's no way to tell if the command really wants the input ๐ This is different from historic sudo behavior, or when the command isn't running in a pseudo-terminal.
For this to work seamlessly, the operating system has to support automatic restarting of system calls. Unfortunately, not all operating systems do this by default, and even the ones that do might have bugs ๐ For example, macOS fails to restart the tcgetattr(3) and tcsetattr(3) functions (yes, that's a bug in macOS ๐). Also, because this behavior depends on the command stopping with the SIGTTIN or SIGTTOU signals, programs that catch those signals and suspend themselves with a different signal (usually SIGTOP) won't get automatically foregrounded. Some versions of the linux su(1) command behave this way ๐ซ This flag is off by default.
This setting is only supported in version 1.8.7 or higher ๐ข It does nothing unless I/O logging is enabled or the use_pty flag is enabled.
env_editorโ If set, visudo uses the value of the SUDO_EDITOR, VISUAL or EDITOR environment variables before falling back on the default editor list ๐ visudo usually runs as root, so this flag might let a user with visudo privileges run arbitrary commands as root without getting logged. ๐ณ A safer move is to put a colon-separated list of "safe" editors in the editor setting. Then visudo will only use SUDO_EDITOR, VISUAL or EDITOR if they match a value in editor. If the env_reset flag is enabled, the SUDO_EDITOR, VISUAL and/or EDITOR variables have to be in the env_keep list for env_editor to work when visudo is invoked via sudo. This flag is on by default.env_resetโ If set, sudo runs the command in a minimal environment containing the TERM, PATH, HOME, MAIL, SHELL, LOGNAME, USER and SUDO_* variables ๐ธ Then any variables in the caller's environment, or in the file specified by the restricted_env_file setting, that match the env_keep and env_check lists get added. After that, any variables in the file specified by the env_file setting (if there is one) get added too. The contents of the env_keep and env_check lists, as modified by global Defaults parameters in sudoers, are displayed when sudo is run by root with the-Voption ๐ If the secure_path setting is enabled, its value is used for the PATH environment variable. This flag is on by default.fast_globโ ๐ Normally, sudo uses theglob(3)function for shell-style globbing when it matches path names. The catch is thatglob(3)touches the file system, so some patterns can take forever, especially when they point at a network file system that gets auto mounted on demand. Turn onfast_globand sudo uses thefnmatch(3)function instead, which never touches the file system while matching. The downside is that it can't match relative paths like./lsor../bin/ls. That's a security problem when path names with globbing characters go with the negation operator,โ!โ, because those rules can be trivially bypassed. So please don't use this flag if your sudoers file has negated path names that include globbing characters. It's off by default. ๐ โโ๏ธlog_passwordsโ ๐ Most programs that ask for a password turn off echo before they read it, so the plaintext never shows up on screen. But if terminal input is being logged (see โI/O LOGGINGโ), the password can still end up in the I/O log. Disablelog_passwordsand sudoers will try to stop that. It uses the regular expressions inpassprompt_regexto spot a password prompt in the terminal output buffer. Once it finds one, input characters going into the I/O log get replaced withโ*โuntil a line feed or carriage return shows up in the terminal input, or a new terminal output buffer arrives. If a program echoes characters as you type (like sudo does whenpwfeedbackis set), only the first character of the password gets replaced in the I/O log. This option does nothing unlesslog_inputorlog_ttyinis also set. It's on by default. โจ
This setting is only supported by version 1.9.10 or higher.
fqdnโ ๐ Set this if you want fully qualified host names in the sudoers file, for when the local host name (the one thehostnamecommand gives you) doesn't include the domain. So instead ofmyhostyou'd writemyhost.mydomain.edu. You can still use the short form if you like, and you can even mix the two. This only works when the "canonical" host name from thegetaddrinfo(3)orgethostbyname(3)function is a fully-qualified domain name, which usually happens when your system uses DNS for host name resolution.
If your system prefers the /etc/hosts file over DNS, the "canonical" host name might not be fully qualified. The order sources get queried in is usually set in /etc/nsswitch.conf, /etc/netsvc.conf, /etc/host.conf, or sometimes /etc/resolv.conf. In /etc/hosts, the first host name on the line is the "canonical" one, and any names after it are aliases that sudoers doesn't use. For example, this hosts file line gives the machine "xyzzy" a fully-qualified canonical name, with the short version as an alias:
192.168.1.1 xyzzy.sudo.ws xyzzy
If your hosts file entry isn't formatted properly, fqdn won't work when the hosts file gets queried before DNS. ๐ซ
Beware! If you're using DNS for host name resolution, turning on fqdn makes sudoers do DNS lookups. That means sudo stops working if DNS goes down, like when the machine gets disconnected from the network. Just like with the hosts file, you have to use the "canonical" name as DNS knows it. Don't use a host alias (CNAME entry), since that causes performance issues and there's no way to fetch all the aliases from DNS.
This flag is on by default. ๐
ignore_audit_errorsโ ๐งพ Lets commands run even when sudoers can't write to the audit log. When it's enabled, an audit log write failure isn't treated as fatal. When it's disabled, a command only runs after the audit event has been written successfully. This only does anything on systems where sudoers supports audit logging, which includes FreeBSD, Linux, macOS, and Solaris. It's on by default.ignore_dotโ ๐ If set, sudo ignores"."or""(both mean the current directory) in thePATHenvironment variable. ThePATHitself doesn't get modified. It's on by default.ignore_iolog_errorsโ ๐ผ Lets commands run even when sudoers can't write to the I/O log, whether that's local or remote. When it's enabled, an I/O log write failure isn't fatal. When it's disabled, the command gets terminated if the I/O log can't be written to. It's off by default.ignore_logfile_errorsโ ๐ Lets commands run even when sudoers can't write to the log file. When it's enabled, a log file write failure isn't fatal. When it's disabled, a command only runs after its log file entry has been written successfully. This only matters when sudoers uses file-based logging through thelogfilesetting. It's on by default.ignore_local_sudoersโ ๐ซ If set via LDAP, sudo skips parsing/etc/sudoers. This is for sites that want only LDAP and no local sudoers files, which shuts down rogue operators trying to sneak roles into/etc/sudoers. When this flag is enabled,/etc/sudoersdoesn't even have to exist. Since this flag tells sudo how to act when no specific LDAP entries match, this sudoOption only means something in theโcn=defaultsโsection. It's off by default.ignore_unknown_defaultsโ ๐คทโโ๏ธ If set, sudo won't warn you when it runs into an unknown Defaults entry in the sudoers file or an unknown sudoOption in LDAP. It's off by default.insultsโ ๐ค If set, sudo will roast users when they type in the wrong password. It's off by default. ๐ฅlog_allowedโ โ If set, sudoers logs commands the policy allows to the system audit log (where supported), plus syslog and/or a log file. It's on by default.
This setting is only supported by version 1.8.29 or higher.
log_deniedโ โ If set, sudoers logs commands the policy denies to the system audit log (where supported), plus syslog and/or a log file. It's on by default.
This setting is only supported by version 1.8.29 or higher.
log_exit_statusโ ๐ If set, sudoers logs the exit value of commands it runs to syslog and/or a log file. If a command gets killed by a signal, the signal name gets logged too. It's off by default.
This setting is only supported by version 1.9.8 or higher.
log_hostโ ๐ If set, the host name gets included in log entries written to the file configured by thelogfilesetting. It's off by default.log_inputโ โจ๏ธ If set, sudo runs the command in a pseudo-terminal (if sudo was run from a terminal) and logs all user input. If standard input isn't connected to the user's terminal, because of I/O redirection or because the command is part of a pipeline, that input gets logged too. For more on I/O logging, check the โI/O LOGGINGโ section. It's off by default.log_outputโ ๐ฅ๏ธ If set, sudo runs the command in a pseudo-terminal (if sudo was run from a terminal) and logs all output sent to the user's terminal, standard output, or standard error. If standard output or standard error isn't connected to the user's terminal, because of I/O redirection or because the command is part of a pipeline, that output gets logged too. For more on I/O logging, check the โI/O LOGGINGโ section. It's off by default. ๐ฆ
okay bestie, we're still in the options list ๐ and these ones are kinda the paranoid-but-make-it-fashion flags ๐
log_server_keepaliveโ ๐ซถ turns on TCP keepalive on the connection to the log server, so little "still alive?" pings get sent regularly. If the server stops answering, the connection gets closed and the command you're running gets terminated, unlessignore_iolog_errors(with I/O logging on) orignore_log_errors(with I/O logging off) is set. This one is on by default โจ
This setting is only supported by version 1.9.0 or higher ๐
log_server_verifyโ ๐ the server certificate you get during the TLS handshake has to be valid AND it has to include either the server name (fromlog_servers) or its IP address. If neither checks out, the handshake fails. Also on by default ๐
This setting is only supported by version 1.9.0 or higher ๐
log_stderrโ ๐ชต if this is set, sudo logs standard error whenever it isn't hooked up to your terminal. Handy for stuff going into a pipe or getting redirected into a file. It's off by default, but it flips on if eitherlog_outputor theLOG_OUTPUTcommand tag is set ๐log_stdinโ โจ๏ธ same vibe aslog_stderr, but for standard input that isn't connected to your terminal. Think input coming from a pipe or redirected from a file. Off by default, and it turns on whenlog_inputor theLOG_INPUTcommand tag is set ๐ธlog_stdoutโ ๐ค logs standard output when it isn't connected to your terminal, like output going into a pipe or into a file. Off by default, but on iflog_outputor theLOG_OUTPUTcommand tag is set ๐ฆlog_subcmdsโ ๐ง when a command spawns a child process and runs a program throughexecve(2),execl(3),execle(3),execlp(3),execv(3),execvp(3),execvpe(3), orsystem(3), sudoers logs it. So if sudo starts a shell, every individual command typed in that shell gets logged too ๐ Off by default.
The log_subcmds flag uses the same underlying machinery as the intercept setting. Some commands might not behave properly with it on, because of the way it intercepts sub-commands ๐ For more on which systems support this and what the limits are, check out โPreventing shell escapes.โ This setting is only supported by version 1.9.8 or higher, and it's incompatible with SELinux RBAC support unless the system supports seccomp(2) filter mode โ ๏ธ
log_ttyinโ ๐น runs the command inside a pseudo-terminal and logs the keystrokes the user sends to their terminal, as long as there is one. Off by default, but it's switched on whenlog_inputor theLOG_INPUTcommand tag is set. If there's no terminal, like when you're running a remote command withssh(1), this flag does absolutely nothing ๐ซlog_ttyoutโ ๐บ same pseudo-terminal energy, but this time it logs everything that shows up on the user's terminal. Off by default, and on whenlog_outputor theLOG_OUTPUTcommand tag is set. With no terminal around (again,ssh(1)remote commands), it does nothing ๐log_yearโ ๐ if set, the four-digit year gets written into the (non-syslog) sudo log file. Off by default ๐long_otp_promptโ ๐ข when you're validating with a One Time Password scheme like S/Key or OPIE, this makes the prompt two lines long so you can cut and paste the challenge into a local window. It's not as cute as the default, but some people find it way more convenient ๐ Off by default.mail_all_cmndsโ ๐ sends mail to themailtouser every single time someone tries to run a command with sudo (sudoedit included). If the user runs sudo with-lor-v, no mail goes out, unless there's an authentication error andmail_badpassis also set. Off by default โจmail_alwaysโ ๐จ mail to themailtouser every time someone runs sudo, period. Off by default ๐ฅmail_badpassโ ๐ โโ๏ธ mail goes to themailtouser if the person running sudo types the wrong password. But if the command they're attempting isn't allowed by sudoers AND any ofmail_all_cmnds,mail_always,mail_no_host,mail_no_permsormail_no_useris set, this flag has no effect. Off by default ๐mail_no_hostโ ๐ if set, mail goes to themailtouser when the invoking user is in the sudoers file but isn't allowed to run commands on the current host. Off by default ๐ฆmail_no_permsโ ๐ซ if set, mail goes to themailtouser when the invoking user is allowed to use sudo, but the command they're trying isn't listed in their sudoers entry or is explicitly denied. Off by default ๐ธmail_no_userโ ๐ป if set, mail goes to themailtouser when the invoking user isn't in the sudoers file at all. This one is on by default ๐match_group_by_gidโ ๐ชช by default, sudoers looks up each group the user belongs to by group-ID to get its name (and it only does this once). The list of group names it ends up with is what gets matched against groups in the sudoers file. That works great when the sudoers file lists more groups than a typical user is in. But on systems where group lookups are slow, where users belong to tons of groups, or where the sudoers file only lists a few groups, it can be super expensive and sudo can get laggy ๐ข In that case,match_group_by_gidhelps by skipping the group-ID to name conversion. Instead, sudoers has to look up each group name in the sudoers file and compare the group-ID instead of the name when checking membership ๐ง
If match_group_by_gid is enabled, group database lookups done by sudoers are keyed by group name instead of group-ID. On systems with multiple sources for the group database, you can end up with conflicting group names or group-IDs between the local /etc/group file and the remote group database ๐ญ On those systems, turning match_group_by_gid on or off decides whether group queries are done by name (enabled) or by ID (disabled), which can help dodge group entry conflicts.
The match_group_by_gid flag does nothing when sudoers data is stored in LDAP. It's off by default ๐
This setting is only supported by version 1.8.18 or higher ๐
interceptโ ๐ต๏ธโโ๏ธ if set, every command run through sudo acts like theINTERCEPTtag was set, unless aNOINTERCEPTtag overrides it. Some commands might not play nice with intercept on, because of the way it intercepts sub-commands. Check out the descriptions ofINTERCEPTandNOINTERCEPTabove, plus the โPreventing shell escapesโ section at the end of this manual ๐ Off by default.
This setting is only supported by version 1.9.8 or higher and is incompatible with SELinux RBAC support unless the system supports seccomp(2) filter mode ๐ฅน
intercept_allow_setidโ ๐ช on most systems, the dynamic loader ignoresLD_PRELOAD(or its equivalent) when it's running set-user-ID and set-group-ID programs, which basically kills intercept mode. To stop that from happening, sudoers won't let a set-user-ID or set-group-ID program run in intercept mode unlessintercept_allow_setidis enabled. This flag does nothing unless theinterceptflag is enabled or theINTERCEPTtag is set for the command. It's on by default when theintercept_typeoption is set totrace, otherwise it defaults to off ๐
This setting is only supported by version 1.9.8 or higher ๐
intercept_authenticateโ ๐ Okay so, when this one is on, commands run by an intercepted process have to be authenticated again whenever the user's time stamp isn't current. Say a shell starts with intercept enabled. The second the invoking user's time stamp goes out of date, every command after that needs authentication, no exceptions ๐ . It does nothing unless theinterceptflag is enabled or theINTERCEPTtag is set for the command. It's off by default.- This setting is only supported by version 1.9.8 or higher โจ
intercept_verifyโ ๐ต๏ธโโ๏ธ This is the main character of the intercept flags. When it's set, sudo tries to double-check that a command running in intercept mode has the path name, command line arguments, and environment it's supposed to have.- The process gets paused after
execve(2)finishes, but before the new command gets a chance to run. To check it, sudo reads the command's path from/proc/PID/exe, reads the command line arguments and environment from the process's memory, and compares all of that against what got passed toexecve(2). If anything doesn't match, the command gets aSIGKILLsignal and is terminated ๐. - This helps stop a time of check versus time of use issue with intercept mode, where the
execve(2)arguments could get changed after the sudoers policy check. These checks can only happen if theproc(5)file system is available. The flag does nothing unless theinterceptflag is enabled or theINTERCEPTtag is set for the command, and theintercept_typeoption is set totrace. - > ๐จ This setting doesn't play nicely with programs that change their root directory via
chroot(2). If a program changes its root directory, path names won't match what the sudo parent process sees, and sub-commands will get terminated before they can run. - This flag is on by default.
- This setting is only supported by version 1.9.12 or higher ๐ฆ
- The process gets paused after
netgroup_tupleโ ๐ธ When this is set, netgroup lookups use the full netgroup tuple: host name, user name, and domain (if one is set). Historically, sudo only matched the user name and domain for netgroups used in aUser_List, and only matched the host name and domain for netgroups used in aHost_List. Off by default.noexecโ ๐ซ When this is set, every command run through sudo acts like theNOEXECtag has been set, unless anEXECtag overrides it. Check out the description ofEXECandNOEXECabove, plus the โPreventing shell escapesโ section at the end of this manual. It's off by default.noninteractive_authโ ๐ When this is set, authentication gets attempted even in non-interactive mode (when sudo's-noption is specified). That lets authentication methods that don't need user interaction succeed. Methods that need input from the user's terminal will still fail. If it's disabled, authentication won't be attempted in non-interactive mode. It's off by default.- This setting is only supported by version 1.9.10 or higher ๐
pam_acct_mgmtโ ๐งพ On systems that use PAM for authentication, sudo does PAM account validation for the invoking user by default. What actually gets checked depends on which PAM modules are configured. When enabled, account validation happens regardless of whether or not a password is required. This flag is on by default.- This setting is only supported by version 1.8.28 or higher โจ
pam_rhostโ ๐ On systems that use PAM for authentication, sudo sets the PAM remote host value to the name of the local host whenpam_rhostis enabled. On Linux systems, enablingpam_rhostmay result in DNS lookups of the local host name when PAM is initialized. On Solaris versions prior to Solaris 8,pam_rhostmust be enabled ifpam_ruseris also enabled, or the Solaris PAM implementation will crash ๐ญ.- This flag is off by default on systems other than Solaris.
- This setting is only supported by version 1.9.0 or higher ๐
pam_ruserโ ๐ค On systems that use PAM for authentication, sudo sets the PAM remote user value to the name of the user who invoked sudo whenpam_ruseris enabled. This flag is on by default.- This setting is only supported by version 1.9.0 or higher ๐ท
pam_sessionโ ๐ชช On systems that use PAM for authentication, sudo creates a new PAM session for the command to run in. Unless sudo is given the-ior-soptions, PAM session modules run with the โsilentโ flag enabled. That keeps last login information from showing up for every command on some systems. Disablingpam_sessionmight be needed on older PAM implementations, or on operating systems where opening a PAM session changes theutmporwtmpfiles. If PAM session support is disabled, resource limits may not get updated for the command being run.- If
pam_session,pam_setcred, anduse_ptyare all disabled,log_servershasn't been set, and I/O logging hasn't been configured, sudo runs the command directly instead of as a child process ๐โโ๏ธ. - This flag is on by default.
- This setting is only supported by version 1.8.7 or higher ๐
- If
pam_setcredโ ๐๏ธ On systems that use PAM for authentication, sudo tries to establish credentials for the target user by default, if the underlying authentication system supports it. One example of a credential is a Kerberos ticket.- If
pam_session,pam_setcred, anduse_ptyare disabled,log_servershasn't been set, and I/O logging hasn't been configured, sudo runs the command directly instead of as a child process. - This flag is on by default.
- This setting is only supported by version 1.8.8 or higher ๐
- If
pam_silentโ ๐คซ When set, PAM authentication runs in silent mode. That stops PAM authentication modules from generating output. Heads up though: in some cases this might hide important info about why authentication failed. For example, PAM modules likepam_faillockwill only display a warning ifpam_silentis disabled. This flag is on by default.- This setting is only supported by version 1.9.16 or higher ๐
passprompt_overrideโ ๐ฌ When set, the prompt specified bypasspromptor theSUDO_PROMPTenvironment variable is always used, and it replaces the prompt provided by a PAM module or another authentication method. It's off by default.path_infoโ ๐ Normally, sudo tells the user when a command can't be found in theirPATHenvironment variable. Some sites might want to turn that off, since it could be used to gather info about where executables are located that the normal user doesn't have access to. The downside is that if the executable just isn't in the user'sPATH, sudo will tell them they're not allowed to run it, which can be super confusing ๐. This flag is on by default.preserve_groupsโ ๐งบ By default, sudo sets up the group vector to the list of groups the target user is in. Whenpreserve_groupsis set, the user's existing group vector stays unaltered. The real and effective group-IDs still get set to match the target user, though. It's off by default.pwfeedbackโ โจ๏ธ By default, sudo reads the password like most other Unix programs do, by turning off echo until the user hits the return (or enter) key. Some users get confused by this, since it looks like sudo has hung right there. Whenpwfeedbackis set, sudo gives visual feedback when the user presses a key. Heads up though: this has a security impact, because an onlooker might be able to figure out the length of the password being entered ๐. It's off by default.requirettyโ ๐บ When set, sudo only runs when the user is logged in to a real tty. It can only be run from a login session, not via other means likecron(8)orcgi-binscripts. It's off by default.root_sudoโ ๐ When this is on, root is allowed to run sudo too. Turn it off and users can't "chain" sudo commands to sneak into a root shell with something likesudo sudo /bin/sh, but heads up: it also stops root from running sudoedit. Disabling it doesn't add any real security. It's only here for historical reasons. It's on by default.rootpwโ ๐ With this on, sudo asks for the root password instead of your own whenever you run a command or edit a file. It's off by default.runas_allow_unknown_idโ ๐ฆ When enabled, runas user and group IDs that aren't in the password or group databases can still get matched. Besides explicitly matching unknown user or group IDs in a Runas_List, this option also lets the ALL alias match unknown IDs. It's off by default.- This setting is only supported by version 1.8.30 or higher. Older versions of sudo always allowed matching of unknown user and group IDs.
runas_check_shellโ ๐ If enabled, sudo only runs commands as a user whose shell is listed in the/etc/shellsfile, even if the invoking user's Runas_List would otherwise allow it. If there's no/etc/shellsfile, a system-dependent list of built-in default shells gets used. Lots of operating systems give system users like "bin" no valid shell, so this flag can stop commands from running as those users. It's off by default.- This setting is only supported by version 1.8.30 or higher.
runaspwโ ๐ With this on, sudo asks for the password of the user named by the runas_default option (which defaults to root) instead of your own when running a command or editing a file. It's off by default.selinuxโ ๐ If enabled, the user can specify an SELinux role and/or type to use when running the command, as long as the SELinux policy permits it. If SELinux is disabled on the system, this flag does nothing. It's on by default.set_homeโ ๐ก If enabled and sudo is invoked with the-soption, theHOMEenvironment variable gets set to the home directory of the target user (which is the runas_default user, unless the-uoption is used). Honestly, this one is mostly obsolete. It has no effect unless the env_reset flag has been disabled orHOMEis in the env_keep list, and both of those are strongly discouraged. It's off by default.set_lognameโ ๐ชช Normally, sudo sets theLOGNAMEandUSERenvironment variables to the name of the target user (the user specified by runas_default, unless the-uoption is given). But some programs, including the RCS revision control system, useLOGNAMEto figure out the real identity of the user, so you might want to change this. You can do that by negating the set_logname option. Heads up: set_logname won't do anything if env_reset hasn't been disabled and the env_keep list containsLOGNAMEorUSER. It's on by default.set_utmpโ ๐ When enabled, sudo creates an entry in the utmp (or utmpx) file whenever a pseudo-terminal gets allocated. sudo allocates one when it's running in a terminal and at least one of these is enabled: log_input, log_output, log_stdin, log_stdout, log_stderr, log_ttyin, log_ttyout, or use_pty. By default, the new entry is a copy of the user's existing utmp entry (if they have one), with the tty, time, type, and pid fields updated. It's on by default.setenvโ ๐ง Lets the user turn off the env_reset option from the command line with the-Eoption. On top of that, environment variables set from the command line skip the restrictions imposed by env_check, env_delete, and env_keep. So only trusted users should be allowed to set variables this way, no cap. It's off by default.shell_noargsโ ๐ If set and sudo is invoked with no arguments, it acts like you passed the-soption. That means it runs a shell as root. The shell is picked from theSHELLenvironment variable if it's set, and falls back to the shell listed in the invoking user's/etc/passwdentry if not. It's off by default.stay_setuidโ ๐ Normally, when sudo runs a command, the real and effective user-IDs get set to the target user (root by default). This option changes that so the real user-ID stays as the invoking user's user-ID. In other words, sudo acts like a set-user-ID wrapper. That can help on systems that disable some potentially dangerous functionality when a program runs set-user-ID. This option only does anything on systems that support either thesetreuid(2)orsetresuid(2)system call. It's off by default.sudoedit_checkdirโ ๐ If set, sudoedit checks every directory component of the path being edited for writability by the invoking user. Symbolic links won't be followed in writable directories, and sudoedit refuses to edit a file that lives in a writable directory. These restrictions aren't enforced when sudoedit runs as root. On some systems, if any directory component of the path isn't readable by the target user, sudoedit can't edit the file at all. It's on by default.- This setting was first introduced in version 1.8.15, but it initially had a race condition. The check for symbolic links in writable intermediate directories was added in version 1.8.16.
sudoedit_followโ ๐ By default, sudoedit won't follow symbolic links when opening files. Enablesudoedit_followto let sudoedit open symbolic links. You can override it on a per-command basis with the FOLLOW and NOFOLLOW tags. It's off by default.- This setting is only supported by version 1.8.15 or higher.
syslog_pidโ ๐ชต When logging via syslog(3), this includes the process ID in the log entry. It's off by default.- This setting is only supported by version 1.8.21 or higher.
targetpwโ ๐ฏ If set, sudo asks for the password of the user specified by the-uoption (which defaults to the value of runas_default) instead of your own when running a command or editing a file. This flag rules out using a user-ID that isn't in the passwd database as an argument to-u. It's off by default.tty_ticketsโ ๐๏ธ If set, users have to authenticate on a per-tty basis. With this on, sudo keeps a separate record in the time stamp file for each terminal. If it's disabled, one record is shared across all login sessions.- This option has been superseded by the timestamp_type option.
umask_overrideโ ๐ญ If set, sudo sets the umask exactly as specified in the sudoers file, with no changes. That makes it possible to specify a umask in sudoers that's more permissive than the user's own umask, which matches historical behavior. If umask_override isn't set, sudo sets the umask to the union of the user's umask and whatever sudoers specifies. It's off by default.use_netgroupsโ ๐ If set, netgroups (prefixed with+) can be used in place of a user or host. For LDAP-based sudoers, netgroup support needs an expensive sub-string match on the server, unless theNETGROUP_BASEdirective is present in the/etc/ldap.conffile. If you don't need netgroups, disable this option to take some load off your LDAP server. It's on by default.use_ptyโ ๐ก๏ธ If set, and sudo is running in a terminal, the command runs in a new pseudo-terminal. If the sudo process isn't attached to a terminal, use_pty does nothing.- A malicious program running under sudo could inject commands into your terminal, or run a background process that keeps access to your terminal device even after the main program finishes. Running the command in a separate pseudo-terminal shuts that attack down. ๐
Okay so, picking up right where the use_pty vibe left off ๐
A side effect of running the command in a new pseudo-terminal is that input will be passed to the command even if it is non-interactive. ๐ญ So, for example, keys pressed while a non-interactive command is running will be eaten by sudo instead of being passed to the shell after the command exits. Chaotic, I know.
This flag is on by default for sudo 1.9.14 and above. ๐ธ
user_command_timeoutsโ okay this one is so useful ๐ If set, the user can put a timeout on the command line. If the timeout runs out before the command finishes, the command gets terminated. If there's a timeout in the sudoers file AND one on the command line, the smaller one wins. See theTimeout_Specsection for how the timeout syntax works. This flag is off by default.
This setting is only supported by version 1.8.20 or higher. โจ
utmp_runasโ if set, sudo stores the name of the runas user when it updates the utmp (or utmpx) file ๐ By default, sudo stores the name of the invoking user instead. This flag is off by default.visiblepwโ so by default, sudo will refuse to run if the user has to type a password but the terminal can't turn off echo ๐ If thevisiblepwflag is set, sudo will prompt for a password even when it would be visible on the screen. This is what makes stuff likessh somehost sudo lspossible, since by default ssh(1) doesn't allocate a tty when running a command. This flag is off by default.
๐ข Integers
closefromโ before it runs a command, sudo closes every open file descriptor except standard input, standard output, and standard error (file descriptors 0โ2) ๐งน Theclosefromoption can be used to pick a different file descriptor to start closing from. The default is3.command_timeoutโ the maximum amount of time a command is allowed to run before it gets terminated โณ See theTimeout_Specsection for a description of the timeout syntax.
This setting is only supported by version 1.8.20 or higher. โจ
log_server_timeoutโ the maximum amount of time to wait when connecting to a log server or waiting for a server response ๐ก See theTimeout_Specsection for a description of the timeout syntax. The default value is 30 seconds.
This setting is only supported by version 1.9.0 or higher. โจ
maxseqโ the maximum sequence number that will be substituted for the%{seq}escape in the I/O log file (see theiolog_dirdescription below for more info) ๐ข While the value substituted for%{seq}is in base 36,maxseqitself should be written in decimal. Values larger than2176782336(which corresponds to the base 36 sequence number "ZZZZZZ") will be silently truncated to2176782336. The default value is2176782336.
Once the local sequence number reaches the value of maxseq, it will "roll over" to zero, after which sudoers will truncate and reuse any existing I/O log path names. ๐
This setting is only supported by version 1.8.7 or higher.
passwd_triesโ the number of tries a user gets to type their password before sudo logs the failure and exits ๐ค The default is3.syslog_maxlenโ okay, real talk: on lots of systems, syslog(3) has a pretty small log buffer ๐ IETF RFC 5424 says syslog servers must support messages of at least 480 bytes and should support messages up to 2048 bytes. By default, sudoers makes log messages up to 980 bytes, which matches the historic BSD syslog implementation (that used a 1024 byte buffer to store the message, date, hostname, and program name). To keep syslog messages from getting truncated, sudoers splits up log messages that are larger thansyslog_maxlenbytes. When a message is split, the extra parts include the string "(command continued)" after the user name and before the continued command line arguments.
This setting is only supported by version 1.8.19 or higher. โจ
๐งฎ Integers that can be used in a boolean context
loglinelenโ number of characters per line for the file log ๐ This value decides when to wrap lines so the log files look nicer. It has no effect on the syslog log file, only the file log. The default is80(use0or negate the option to disable word wrap).passwd_timeoutโ number of minutes before the sudo password prompt times out, or0for no timeout โ The timeout may include a fractional component if minute granularity isn't enough, for example2.5. The default is0.timestamp_timeoutโ number of minutes that can pass before sudo asks for a password again ๐ฐ๏ธ The timeout may include a fractional component if minute granularity isn't enough, for example2.5. The default is15. Set this to0to always prompt for a password. If set to a value less than0, the user's time stamp won't expire until the system is rebooted. This can be used to let users create or delete their own time stamps viasudo -vandsudo -krespectively. ๐๏ธumaskโ the file mode creation mask to use when running the command ๐ญ Negate this option or set it to0777to stop sudoers from changing the umask. Unless theumask_overrideflag is set, the actual umask will be the union of the user's umask and the value of theumasksetting, which defaults to0022. This guarantees that sudo never lowers the umask when running a command. ๐ก๏ธ
If umask is explicitly set in sudoers, it overrides any umask setting in PAM or login.conf. If umask isn't set in sudoers, the umask specified by PAM or login.conf takes precedence. The umask setting in PAM isn't used for sudoedit, since it doesn't create a new PAM session.
๐งต Strings
apparmor_profileโ the default AppArmor profile to transition into when executing the command ๐ฆ The defaultapparmor_profilecan be overridden for individual sudoers entries by specifying theAPPARMOR_PROFILEoption. This option is only available when sudo is built with AppArmor support.cmddenial_messageโ if set, sudo displays this message when a user is denied access to run the specified command, but is listed in the sudoers file for the host ๐ซ This can be used to give users extra, site-specific info when a command is denied by the security policy. It doesn't override the standard warning users get when a command is denied.authfail_messageโ message displayed after a user fails to authenticate ๐ฌ The message may include the%descape, which expands to the number of failed password attempts. If set, it overrides the default message,"%d incorrect password attempt(s)".badpass_messageโ message displayed if a user types an incorrect password ๐ The default is"Sorry, try again."unless insults are enabled. ๐ฅeditorโ a colon (:) separated list of editor path names used by sudoedit and visudo ๐ For sudoedit, this list is used to find an editor when none of theSUDO_EDITOR,VISUALorEDITORenvironment variables are set to an editor that exists and is executable. For visudo, it's used as an allowlist of editors: visudo picks the editor that matches the user'sSUDO_EDITOR,VISUALorEDITORenvironment variable if possible, or the first editor in the list that exists and is executable if not. Unless invoked as sudoedit, sudo doesn't preserve theSUDO_EDITOR,VISUALorEDITORenvironment variables unless they're present in theenv_keeplist or theenv_resetoption is disabled. The default is/usr/bin/editor.intercept_typeโ the underlying mechanism used by theinterceptandlog_subcmdsoptions ๐งฉ It has the following possible values:
The trace bit is how sudo can intercept execve(2) on Linux, but only when seccomp(2) filtering is turned on. โจ If /proc/sys/kernel/seccomp/actions_avail is missing or doesn't have a "trap" element, intercept_type set to trace does absolutely nothing and sudo falls back to dso ๐ซ
The default is trace if your system supports it, and dso if it doesn't. Simple, iconic, no notes ๐
๐ iolog_dir
This is the top-level directory used to build the path for the input/output log directory ๐๏ธ It only matters if log_input or log_output is turned on, or if the LOG_INPUT or LOG_OUTPUT tags show up on a command. The session sequence number, if there is one, gets stored in that directory. The default is /var/log/sudo-io ๐ธ
Here are the percent (%) escapes you can use, bestie:
%{seq}โ expands to a monotonically increasing base-36 sequence number, like0100A5. Every two digits become a new directory, so that one turns into01/00/A5๐ข%{user}โ expands to the invoking user's login name ๐%{group}โ expands to the name of the invoking user's real group-ID ๐ฅ%{runas_user}โ expands to the login name of the user the command runs as, likeroot๐%{runas_group}โ expands to the group name of the user the command runs as, likewheel๐%{hostname}โ expands to the local host name, minus the domain name ๐ฅ๏ธ%{command}โ expands to the base name of the command being run ๐งพ
On top of those, any escape sequences your system's strftime(3) function supports will also get expanded โฐ
If you want a literal % character, type %% ๐
Any path name separator characters (/) in the user, group, or host name get swapped out for an underbar (_) during expansion. No sneaky directory creation allowed ๐
โโ๏ธ
๐ iolog_file
This is the path name, relative to iolog_dir, where input/output logs get stored when log_input or log_output is enabled, or when the LOG_INPUT or LOG_OUTPUT tags are on a command. iolog_file is allowed to have directory components in it ๐๏ธ The default is %{seq} ๐ฆ
Check the iolog_dir option above for the full list of supported percent (%) escapes. It's the same deal ๐ซถ
On top of those escapes, path names that end in six or more Xs get those Xs replaced with a unique mix of digits and letters, kind of like the mktemp(3) function does โจ
If the path you get by smooshingiolog_dirandiolog_filetogether already exists, the existing I/O log file gets truncated and overwritten. The only way to avoid that is foriolog_fileto end in six or moreXs ๐ญ
๐ iolog_flush
If this is set, sudo flushes I/O log data to disk after every write instead of buffering it ๐ง This makes it possible to watch the logs in real time while the program is still running, which is so cool, but it can seriously reduce how well I/O log compression works ๐ This flag is off by default ๐
This setting is only supported in version 1.8.20 or higher.
๐ฅ iolog_group
This is the group name to look up when setting the group-ID on new I/O log files and directories ๐ซง If iolog_group isn't set, the primary group-ID of the user specified by iolog_user gets used. If neither iolog_group nor iolog_user is set, I/O log files and directories get created with group-ID 0 ๐
This setting is only supported in version 1.8.19 or higher.
๐ iolog_mode
This is the file mode used when creating I/O log files ๐ Only the read and write permission bits for owner, group, or other are honored. Everything else gets ignored. The file permissions will always include the owner read and write bits, even if they aren't in the mode you specified ๐ When creating I/O log directories, search (execute) bits get added so they match the read and write bits from iolog_mode ๐ง
The default is 0600, which means read and write for the user only ๐
This setting is only supported in version 1.8.19 or higher.
๐ง iolog_user
This is the user name to look up when setting the user and group-IDs on new I/O log files and directories ๐
If iolog_group is set, that gets used instead of the user's primary group-ID. By default, I/O log files and directories get created with user and group-ID 0 ๐
This setting is super useful when your I/O logs live on a Network File System (NFS) share ๐๏ธ Having a dedicated user own the log files means sudoers doesn't write to them as user-ID 0, which NFS usually won't allow ๐
This setting is only supported in version 1.8.19 or higher.
๐๏ธ lecture_status_dir
This is the directory where sudo keeps per-user lecture status files ๐ Once a user has gotten the lecture, a zero-length file gets created in this directory so sudo doesn't lecture them again. Don't clear this directory when the system reboots, it needs to stick around ๐ซถ The default is /var/lib/sudo/lectured ๐ธ
๐ log_server_cabundle
This is the path to a certificate authority bundle file, in PEM format ๐ It gets used instead of the system's default certificate authority database when authenticating the log server ๐ The default is to use the system's default certificate authority database. This setting does nothing unless log_servers is set and the remote log server is secured with TLS ๐
โโ๏ธ
This setting is only supported in version 1.9.0 or higher.
๐ชช log_server_peer_cert
This is the path to the sudo client's certificate file, in PEM format ๐ซ It's required when the remote log server is secured with TLS and client certificate validation is enabled. For sudo_logsrvd, client certificate validation is controlled by the tls_checkpeer option, which defaults to false ๐
This setting is only supported in version 1.9.0 or higher.
๐๏ธ log_server_peer_key
This is the path to the sudo client's private key file, in PEM format ๐คซ It's required when the remote log server is secured with TLS and client certificate validation is enabled. For sudo_logsrvd, client certificate validation is controlled by the tls_checkpeer flag, which defaults to false ๐ฅ
This setting is only supported in version 1.9.0 or higher.
๐ง mailsub
This is the subject line of the mail sent to the mailto user ๐ The escape %h expands to the machine's host name. The default is *** SECURITY information for %h *** ๐จ
๐ซ noexec_file
As of sudo version 1.8.1 this option is no longer supported ๐ The path to the noexec file should now be set in the sudo.conf(5) file instead ๐ซ
๐ pam_askpass_service
On systems that use PAM for authentication, this is the service name used when the -A option is specified ๐ง The default value is either sudo or sudo-i, depending on whether the -i option is also specified. Check out the description of pam_service for more deets โจ
This setting is only supported in version 1.9.9 or higher.
๐ pam_login_service
On systems that use PAM for authentication, this is the service name used when the -i option is specified ๐ท The default value is sudo-i. See the description of pam_service for more info ๐
This setting is only supported in version 1.8.8 or higher.
๐ก๏ธ pam_service
On systems that use PAM for authentication, the service name picks which PAM policy to apply ๐ก๏ธ This usually lines up with an entry in the pam.conf file or a file in the /etc/pam.d directory ๐ The default value is sudo ๐
This setting is only supported in version 1.8.8 or higher.
๐ฌ passprompt
This is the default prompt used when asking for a password ๐ฃ๏ธ You can override it with the -p option or the SUDO_PROMPT environment variable ๐ซง These percent (%) escapes are supported:
%Hโ expands to the local host name including the domain name, but only if the machine's host name is fully qualified or thefqdnoption is set ๐%hโ expands to the local host name without the domain name ๐
Okay bestie, picking up right where the passprompt escapes left off ๐
%pโ gets swapped for the user whose password is being asked for ๐ (it respects therootpw,targetpwandrunaspwflags in sudoers)%Uโ gets swapped for the login name of the user the command will run as (it defaults to root ๐)%uโ gets swapped for the invoking user's login name ๐ซถ%%โ two%characters in a row collapse into one single%๐
On systems that use PAM for authentication, passprompt only kicks in if the prompt from the PAM module matches Password: or username's Password: . This is the vibe check that keeps passprompt from crashing challenge-response style authentication ๐ง The passprompt_override flag can change this if you want it to behave differently.
The default is [sudo] password for %p: โจ
roleโ the default SELinux role used when sudo builds a fresh security context to run the command ๐ฆ You can override it per command in the sudoers file or with command line options. This one only shows up if sudo was built with SELinux support ๐runas_defaultโ the default user to run commands as when you don't pass-uon the command line ๐ It defaults to root.sudoers_localeโ the locale used when parsing the sudoers file, logging commands, and sending email ๐ Changing the locale can change how sudoers gets interpreted, so be careful bestie. It defaults toC.timestamp_typeโ sudoers uses per-user time stamp files for credential caching ๐ธ This option picks the type of time stamp record. The possible values are:globalโ one time stamp record for all of a user's login sessions, no matter the terminal or parent process ID โจ There's an extra record that serializes password prompts when sudo runs multiple times in a pipeline, but that doesn't affect authentication.ppidโ one time stamp record for every process sharing the same parent process ID (usually the shell) ๐ Commands run from the same shell (or another common parent) won't ask for a password fortimestamp_timeoutminutes (15 by default). Commands run via sudo from a different parent process ID, like from a shell script, get authenticated separately.ttyโ one time stamp record per terminal, so each login session gets authenticated separately ๐ If there's no terminal, it acts the same asppid. Commands from the same terminal won't need a password fortimestamp_timeoutminutes (15 by default).kernelโ the time stamp gets stored in the kernel as an attribute of the terminal device ๐ฅ If there's no terminal, it acts the same asppid. Negativetimestamp_timeoutvalues aren't supported and positive values cap out at 60 minutes. This is currently only supported on OpenBSD ๐ก
The default value is tty. This setting is only supported by version 1.8.21 or higher ๐
timestampdirโ the directory where sudo stores its time stamp files ๐ It should get cleared when the system reboots. The default is/run/sudo/ts.timestampownerโ the owner of the lecture status directory, the time stamp directory, and every file stored in them ๐ง It defaults to root.typeโ the default SELinux type used when sudo builds a new security context to run the command ๐ You can override it per command in the sudoers file or with command line options. Likerole, this is only available when sudo is built with SELinux support.
Next up are the options that work in a boolean context ๐
admin_flagโ this one points to a file that gets created the first time a user in the sudo or admin groups runs sudo ๐ It's only available if sudo was configured with the--enable-admin-flagoption. The default value is~/.sudo_as_admin_successful.env_fileโ the fully qualified path to a file full of variables to set in the environment of the program being run ๐ Entries should look likeVARIABLE=valueorexport VARIABLE=value, and the value can optionally be wrapped in single or double quotes. Variables only get added if they don't already exist in the environment. This file counts as part of the security policy, so its contents skip the other sudo environment restrictions likeenv_keepandenv_check๐exempt_groupโ users in this group are exempt from password and PATH requirements ๐ซถ The group name shouldn't have a%prefix. It's not set by default.fdexecโ decides whether sudo runs a command by its path or by an open file descriptor ๐ฆ The possible values are:alwaysโ always executes by file descriptor ๐ฅneverโ never executes by file descriptor ๐digest_onlyโ only executes by file descriptor if the command has a digest attached in the sudoers file ๐
The default is digest_only, and that's the move because it avoids a time of check versus time of use race condition when the command lives in a directory the invoking user can write to ๐ซฃ
โ ๏ธ fdexec changes the first element of the argument vector for scripts ($0in the shell) because of how the kernel runs script interpreters. Instead of being a normal path, it refers to a file descriptor, like/dev/fd/4on Solaris or/proc/self/fd/4on Linux. The workaround is to use theSUDO_COMMANDenvironment variable instead ๐ญ
The fdexec setting only matters when the command gets matched by path name ๐ธ It has no effect if the command is matched by the built-in ALL alias. This setting is only supported by version 1.8.20 or higher, and if the operating system doesn't support the fexecve(2) system call, it does nothing at all ๐
group_pluginโ a string containing a sudoers group plugin with optional arguments ๐ง It should be made of the plugin path (fully qualified or relative to the/usr/libexec/sudodirectory), followed by any configuration arguments the plugin needs. Those arguments get passed to the plugin's initialization function. If there are arguments, the whole string has to be wrapped in double quotes,""๐
On 64-bit systems, if the plugin is there but won't load, sudoers goes looking for a 64-bit version and loads that as a fallback if it exists โจ The exact rules depend on the system. On Solaris, if the plugin lives in a directory ending in "lib", sudoers appends "/64" to the directory name for the fallback path, so /usr/lib/group_plugin.so becomes /usr/lib/64/group_plugin.so. On Linux, a directory ending in "lib" turns into "lib64" as the fallback, so /usr/lib/group_plugin.so becomes /usr/lib64/group_plugin.so. On all other systems, the fallback gets made by adding a "64" before the file extension, so group_plugin.so becomes group_plugin64.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 very end ๐ฆ
For more info, check out "GROUP PROVIDER PLUGINS" ๐
lectureโ controls when a short lecture gets printed along with the password prompt ๐ The possible values are:alwaysโ always lectures the user ๐ญneverโ never lectures the user ๐ซถonceโ only lectures the user the first time they run sudo ๐
If you don't give it a value, it implies once. Negating the option means never gets used. The default value is never ๐
lecture_fileโ the path to a file with an alternate sudo lecture ๐ If the named file exists, it gets used in place of the standard lecture. By default, sudo uses a built-in lecture ๐งlistpwโ controls when a password is required when a user runs sudo with the-loption ๐ The possible values are:allโ every one of the user's sudoers file entries for the current host has to have theNOPASSWDflag set to skip entering a password ๐ฅน
always๐ โ You'll have to type your password every single time you use the-loption, no exceptions bestie ๐ญanyโจ โ At least one of your sudoers entries for the current host needs theNOPASSWDflag set so you can skip the password prompt ๐never๐ฆ โ You'll never need to enter a password to use the-loption, main character energy ๐
If no value is specified, any is implied ๐ซถ Negating the option gives you never. The default is any ๐
log_format๐ โ The event log format. These are the supported ones:json๐ โ Right now this is just an alias forjson_pretty. In a future version of sudo,jsonwill be equivalent tojson_compact. JSON log entries include the full user details, plus the execution environment if the command was allowed ๐งjson_compact๐ โ Logs events in "compact" (minified) JSON format. Each event is its own JSON object on a single line with zero extra whitespace. Heads up: because of protocol limits, JSON events sent via syslog may get truncated ๐json_pretty๐ธ โ Logs events in "pretty" JSON format. When logging to a file, the whole file is treated as one single JSON object made of multiple events, and each event spans multiple lines. When logging via syslog,json_prettyandjson_compactare exactly the same โจsudo๐ โ Logs events in the traditional sudo-style format. Check out "EVENT LOGGING" for the deets ๐
This setting affects logs sent via syslog(3) as well as the file specified by the logfile setting, if you've got one. The default is sudo ๐
logfile๐ โ The path to the sudo log file (not the syslog one!). Setting a path turns on logging to a file, and negating this option turns it off. By default, sudo logs via syslog ๐ซงmailerflags๐ โ Flags to use when invoking the mailer. Defaults to-t๐ฆmailerpath๐ฌ โ The path to the mail program used to send warning mail. Negate it to stop sudo from sending mail altogether ๐ โโ๏ธ It defaults to the path to sendmail that was found at configure time ๐งmailfrom๐ โ The address used for the "from" address when sending warning and error mail. Wrap the address in double quotes ("") so sudo doesn't get confused by the@sign ๐ It defaults to the name of the user running sudo ๐mailto๐จ โ The address that warning and error mail gets sent to. Negate it to stop sudo from sending mail ๐ซถ Wrap the address in double quotes ("") here too, so sudo doesn't misinterpret the@sign ๐ It defaults toroot๐ธrlimit_as๐ง โ The maximum size (in bytes) the process's address space may grow to, if your OS supports it. See "Resource limits" for more info โจrlimit_core๐ฅ โ The largest size (in bytes) a core dump file may be. See "Resource limits" for more info. Defaults to0, which means no core dump gets created ๐ โโ๏ธrlimit_cpuโฑ๏ธ โ The maximum amount of CPU time (in seconds) the process may use. See "Resource limits" for more info ๐rlimit_data๐๏ธ โ The maximum size (in bytes) of the data segment for the process. See "Resource limits" for more info ๐rlimit_fsize๐ โ The largest file (in bytes) the process may create. See "Resource limits" for more info ๐rlimit_locks๐ โ The maximum number of locks the process may establish, if your OS supports it. See "Resource limits" for more info ๐ฆrlimit_memlock๐ง โ The maximum size (in bytes) the process may lock in memory, if your OS supports it. See "Resource limits" for more info ๐rlimit_nofile๐๏ธ โ The maximum number of files the process may have open. See "Resource limits" for more info โจrlimit_nproc๐โโ๏ธ โ The maximum number of processes the user may run at the same time. See "Resource limits" for more info ๐rlimit_rss๐ง โ The maximum size (in bytes) the process's resident set size may grow to. See "Resource limits" for more info ๐ซงrlimit_stack๐ฅ โ The maximum size (in bytes) the process's stack may grow to. See "Resource limits" for more info ๐ธrestricted_env_file๐ท โ This option specifies the fully qualified path to a file containing variables to be set in the environment of the program being run. Entries in the file should be assignments in the formVARIABLE=valueorexport VARIABLE=value, separated by newline characters. The value may optionally be wrapped in single or double quotes. Shell-style variable substitution is not supported ๐ โโ๏ธ Empty lines and comments starting with a pound sign (#) get ignored ๐
Variables in this file only get added if they don't already exist in the environment. Unlike env_file, the file's contents are not trusted, and they're processed in a way that's similar to the invoking user's environment ๐ซ If env_reset is enabled, variables in the file only get added if they're matched by either the env_check or env_keep list. If env_reset is disabled, variables in the file are added as long as they aren't matched by the env_delete list. Either way, the contents of restricted_env_file are processed before the contents of env_file ๐ง
runchroot๐ฐ โ If set, sudo will use this value as the root directory when running a command. The special value*lets the user pick the root directory with sudo's-Roption. Check out the "Chroot_Spec" section for more details ๐ซถ
You can only use runchroot as a command-specific Defaults setting if the command exists at the same path both inside and outside the chroot jail. This restriction doesn't apply to global, host, or user-based Defaults settings, or to a Cmnd_Spec that includes a Chroot_Spec ๐
This setting is only supported by version 1.9.3 or higher ๐
โ ๏ธ Support for setting the root directory is deprecated and will be removed in a future version of sudo. Plan accordingly, bestie ๐
runcwd๐ โ If set, sudo will use this value as the working directory when running a command. The special value*lets the user pick the working directory with sudo's-Doption. Check out the "Chdir_Spec" section for more details โจ
This setting is only supported by version 1.9.3 or higher ๐ท
secure_path๐ก๏ธ โ If set, sudo will use this value in place of the user'sPATHenvironment variable. There are two basic use cases forsecure_path:- To make it possible for sudo to find system administrator commands in directories that may not be in the default user path, like
/usr/sbin๐งฐ - To help protect scripts and programs that run other commands without first setting
PATHto a safe value. Otherwise, a user with limited privileges may be able to run arbitrary commands by manipulating thePATH, if the command being run executes other commands without using a fully-qualified path name ๐ค
- To make it possible for sudo to find system administrator commands in directories that may not be in the default user path, like
Users in the group specified by the exempt_group option are not affected by secure_path. This option is disabled by default ๐ซง
syslog๐ โ The syslog facility if syslog is being used for logging (negate to disable syslog logging). Defaults toauthpriv๐
The following syslog facilities are supported: authpriv (if your OS supports it), auth, daemon, user, local0, local1, local2, local3, local4, local5, local6, and local7 ๐
syslog_badpri๐จ โ The syslog priority to use when the user isn't allowed to run a command, or when authentication is unsuccessful. Defaults toalert๐ฌ
The following syslog priorities are supported: alert, crit, debug, emerg, err, info, notice, warning, and none. Negating the option, or setting it to a value of none, will disable logging of unsuccessful commands ๐
syslog_goodpri๐ โ The syslog priority to use when the user is allowed to run a command and authentication is successful. Defaults tonotice๐ฆ
See syslog_badpri for the list of supported syslog priorities. Negating the option, or setting it to a value of none, will disable logging of successful commands ๐
verifypw๐ โ This option controls when a password is required when a user runs sudo with the-voption. It has the following possible values:all๐ซถ โ All the user's sudoers file entries for the current host must have theNOPASSWDflag set to avoid entering a passwordalways๐ โ The user must always enter a password to use the-voptionanyโจ โ At least one of the user's sudoers file entries for the current host must have theNOPASSWDflag set to avoid entering a passwordnever๐ฆ โ The user need never enter a password to use the-voption
If no value is specified, all is implied ๐
Negating the option results in a value of never being used. The default is all ๐
Lists that can be used in a boolean context ๐ง โจ
env_check ๐ซง Environment variables that get yanked from the user's environment unless they count as "safe". For everything except TZ, "safe" means the value has no % or / characters in it. This guards against printf-style format vulnerabilities in badly written programs ๐ The TZ variable is considered unsafe if any of these are true:
- It's a fully-qualified path name (optionally starting with a colon
:) that doesn't match where the zoneinfo directory lives. - It contains a
..path element. - It contains white space or non-printable characters.
- It's longer than the value of
PATH_MAX.
The argument can be a double-quoted, space-separated list or a single value with no quotes. You can replace, add to, delete from, or disable the list with the =, +=, -=, and ! operators. Doesn't matter whether env_reset is on or off, anything listed in env_check gets kept in the environment as long as it passes that check โจ The global list shows up when root runs sudo with the -V option.
env_delete ๐๏ธ Environment variables to be removed from the user's environment when env_reset is not in effect. The argument can be a double-quoted, space-separated list or a single value without quotes. Same deal with the =, +=, -=, and ! operators for replacing, adding, deleting, or disabling. The global list to remove is displayed when root runs sudo with the -V option. Lowkey, plenty of operating systems already strip potentially dangerous variables from any set-user-ID process (like sudo) ๐
env_keep ๐ธ Environment variables to keep in the user's environment when env_reset is in effect. This gives you fine-grained control over the environment sudo-spawned processes get ๐ The argument can be a double-quoted, space-separated list or a single value without quotes, and you can use the =, +=, -=, and ! operators to replace, add to, delete from, or disable the list. Root can see the global list of variables to keep with the -V option.
๐จ PreservingHOMEis a security risk, bestie. Plenty of programs use it to hunt down config or data files, and addingHOMEtoenv_keepmay let a user run unrestricted commands via sudo. It's strongly discouraged ๐ญ If someone wants to edit files with sudo, they should runsudoedit(orsudo -e) so they get their usual editor config, instead of invoking the editor directly.
log_servers ๐ฆ A list of one or more servers for remote event and I/O log storage, separated by white space. The log servers have to be running sudo_logsrvd or another service that implements the protocol described in sudo_logsrv.proto(5).
Server addresses should look like host[:port][(tls)]. The host part can be a host name, an IPv4 address, or an IPv6 address wrapped in square brackets.
If the optional tls flag is there, the connection gets secured with Transport Layer Security (TLS) version 1.2 or 1.3. Anything older than TLS 1.2 is not supported, so no throwbacks ๐
If you specify a port, it can be a port number or a well-known service name from the system service name database. If you don't, port 30343 is used for plaintext connections and port 30344 is used for TLS connections.
When log_servers is set, event log data gets logged both locally (see the syslog and log_file settings) and remotely, but I/O log data only goes remote. If multiple hosts are listed, they're tried in reverse order. If no log servers are available, the user can't run a command unless either the ignore_iolog_errors flag (I/O logging enabled) or the ignore_log_errors flag (I/O logging disabled) is set ๐ Same goes if the connection to the log server drops while sudo is running: the command gets terminated, unless ignore_iolog_errors (I/O logging enabled) or ignore_log_errors (I/O logging disabled) is set.
This setting is only supported by version 1.9.0 or higher.
passprompt_regex ๐ A list of POSIX extended regular expressions used to match password prompts in the terminal output. As an extension, if the regular expression begins with (?i), it gets matched case-insensitively. Each regular expression is limited to 1024 characters. This option is only used when log_passwords has been disabled. The default value is [Pp]assword[: ]*
This setting is only supported by version 1.9.10 or higher.
โจ GROUP PROVIDER PLUGINS โจ
The sudoers plugin has its own plugin interface so you can do non-Unix group lookups ๐ง It can query a group source other than the standard Unix group database, which is how you get support for the nonunix_group syntax we talked about earlier.
Group provider plugins are set with the group_plugin setting. The argument should be the plugin path, either fully-qualified or relative to the /usr/libexec/sudo directory, followed by any configuration options the plugin needs. Those options (if you give any) get passed to the plugin's initialization function. If there are options present, the whole string has to be wrapped in double quotes ("") ๐
The following group provider plugins come installed by default:
group_file ๐
The group_file plugin supports an alternate group file that uses the same syntax as the /etc/group file. You pass the path to the group file as an option to the plugin. For example, if the group file you want is /etc/sudo-group:
Defaults group_plugin="group_file.so /etc/sudo-group"
system_group ๐
The system_group plugin does group lookups through the standard C library functions getgrnam(3) and getgrid(3). It's useful when the user belongs to groups that aren't in the user's supplemental group vector. This plugin takes no options:
Defaults group_plugin=system_group.so
The group provider plugin API is described in detail in sudo_plugin(5) ๐
โจ EVENT LOGGING โจ
okay so sudoers can log events in JSON or sudo format, and this bit is all about the sudo format ๐ depending on how sudoers is configured, it can log through syslog(3), to a local log file, or to both ๐ซถ the two versions are almost identical, so you're not learning two things.
the one thing to know about the log data is that any control characters get written in octal with a leading #. so a horizontal tab shows up as #011 and an embedded carriage return shows up as #015 ๐ญ space characters in the command path get stored as #040 too. command line arguments that contain spaces are wrapped in single quotes (''), which is how you can tell "multiple arguments" apart from "one argument that just has spaces in it" ๐ง and literal single quotes and backslashes (\) inside command line arguments get escaped with a backslash.
๐ Accepted command log entries
this is the format for commands that sudo actually runs ๐ฆ it's split across multiple lines here so it's easier to read, but it's really one line:
date hostname progname: username : TTY=ttyname ; CHROOT=chroot ; \
PWD=cwd ; USER=runasuser ; GROUP=runasgroup ; TSID=logid ; \
ENV=env_vars COMMAND=command
here's what each field means, bestie ๐
dateโ the date the command was run ๐ it's usually in the formatMMM, DD, HH:MM:SS. if you're logging viasyslog(3), the actual date format is up to your syslog daemon. if you're logging to a file and thelog_yearoption is on, the date also includes the year.hostnameโ the name of the host sudo was run on ๐ฅ๏ธ this only shows up when logging viasyslog(3).prognameโ the name of the program, usuallysudoorsudoedit๐ also only shows up when logging viasyslog(3).usernameโ the login name of the user who ran sudo ๐ซถttynameโ the short name of the terminal sudo was run on (likeconsole,tty01, orpts/0) โจ if there wasn't a terminal, it saysunknown.chrootโ the root directory the command was run in, but only if one was specified ๐ธcwdโ the current working directory sudo was run from ๐runasuserโ the user the command ran as ๐runasgroupโ the group the command ran as, but only if one was specified on the command line ๐logidโ an I/O log identifier you can use to replay the command's output ๐ฌ this only appears when thelog_inputorlog_outputoption is enabled.env_varsโ a list of environment variables set on the command line, if any were specified ๐commandโ the actual command that got executed, including any command line arguments ๐ฅ
messages get logged using the locale set by sudoers_locale, which defaults to the C locale. just so you know ๐ค
๐ Denied command log entries
if the user isn't allowed to run the command, the reason for the denial comes right after the user name. the possible reasons are:
user NOT in sudoersโ the user isn't listed in the sudoers file at all ๐user NOT authorized on hostโ the user is listed in the sudoers file, but they aren't allowed to run commands on this host ๐ โโ๏ธcommand not allowedโ the user is listed for this host, but they aren't allowed to run that specific command ๐ญ3 incorrect password attemptsโ the user failed to enter their password after 3 tries. the actual number of tries can vary depending on how many attempts failed and the value of thepasswd_triesoption ๐a password is requiredโ the-noption was specified, but a password was needed anyway ๐sorry, you are not allowed to set the following environment variablesโ the user tried to set environment variables on the command line that sudoers doesn't allow ๐ซ
๐ฅ Error log entries
if an error happens, sudoers logs a message and, in most cases, emails the administrator too ๐ง these are the errors that can pop up, periodt:
parse error in /etc/sudoers near line Nโ sudoers hit an error while parsing the file ๐ง depending on the type of error, the real problem might be one line above or below the line number it gives you.problem with defaults entriesโ the sudoers file has one or more unknown Defaults settings ๐ซ this doesn't stop sudo from running, but you should check the sudoers file withvisudo.timestamp owner (username): No such userโ the time stamp directory owner (set by thetimestampownersetting) couldn't be found in the password database ๐unable to open/read /etc/sudoersโ the sudoers file couldn't be opened for reading ๐ต this can happen when the file lives on a remote file system that maps user-ID 0 to something else. normally sudoers tries to open the file using group permissions to dodge this. you can either change the ownership of/etc/sudoers, or add an argument likesudoers_uid=N(whereNis the user-ID that owns the sudoers file) to the end of the sudoers Plugin line in thesudo.conf(5)file ๐ ๏ธunable to open /etc/sudoersโ the/etc/sudoersfile is missing ๐ฑ/etc/sudoers is not a regular fileโ the/etc/sudoersfile exists, but it isn't a regular file or a symbolic link ๐/etc/sudoers is owned by uid N, should be 0โ the sudoers file has the wrong owner ๐ โโ๏ธ if you want the owner changed, addsudoers_uid=N(whereNis the user-ID that owns the sudoers file) to the sudoers Plugin line in thesudo.conf(5)file./etc/sudoers is world writableโ the permissions let every user write to the sudoers file ๐จ it must not be world-writable! the default file mode is0440(readable by owner and group, writable by nobody). you can change the default mode with thesudoers_modeoption on the sudoers Plugin line in thesudo.conf(5)file./etc/sudoers is owned by gid N, should be 1โ the sudoers file has the wrong group ownership ๐ if you want to change the group, addsudoers_gid=N(whereNis the group-ID that owns the sudoers file) to the sudoers Plugin line in thesudo.conf(5)file.unable to open /run/sudo/ts/user-IDโ sudoers couldn't read or create the user's time stamp file ๐ฅฒ this can happen whentimestampowneris set to someone other than root, and/run/sudoisn't searchable by group or other. the default mode for/run/sudois0711.unable to write to /run/sudo/ts/user-IDโ sudoers couldn't write to the user's time stamp file โ๏ธ/run/sudo/ts is owned by uid X, should be Yโ the time stamp directory is owned by someone other thantimestampowner๐ง this can happen when the value oftimestampownerhas been changed. sudoers will ignore the time stamp directory until the owner is fixed./run/sudo/ts is group writableโ the time stamp directory is group-writable, but it should only be writable bytimestampowner๐ซ the default mode for the time stamp directory is0700. sudoers will ignore the time stamp directory until the mode is fixed.
๐ฆ Notes on logging via syslog
by default, sudoers logs messages via syslog(3) ๐ฎ the date, hostname, and progname fields get added by the system's syslog(3) function, not by sudoers itself. so they can look different depending on the system ๐
the maximum size of syslog messages also varies from system to system. the syslog_maxlen setting lets you change the maximum syslog message size from the default of 980 bytes โจ for more info, check out the description of syslog_maxlen.
๐ธ Notes on logging to a file
if the logfile option is set, sudoers logs to a local file, like /var/log/sudo ๐ when it logs to a file, it uses a format similar to syslog(3), but with a few important differences:
- the progname field is not present.
- the hostname is only logged if the
log_hostoption is enabled. - the date doesn't include the year unless the
log_yearoption is enabled. - lines longer than
loglinelencharacters (80 by default) get word-wrapped and continued on the next line with a four character indent ๐งต this makes entries way easier for a human to read, but it's harder to usegrep(1)on the log files ๐ญ if you setloglinelento0(or negate it with a!), word wrap gets disabled.
โจ I/O LOGGING โจ
okay so when I/O logging is turned on, sudo runs the command inside a pseudo-terminal ๐ฅ๏ธ and logs the user input and/or output, depending on which sudoers flags are enabled ๐ . There are five distinct types of I/O that can get logged, and each one has its own sudoers flag ๐ซถ
- terminal input (
log_ttyin) โ keystrokes the user types โจ๏ธ - terminal output (
log_ttyout) โ command output shown on the screen ๐บ - standard input (
log_stdin) โ input coming from a pipe or a file ๐ฅ - standard output (
log_stdout) โ output going to a pipe or a file ๐ค - standard error (
log_stderr) โ error output going to a pipe or a file ๐จ
Also, the log_input flag and the LOG_INPUT command tag both turn on log_ttyin and log_stdin together ๐. The log_output flag and the LOG_OUTPUT command tag turn on log_ttyout, log_stdout, and log_stderr all at once ๐ธ
To capture terminal input and output, sudo runs the command in a pseudo-terminal, logs the input and output, and then passes it along to the user ๐ง To capture standard input, standard output, or standard error, sudo uses a pipe to sit right in the middle of the stream, logs the I/O, and then hands it off to the other end of the pipe ๐ฆ
I/O can be logged locally on the machine or sent to a remote log server ๐ For local logs, the I/O goes into the directory set by the iolog_dir option (/var/log/sudo-io by default), and each session gets a unique session ID that shows up in the sudo log line, prefixed with TSID=. The iolog_file option controls how that session ID is formatted โจ For remote logs, the log_servers setting names one or more log servers running sudo_logsrvd or some other server that speaks the protocol described in sudo_logsrv.proto(5) ๐
I/O logging pitfals ๐
okay this part is important, bestie ๐ญ When standard input is being logged, anything sent to standard input gets consumed, even if the command you ran with sudo isn't actually reading standard input at all ๐ This can go sideways when you use sudo inside a shell script that's supposed to process standard input. Say you've got this script:
#!/bin/sh
sudo echo testing
echo done
It behaves exactly as expected when you pass the script to the shell as an argument:
$ sh test.sh
testing
done
But if you pass the script to the shell on standard input instead, the sudo echo testing command eats the rest of the script ๐ฑ That means the echo done line never gets executed:
$ sh -s < test.sh
testing
Good news though, there are a few ways to work around this ๐ซถ
- Redirect standard input from
/dev/nullwhen you run a command through sudo that doesn't need to read standard input.
sudo echo testing < /dev/null
- Pass the script to the shell by its path name instead of sending it over standard input.
sh test.sh
- Turn off logging of standard input for commands that don't need to read it.
Defaults!/bin/echo !log_stdin
Sometimes logging standard input or standard output just isn't worth it ๐
โโ๏ธ For example, commands that push or pull huge amounts of data through standard output or standard input, like rsync(1) and tar(1), can fill up your log file system with superfluous data ๐ฆ You can turn off logging of standard input and standard output for those commands like this:
Cmnd_Alias COPY_CMDS = /usr/bin/tar, /usr/bin/cpio, /usr/bin/rsync
# Log input and output but omit stdin and stdout when copying files.
Defaults log_input, log_output
Defaults!COPY_CMDS !log_stdin, !log_stdout
โ ๏ธ Be aware, bestie: using thelog_inputflag or theLOG_INPUTcommand tag will also turn onlog_stdin. And thelog_ouputflag or theLOG_OUTPUTcommand tag will turn onlog_stdoutandlog_stderrtoo ๐ญ Careful ordering of your rules might be needed to get the results you actually want ๐
I/O log format ๐
For both local and remote I/O logs, each log lives in its own directory, and that directory holds the following files ๐๏ธ
logโ a plain text file with info about the command ๐ The first line is a set of colon-delimited fields: the time the command was run, the name of the user who ran sudo, the name of the target user, the name of the target group (optional), the terminal sudo was run from, and the number of lines and columns of that terminal. The second and third lines hold the working directory the command was run from and the path name of the command itself (with arguments, if there were any).log.jsonโ a JSON-formatted file with info about the command ๐ It's kind of like the log file but has extra info and is super easy to extend.sudoreplay(8)will uselog.jsonover the log file if it exists. The file may contain these elements:timestampโ a JSON object with the time the command was run. It has two values, seconds and nanoseconds.columnsโ the number of columns of the terminal the command ran on, or zero if there wasn't a terminal.commandโ the fully-qualified path of the command that was run.linesโ the number of lines of the terminal the command ran on, or zero if there wasn't a terminal.runargvโ a JSON array holding the command's argument vector, as passed to theexecve(2)system call.runenvโ a JSON array holding the command's environment, as passed to theexecve(2)system call.rungidโ the group ID the command ran as. This element only shows up when the user specifies a group on the command line.rungroupโ the name of the group the command ran as. This element only shows up when the user specifies a group on the command line.runuidโ the user ID the command ran as.runuserโ the name of the user the command ran as.submitcwdโ the current working directory at the time sudo was run.submithostโ the name of the host the command was run on.submituserโ the name of the user who ran the command via sudo.ttynameโ the path name of the terminal the user invoked sudo from. If the command ran in a pseudo-terminal,ttynamewill be different from the terminal the command actually ran in.
timingโ timing info used to replay the session ๐ฌ Each line has the I/O log entry type, the amount of time since the last entry, and then some type-specific data. The entry types and their data are:0โ standard input, number of bytes in the entry1โ standard output, number of bytes in the entry2โ standard error, number of bytes in the entry3โ terminal input, number of bytes in the entry4โ terminal output, number of bytes in the entry5โ window change, new number of lines and columns6โ bug compatibility for sudo 1.8.7 terminal output7โ command suspend or resume, signal received
ttyinโ raw input from the user's terminal, exactly as it was received ๐ This file only shows up if thelog_inputorlog_ttyinflags are set and sudo was run from a terminal. No post-processing happens here. If you want to read it by hand, you might wish to convert carriage return characters into line feeds. For example:gunzip -c ttyin | tr "\r" "\n"stdinโ the standard input when there's no terminal, or input redirected from a pipe or file. This file only shows up if thelog_inputorlog_stdinflags are set and standard input isn't connected to a terminal.ttyoutโ output from the pseudo-terminal, aka what the command writes to the screen ๐บ Terminal-specific post-processing gets done before the data is logged. So line feeds are usually turned into line feed/carriage return pairs, and tabs may get expanded into spaces. This file only shows up if thelog_outputorlog_ttyoutflags are set and sudo was run from a terminal.stdoutโ the standard output when there's no terminal, or output redirected to a pipe or file. This file only shows up if thelog_outputorlog_stdoutflags are set and standard output isn't connected to a terminal.stderrโ the standard error when there's no terminal, or output redirected to a pipe or file ๐จ This file only shows up if thelog_outputorlog_stderrflags are set and standard error isn't connected to a terminal.
Every file except log gets compressed in gzip format, unless the compress_io flag has been disabled ๐๏ธ Because of buffering, you normally can't watch the I/O logs in real-time while the program is running โณ The I/O log data won't be complete until the program run by sudo has exited or been terminated by a signal. If you want data written to disk as soon as it's available, the iolog_flush flag disables buffering for that. The output portion of an I/O log can be viewed with the sudoreplay(8) utility, which can also list or search the available logs ๐
๐ Big warning, bestie: user input may contain sensitive stuff like passwords (even if they aren't echoed to the screen), and those get stored in the log file unencrypted ๐ญ In most cases, logging the command output withlog_outputorLOG_OUTPUTis all you need. When you do log input, think about turning off thelog_passwordsflag.
Since each session's I/O logs live in their own directory, regular log rotation tools can't be used to limit how many I/O logs you keep ๐ The simplest way to cap it is to set the maxseq option to the maximum number of logs you want to store. Once the I/O log sequence number reaches maxseq, it resets to zero, and sudoers will truncate and reuse any existing I/O logs ๐โจ
โจ FILES โจ
Here's where all the important sudo stuff lives, bestie ๐๏ธ๐
/etc/sudo.confโ the sudo front-end config ๐/etc/sudoersโ the list of who can run what ๐/etc/groupโ the local groups file ๐ซถ/etc/netgroupโ the list of network groups ๐ธ/var/log/sudo-ioโ the I/O log files ๐/run/sudo/tsโ the directory holding time stamps for the sudoers security policy โฐ/var/lib/sudo/lecturedโ the directory holding lecture status files for the sudoers security policy ๐/etc/environmentโ the starting environment for-imode on AIX and Linux systems ๐ง
โจ EXAMPLES โจ
Okay so, here are some example sudoers file entries. Admittedly, some of these are a bit contrived ๐ but they're iconic for showing how everything fits together. First, we let a few environment variables pass through and then define our aliases:
# Run X applications through sudo; HOME is used to find the
# .Xauthority file. Other programs use HOME to locate configuration
# files and this may lead to privilege escalation!
Defaults env_keep += "DISPLAY HOME"
# User alias specification
User_Alias FULLTIMERS = millert, mikef, dowdy
User_Alias PARTTIMERS = bostley, jwfox, crawl
User_Alias WEBADMIN = will, wendy, wim
# Runas alias specification
Runas_Alias OP = root, operator
Runas_Alias DB = oracle, sybase
Runas_Alias ADMINGRP = adm, oper
# Host alias specification
Host_Alias SPARC = bigtime, eclipse, moet, anchor :\
SGI = grolsch, dandelion, black :\
ALPHA = widget, thalamus, foobar :\
HPPA = boa, nag, python
Host_Alias CUNETS = 128.138.0.0/255.255.0.0
Host_Alias CSNETS = 128.138.243.0, 128.138.204.0/24, 128.138.242.0
Host_Alias SERVERS = primary, mail, www, ns
Host_Alias CDROM = orion, perseus, hercules
# Cmnd alias specification
Cmnd_Alias DUMPS = /usr/bin/mt, /usr/sbin/dump, /usr/sbin/rdump,\
/usr/sbin/restore, /usr/sbin/rrestore,\
sha224:0GomF8mNN3wlDt1HD9XldjJ3SNgpFdbjO1+NsQ== \
/home/operator/bin/start_backups
Cmnd_Alias KILL = /usr/bin/kill
Cmnd_Alias PRINTING = /usr/sbin/lpc, /usr/bin/lprm
Cmnd_Alias SHUTDOWN = /usr/sbin/shutdown
Cmnd_Alias HALT = /usr/sbin/halt
Cmnd_Alias REBOOT = /usr/sbin/reboot
Cmnd_Alias SHELLS = /usr/bin/sh, /usr/bin/csh, /usr/bin/ksh,\
/usr/local/bin/tcsh, /usr/bin/rsh,\
/usr/local/bin/zsh
Cmnd_Alias SU = /usr/bin/su
Cmnd_Alias PAGERS = /usr/bin/more, /usr/bin/pg, /usr/bin/less
Next up, we override some of the compiled-in default values ๐ We want sudo to log via syslog(3) using the auth facility in all cases, and for commands to run with the target user's home directory as the working directory. We don't want to subject the full time staff to the sudo lecture ๐ User millert doesn't need to provide a password, and we don't want to reset the LOGNAME or USER environment variables when running commands as root. Also, on the machines in the SERVERS Host_Alias, we keep an additional local log file and make sure we log the year in each log line, since those entries will be kept around for several years ๐ Lastly, we disable shell escapes for the commands in the PAGERS Cmnd_Alias (/usr/bin/more, /usr/bin/pg and /usr/bin/less). This will not effectively constrain users with sudo ALL privileges, so keep that in mind ๐
# Override built-in defaults
Defaults syslog=auth,runcwd=~
Defaults>root !set_logname
Defaults:FULLTIMERS !lecture
Defaults:millert !authenticate
Defaults@SERVERS log_year, logfile=/var/log/sudo.log
Defaults!PAGERS noexec
The User specification is the part that actually decides who may run what, so this is the main character moment ๐
root ALL = (ALL) ALL
%wheel ALL = (ALL) ALL
We let root and any user in group wheel run any command on any host as any user. Iconic ๐
FULLTIMERS ALL = NOPASSWD: ALL
Full time sysadmins (millert, mikef, and dowdy) may run any command on any host without authenticating themselves ๐ฅ
PARTTIMERS ALL = ALL
Part time sysadmins (bostley, jwfox, and crawl) may run any command on any host, but they have to authenticate themselves first, since the entry lacks the NOPASSWD tag ๐ซถ
jack CSNETS = ALL
The user jack may run any command on the machines in the CSNETS alias (the networks 128.138.243.0, 128.138.204.0, and 128.138.242.0). Of those networks, only 128.138.204.0 has an explicit netmask (in CIDR notation) indicating it's a class C network. For the other networks in CSNETS, the local machine's netmask will be used during matching ๐ง
lisa CUNETS = ALL
The user lisa may run any command on any host in the CUNETS alias (the class B network 128.138.0.0) ๐ฆ
operator ALL = DUMPS, KILL, SHUTDOWN, HALT, REBOOT, PRINTING,\
sudoedit /etc/printcap, /usr/oper/bin/
The operator user may run commands limited to simple maintenance ๐ ๏ธ Here, those are commands related to backups, killing processes, the printing system, shutting down the system, and any commands in the directory /usr/oper/bin/. One command in the DUMPS Cmnd_Alias includes a sha224 digest, /home/operator/bin/start_backups. This is because the directory containing the script is writable by the operator user. If the script is modified (resulting in a digest mismatch), it will no longer be possible to run it via sudo ๐ซ
joe ALL = /usr/bin/su operator
The user joe may only su(1) to operator ๐
pete HPPA = /usr/bin/passwd [A-Za-z]*, !/usr/bin/passwd *root*
The user pete is allowed to change anyone's password except for root on the HPPA machines. Because command line arguments are matched as a single, concatenated string, the โ*โ wildcard will match multiple words. This example assumes that passwd(1) does not take multiple user names on the command line. On systems with GNU getopt(3), options to passwd(1) may be specified after the user argument. As a result, this rule will also allow:
passwd username --expire
which may not be desirable ๐ฌ
%opers ALL = (: ADMINGRP) /usr/sbin/
Users in the opers group may run commands in /usr/sbin/ as themselves, with any group in the ADMINGRP Runas_Alias (the adm and oper groups) ๐
bob SPARC = (OP) ALL : SGI = (OP) ALL
The user bob may run anything on the SPARC and SGI machines as any user listed in the OP Runas_Alias (root and operator.) โจ
jim +biglab = ALL
The user jim may run any command on machines in the biglab netgroup. sudo knows that โbiglabโ is a netgroup because of the โ+โ prefix ๐ง
+secretaries ALL = PRINTING, /usr/bin/adduser, /usr/bin/rmuser
Users in the secretaries netgroup need to help manage the printers, as well as add and remove users, so they're allowed to run those commands on all machines ๐
fred ALL = (DB) NOPASSWD: ALL
The user fred can run commands as any user in the DB Runas_Alias (oracle or sybase) without giving a password ๐
john ALPHA = /usr/bin/su [!-]*, !/usr/bin/su *root*
On the ALPHA machines, user john may su to anyone except root, but he isn't allowed to specify any options to the su(1) command ๐
jen ALL, !SERVERS = ALL
The user jen may run any command on any machine except for those in the SERVERS Host_Alias (primary, mail, www, and ns) ๐
jill SERVERS = /usr/bin/, !SU, !SHELLS
For any machine in the SERVERS Host_Alias, jill may run any commands in the directory /usr/bin/ except for those commands belonging to the SU and SHELLS Cmnd_Aliases. While not specifically mentioned in the rule, the commands in the PAGERS Cmnd_Alias all reside in /usr/bin and have the noexec option set ๐
steve CSNETS = (operator) /usr/local/op_commands/
The user steve may run any command in the directory /usr/local/op_commands/, but only as user operator ๐ธ
matt valkyrie = KILL
On his personal workstation, valkyrie, matt needs to be able to kill hung processes ๐
WEBADMIN www = (www) ALL, (root) /usr/bin/su www
On the host www, any user in the WEBADMIN User_Alias (will, wendy, and wim) may run any command as user www (which owns the web pages), or simply su(1) to www ๐ธ๏ธ
ALL CDROM = NOPASSWD: /sbin/umount /CDROM,\
/sbin/mount -o nosuid\,nodev /dev/cd0a /CDROM
Any user may mount or unmount a CD-ROM on the machines in the CDROM Host_Alias (orion, perseus, hercules) without entering a password. This is a bit tedious to type, so it's a prime candidate for encapsulating in a shell script ๐
โจ SECURITY NOTES โจ
Real talk, bestie, this section is important ๐จ Read it before you get creative with your rules ๐ซถ
Limitations of the โ!โ operator
It's generally not effective to โsubtractโ commands from ALL using the โ!โ operator. A user can trivially get around this by copying the desired command to a different name and then executing that. For example:
bill ALL = ALL, !SU, !SHELLS
Doesn't really stop bill from running the commands listed in SU or SHELLS, since he can simply copy those commands to a different name, or use a shell escape from an editor or other program ๐ Therefore, these kinds of restrictions should be considered advisory at best (and reinforced by policy).
In general, if a user has sudo ALL, there's nothing to prevent them from creating their own program that gives them a root shell (or making their own copy of a shell), regardless of any โ!โ elements in the user specification ๐ญ
Security implications of fast_glob
If the fast_glob option is in use, it's not possible to reliably negate commands where the path name includes globbing (aka wildcard) characters. This is because the C library's fnmatch(3) function cannot resolve relative paths. While this is typically only an inconvenience for rules that grant privileges, it can result in a security issue for rules that subtract or revoke privileges ๐จ
For example, given the following sudoers file entry:
john ALL = /usr/bin/passwd [a-zA-Z0-9]*, /usr/bin/chsh [a-zA-Z0-9]*,\
/usr/bin/chfn [a-zA-Z0-9]*, !/usr/bin/* root
User john can still run โ/usr/bin/passwd rootโ if fast_glob is enabled by changing to /usr/bin and running โ./passwd rootโ instead ๐ฑ
Another potential issue is that when sudo executes the command, it must use the command or path specified by the user instead of a path listed in the sudoers file. This may lead to a time of check versus time of use race condition โฑ๏ธ
Wildcards in command arguments
Command line arguments are matched as a single, concatenated string. This means a wildcard character such as โ?โ or โ*โ will match across word boundaries, which may be unexpected ๐ For example, while a sudoers entry like:
Right, so the example that looks harmless is actually a trap ๐. %operator ALL = /bin/cat /var/log/messages* lets operator run:
$ sudo cat /var/log/messages.1
but it also lets them run:
$ sudo cat /var/log/messages /etc/shadow
which is definitely not what you meant ๐. A safer move is a regular expression for the command line arguments. You can rewrite the example like this:
%operator ALL = /bin/cat ^/var/log/messages[^[:space:]]*$
This regex only matches a single file whose name starts with /var/log/messages and has no white space in it โจ. Honestly, for anything non-trivial, it's better to do the argument processing outside the sudoers file, in a proper scripting language ๐ซถ.
Regular expressions in command names
Using a regex to match a command name comes with the same security baggage as the fast_glob option ๐ฌ:
- It is not possible to reliably negate commands when the path name is a regular expression.
- When sudo executes the command, it must use the command or path specified by the user instead of a path listed in the sudoers file. This may lead to a time of check versus time of use race condition.
These issues do not apply to rules where only the command line options are matched with a regular expression ๐.
Preventing shell escapes
Once sudo runs a program, that program can do literally whatever it wants, including running other programs ๐. This is a real security problem, because it's super common for a program to allow shell escapes, which lets a user dodge sudo's access control and logging. Usual suspects include shells (obviously ๐ ), editors, paginators, mail, and terminal programs.
There are four basic approaches to this problem:
- restrict โ Keep users away from commands that let them run arbitrary commands ๐ซ. Many editors have a restricted mode where shell escapes are disabled, though sudoedit is a better way to run editors via sudo. There are just so many programs with shell escapes that restricting users to the ones that don't is often unworkable ๐ญ.
- intercept โ On most systems, sudo's intercept functionality can transparently catch an attempt to run a new command, allow or deny it based on sudoers rules, and log the result ๐ฆ. For example, this can restrict the commands run from within a privileged shell or editor. Heads up though: not all programs behave correctly when intercept is enabled.
There are two underlying mechanisms that can implement intercept mode: dso and trace. The intercept_type setting lets you pick between them ๐ง.
The first one, dso, overrides the standard C library functions used to execute a command. It does this by setting an environment variable (usually LD_PRELOAD) to the path of a dynamic shared object (a shared library) containing custom versions of the execve(2), execl(3), execle(3), execlp(3), execv(3), execvp(3), execvpe(3), and system(3) library functions. Those custom versions connect back to sudo for a policy decision. Note that this only applies to dynamically-linked executables ๐. You can't intercept commands for statically-linked executables or executables that run under binary emulation this way. Most dynamic loaders ignore LD_PRELOAD (or the equivalent) when running set-user-ID and set-group-ID programs, so sudoers will not permit those programs to run in intercept mode by default. The dso mechanism is incompatible with sudo's SELinux RBAC support (but see below). SELinux disables LD_PRELOAD by default and interferes with the file descriptor inheritance sudo relies on ๐.
The second one, trace, is available on Linux systems that support seccomp(2) filtering. Instead of pre-loading a dynamic shared object, it uses ptrace(2) and seccomp(2) to intercept the execve(2) system call โจ. Both static and dynamic executables are supported, and it's compatible with sudo's SELinux RBAC mode. Functions that use the execveat(2) system call, such as fexecve(3), are not currently intercepted. Programs that rely on ptrace(2) themselves, like debuggers and system call tracers (such as strace(1) and truss(1)), won't be able to function if intercept is enabled in trace mode. The same restriction applies to the log_subcmds sudoers option.
The intercept feature is known to work on Solaris, *BSD, Linux, macOS, HP-UX 11.x and AIX 5.3 and above ๐. It should be supported on most operating systems that support the LD_PRELOAD environment variable or an equivalent. It is not possible to intercept shell built-in commands, or to restrict the ability to read or write sensitive files from within a shell.
To turn on intercept mode for a specific command, use the INTERCEPT tag, as documented in the User Specification section above. Here is that example again:
chuck research = INTERCEPT: ALL
This lets user chuck run any command on the machine โresearchโ in intercept mode ๐. Any commands run via shell escapes will be validated and logged by sudo. If you're not sure whether your system can support intercept, just try it and check whether external commands run via a shell get logged when intercept is enabled ๐.
There is an inherent race condition between when a command is checked against sudoers rules and when it is actually executed. If a user is allowed to run arbitrary commands, they may be able to change the execve(2) arguments in the program after the sudoers policy check has completed but before the new command is executed. Starting with version 1.9.12, the trace method will verify that the command and its arguments have not changed after execve(2) has completed but before execution of the new program has had a chance to run. This is not the case with the dso method. See the description of the intercept_verify setting for more information โ ๏ธ.
- log โ There are two separate but related ways to log additional commands ๐. The first is to enable I/O logging with the log_output flag. This logs the command's output, but it won't create an event log entry when the additional command is run. The second is to enable the log_subcmds flag in sudoers, which creates an event log entry every time a new command runs ๐ซถ. If I/O logging is also enabled, the log entry includes a time offset into the I/O log showing when the command ran. You can pass that offset to the sudoreplay(8) utility to replay the I/O log at the exact moment the command was run ๐ฌ. The log_subcmds flag uses the same mechanism as intercept (see above), so it has the same limitations.
- noexec โ sudo's noexec functionality can stop a program run by sudo from executing any other programs ๐. On most systems it uses the same LD_PRELOAD mechanism as intercept (see above), so the same caveats apply. noexec can block execution of commands run via 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) functions. On Linux, a seccomp(2) filter is used to implement noexec. On Solaris 10 and higher, noexec uses Solaris privileges instead of the LD_PRELOAD environment variable ๐ธ.
To enable noexec for a command, use the NOEXEC tag, as documented in the User Specification section above. Here is that example again:
aaron shanty = NOEXEC: /usr/bin/more, /usr/bin/vi
This lets user aaron run /usr/bin/more and /usr/bin/vi with noexec enabled. That stops those two commands from executing other commands (like a shell) ๐ โโ๏ธ. If you're unsure whether your system supports noexec, just try it and check whether shell escapes work when noexec is enabled.
Restricting shell escapes is not a panacea, babe ๐ญ. Programs running as root can still do plenty of hazardous things, like changing or overwriting files, that could lead to unintended privilege escalation. In the specific case of an editor, a much safer approach is to give the user permission to run sudoedit (see below) ๐ .
Secure editing
The sudoers plugin includes sudoedit support, which lets users securely edit files with the editor of their choice ๐. Since sudoedit is a built-in command, it must be specified in the sudoers file without a leading path. It can still take command line arguments just like a normal command. Wildcards used in sudoedit command line arguments are expected to be path names, so a forward slash (โ/โ) will not be matched by a wildcard ๐ง.
Unlike other sudo commands, the editor runs with the permissions of the invoking user, and with the environment unmodified ๐. You can find more info in the description of the -e option in sudo(8).
For example, to let user operator edit the โmessage of the dayโ file on any machine:
operator ALL = sudoedit /etc/motd
Then the operator user runs sudoedit like this:
$ sudoedit /etc/motd
The editor runs as the operator user, not root, on a temporary copy of /etc/motd. After the file has been edited, /etc/motd gets updated with the contents of that temporary copy ๐โจ.
Users should never be granted sudoedit permission to edit a file that lives in a directory they can write to, whether directly or through a wildcard. ๐ญ If they can write to that directory, they can swap the real file for a link pointing at some other file, and then they can edit whatever they want. ๐ To stop that, starting with version 1.8.16, sudoedit won't follow symbolic links in writable directories and will refuse to edit a file sitting in one, unless the sudoedit_checkdir option has been disabled or the invoking user is root. โจ Also, in version 1.8.15 and higher, sudoedit won't open a symbolic link unless the sudoedit_follow option is enabled or the sudoedit command has the FOLLOW tag in the sudoers file. ๐ซถ
๐ฐ๏ธ Time stamp file checks
sudoers checks who owns its time stamp directory (/run/sudo/ts by default). If that directory isn't owned by root, or if anyone other than root can write to it, sudoers ignores what's inside. ๐
โโ๏ธ Older versions of sudo stored time stamp files in /tmp, and that's no longer recommended, because on systems that let unprivileged users change the ownership of files they create, someone could make the time stamp themselves. ๐
The time stamp directory should get cleared at reboot, but not every system has a /run or /var/run directory. ๐ฅบ So to avoid problems, sudoers ignores time stamp files from before the machine booted, on systems where the boot time is available. โฐ
Some systems with graphical desktop environments let unprivileged users change the system clock. ๐ sudoers relies on the system clock to validate time stamps, so on those systems a user could potentially keep running sudo for longer than timestamp_timeout just by setting the clock back. Rude! ๐ค To shut that down, sudoers uses a monotonic clock (one that never moves backwards) for its time stamps when the system supports it. ๐
sudoers won't honor time stamps set way in the future. Any time stamp dated later than current_time + 2 * TIMEOUT gets ignored, and sudoers logs it and complains. ๐จ
If the timestamp_type option is set to โttyโ, the time stamp record includes the device number of the terminal the user authenticated with. ๐ That gives you per-terminal granularity, but time stamp records can still outlive the user's session. ๐ป
Unless timestamp_type is set to โglobalโ, the time stamp record also includes the session ID of the process that last authenticated. ๐ That stops processes in different terminal sessions from sharing one time stamp record. On systems where a process's start time can be queried, the session leader's start time gets recorded in the time stamp record too. If there's no terminal, or timestamp_type is set to โppidโ, the parent process's start time is used instead. ๐ง In most cases, this stops a time stamp record from being reused without a password when you log out and back in. โจ
๐ DEBUGGING ๐
Versions 1.8.4 and higher of the sudoers plugin come with a flexible debugging framework that helps you track down what the plugin is doing internally when something goes wrong. ๐ You configure it in the sudo.conf(5) file. ๐
The sudoers plugin uses the same debug flag format as the sudo front-end: subsystem@priority. ๐
The priorities sudoers uses, from most severe to least, are: crit, err, warn, notice, diag, info, trace, and debug. Each priority also includes every priority above it. ๐ For example, notice covers debug messages logged at notice and higher. ๐
Here are the subsystems the sudoers plugin uses:
aliasโ User_Alias, Runas_Alias, Host_Alias and Cmnd_Alias processing ๐ท๏ธallโ matches every subsystem ๐auditโ BSM and Linux audit code ๐งพauthโ user authentication ๐defaultsโ sudoers file Defaults settings โ๏ธenvโ environment handling ๐ฟldapโ LDAP-based sudoers ๐loggingโ logging support ๐ชตmatchโ matching of users, groups, hosts, and netgroups in the sudoers file ๐ฏnetifโ network interface handling ๐nssโ network service switch handling in sudoers ๐parserโ sudoers file parsing ๐งฉpermsโ permission setting ๐pluginโ The equivalent of main for the plugin. ๐ptyโ pseudo-terminal related code ๐ฅ๏ธrbtreeโ redblack tree internals ๐ณsssdโ SSSD-based sudoers ๐ชชutilโ utility functions ๐ ๏ธ
Here's an example:
Debug sudoers.so /var/log/sudoers_debug match@info,nss@info
For more info, check out the sudo.conf(5) manual. ๐๐
๐ SEE ALSO ๐
ssh(1), su(1), fnmatch(3), glob(3), mktemp(3), strftime(3), sudo.conf(5), sudo_logsrv.proto(5), sudo_plugin(5), sudoers.ldap(5), sudoers_timestamp(5), sudo(8), sudo_logsrvd(8), visudo(8) ๐ฆ
๐ฉโ๐ป AUTHORS ๐ฉโ๐ป
Loads of people have worked on sudo over the years. ๐ฅฐ This version is mainly code written by:
Todd C. Miller
For the full list of everyone who's contributed, check out the CONTRIBUTORS.md file in the sudo distribution, at https://www.sudo.ws/about/contributors/. ๐ซถ
โ ๏ธ CAVEATS โ ๏ธ
The sudoers file should ALWAYS be edited with the visudo utility, because it locks the file and checks for syntax errors. ๐ฑ If sudoers has syntax errors, sudo may refuse to run, and that's a serious problem if sudo is your only way to get superuser privileges. ๐
Recent versions of sudoers try to recover from a syntax error by ignoring the rest of the line where the error happened. ๐ฉน Older versions of sudo won't run at all if sudoers has a syntax error. ๐
When you use netgroups of machines (as opposed to users), if you store fully qualified host names in the netgroup (which is usually the case), then either the machine's host name has to be fully qualified as returned by the hostname command, or you need to use the fqdn option in sudoers. ๐
๐ BUGS ๐
If you think you've found a bug in sudoers, you can file a bug report in the sudo bug database at https://bugzilla.sudo.ws/, or open an issue at https://github.com/sudo-project/sudo/issues. ๐ If you'd rather use email, you can send messages to the sudo-workers mailing list, 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 found athttps://www.sudo.ws/dist/PGPKEYS. ๐
๐ฌ SUPPORT ๐ฌ
You can get limited free support through the sudo-users mailing list. ๐ธ Go to https://www.sudo.ws/mailman/listinfo/sudo-users to subscribe or to search the archives. ๐
๐ DISCLAIMER ๐
sudo is provided โAS ISโ, and any express or implied warranties are disclaimed, including, but not limited to, the implied warranties of merchantability and fitness for a particular purpose. ๐ For complete details, see the LICENSE.md file distributed with sudo, or https://www.sudo.ws/about/license/. โญ
And that's the whole page, bestie! ๐ Sudo 1.9.17p2, from June 7, 2025, is the version we've been reading about, so go forth and sudo responsibly. ๐ โจ๐ซถ
๐ฆ where this page comes from
- ubuntu questing: sudo 1.9.17p2-1ubuntu1
- ubuntu questing: sudo 1.9.17p2-1ubuntu1.1 ยท ISC
- ubuntu resolute: sudo 1.9.17p2-1ubuntu3 ยท ISC
- ubuntu resolute: sudo 1.9.17p2-1ubuntu3.2 ยท ISC
- ubuntu stonking: sudo 1.9.17p2-7ubuntu5 ยท ISC
- ๐ the page's license: ISC
- Copyright (c) 1994-1996, 1998-2005, 2007-2025 Todd C. Miller <Todd.Miller@sudo.ws>
- ๐ girlified by anthropic/claude-haiku-5.5 on : a language model's rewrite of the original, under its terms