This question is about understanding the reason(s) behind a perceived inconsistency between documented and actual behavior when calling an executable through sudo. When the secure_path option is enabled (default on my system), the search path behave as expected. But when the option is disabled, something strange happens: an executable in /usr/local/bin can be reached without a fully qualified path name despite its location not being in the search path.

System Information

The following software is currently installed on my system:

## Yeah... still haven't migrated to Alma
[me@localhost ~]$ cat /etc/centos-release
CentOS Linux release 8.5.2111

[me@localhost ~]$ bash --version | head -1
GNU bash, version 4.4.20(1)-release (x86_64-redhat-linux-gnu)

[me@localhost ~]$ sudo --version
Sudo version 1.8.29
Sudoers policy plugin version 1.8.29
Sudoers file grammar version 46
Sudoers I/O plugin version 1.8.29

[me@localhost ~]$ ssh -V
OpenSSH_8.0p1, OpenSSL 1.1.1k  FIPS 25 Mar 2021

PAM does not set or update the value of the PATH variable on my system:

[me@localhost ~]$ grep --recursive 'pam_env\.so' /etc/pam.d
/etc/pam.d/fingerprint-auth:auth        required      pam_env.so
/etc/pam.d/smartcard-auth:auth        required      pam_env.so
/etc/pam.d/su:auth        required    pam_env.so
/etc/pam.d/password-auth:auth        required      pam_env.so
/etc/pam.d/system-auth:auth        required      pam_env.so

[me@localhost ~]$ sudo cat /etc/security/pam_env.conf /etc/environment | grep PATH
# be useful to be set: NNTPSERVER, LESS, PATH, PAGER, MANPAGER .....
#PATH       DEFAULT=${HOME}/bin:/usr/local/bin:/bin\

My /etc/sudoers file sets a secure_path value for all sudoers:

[me@localhost ~]$ sudo grep --recursive secure_path /etc/sudoers /etc/sudoers.d
/etc/sudoers:Defaults    secure_path = /sbin:/bin:/usr/sbin:/usr/bin

Bash defaults to the following PATH value:

[me@localhost ~]$ env --ignore-environment bash -c 'echo $PATH'

Finally, I am connected to the system through SSH, whose /etc/ssh/sshd_config file contains this line:

[me@localhost ~]$ sudo grep PATH /etc/sshd_config
# This sshd was compiled with PATH=/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin

Replication Procedure

To replicate, I first create a dummy script and install it in /usr/local/bin:

[me@localhost ~]$ cat > dummy <<EOF
echo 'Found!'

[me@localhost ~]$ sudo install --owner=root --group=root --mode=755 dummy /usr/local/bin

I verify that the search path works as expected when not using sudo:

## The /usr/local/bin location is part of my search path
[me@localhost ~]$ echo $PATH

## This is expected
[me@localhost ~]$ dummy

And using sudo, no surprise here either:

## Sudo's secure_path value
[me@localhost ~]$ sudo --user=other env | grep PATH

## This is expected
[me@localhost ~]$ sudo --user=other which dummy
which: no dummy in (/sbin:/bin:/usr/sbin:/usr/bin)

## This too, of course
[me@localhost ~]$ sudo --user=other dummy
sudo: dummy: command not found

## Indeed, a fully qualified path name is required
[me@localhost ~]$ sudo --user=other /usr/local/bin/dummy

Then, I disable secure_path (careful here! use of visudo is advised), either by commenting the line out in /etc/sudoers or by creating a file /etc/sudoers.d/local with the following lines:

# Disable secure_path if set
Defaults !secure_path

This should prevent sudo, when called with the --preserve-env switch, from overriding the PATH environment variable with the value of secure_path. And it works as expected.

However, when not using the --preserve-env switch, and not prompting a full login sequence with the --login switch (thereby not sourcing any of Bash's startup files), and with no assignment to PATH in any of PAM environment files, something strange happens:

## Not sure where this PATH value is from, neither from sudo's secure_path option
## (not set), PAM environment files (contain no assignment to PATH), Bash startup
## scripts (not sourced), nor Bash or sshd default PATH values (no match).
[me@localhost ~]$ sudo --user=other env | grep PATH

## Regardless, this is expected
[me@localhost ~]$ sudo --user=other which dummy
which: no dummy in (/usr/bin:/bin:/usr/sbin:/sbin)

## But wait! What?!?
[me@localhost ~]$ sudo --user=other dummy

So, how is it that which dummy complains of dummy not in search path while a straight call to dummy without path prefix finds it?

Relevant Documentation

Following are references to various bits of information that I found relevant while researching this question.

Sudo documentation says this about the secure_path option:

Path used for every command run from sudo. If you don't trust the people running sudo to have a sane PATH environment variable you may want to use this. Another use is if you want to have the “root path” be separate from the “user path”. Users in the group specified by the exempt_group option are not affected by secure_path. This option is not set by default.

As per pam_env documentation in man pages, only the /etc/environment, the /etc/security/pam_env.conf and ~/.pam_environment should be processed by the pam_env module by default, unless non-default file names are specified. This is not the case on my system and none of these files set or update the value of PATH.

Bash's man page says:

PATH The search path for commands. It is a colon-separated list of directories in which the shell looks for commands (see COMMAND EXECUTION below). A zero-length (null) directory name in the value of PATH indicates the current directory. A null directory name may appear as two adjacent colons, or as an initial or trailing colon. The default path is system-dependent, and is set by the administrator who installs bash. A common value is “/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin”.

This page of Bash documentation on gnu.org (https://www.gnu.org/software/bash/manual/html_node/Bash-Startup-Files.html) explains when the various startup files are sourced. In my demonstration above, none of these files should be sourced since I'm neither spawning new shells in --login or --interactive, nor calling sudo with the --login switch.

This ServerFault answer (https://serverfault.com/questions/833762/where-does-the-bash-path-on-centos-7-get-usr-local-bin-from#answer-838552) explains Bash's default values for the PATH variable. Even though it was answered for Bash on CentOS 7, the answer is still relevant to the Bash version packaged with CentOS 8. As per the accepted answer, the bash source config-top.h has this:

/* The default value of the PATH variable. */

/* The value for PATH when invoking `command -p'.  This is only used when
   the Posix.2 confstr () function, or CS_PATH define are not present. */
  • Some new information which I had not realized could be relevant: I'm connected to the system through SSH, whose configuration file says that "This sshd was compiled with PATH=/usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin". I'm not quite sure of what this implies, but there could be a connection, as it contains the /usr/local/bin location. Updating the question with this new information.
    – Rafa
    Jun 21 at 21:27
  • Yet another clue could be given by Bash's default (hard-coded) PATH value which, on the system under consideration, also contains the /usr/local/bin entry. Updating the question.
    – Rafa
    Jun 21 at 22:00


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.