60

On my fedora VM, when running with my user account I have /usr/local/bin in my path:

[justin@justin-fedora12 ~]$ env | grep PATH
 PATH=/usr/kerberos/sbin:/usr/kerberos/bin:/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin:/home/justin/bin

And likewise when running su:

[justin@justin-fedora12 ~]$ su -
Password: 
[root@justin-fedora12 justin]# env | grep PATH
PATH=/usr/kerberos/sbin:/usr/kerberos/bin:/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin:/home/justin/bin

However, when running via sudo, this directory is not in the path:

[root@justin-fedora12 justin]# exit
[justin@justin-fedora12 ~]$ sudo bash
[root@justin-fedora12 ~]# env | grep PATH
PATH=/usr/kerberos/sbin:/usr/kerberos/bin:/sbin:/bin:/usr/sbin:/usr/bin

Why would the path be different when running via sudo?

kenorb
  • 20,988
Justin Ethier
  • 16,806

8 Answers8

58

Take a look at /etc/sudoers. The default file in Fedora (as well as in RHEL, and also Ubuntu and similar) includes this line:

Defaults    secure_path = /sbin:/bin:/usr/sbin:/usr/bin

Which ensures that your path is clean when running binaries under sudo. This helps protect against some of the concerns noted in this question. It's also convenient if you don't have /sbin and /usr/sbin in your own path.

mattdm
  • 40,245
  • Ah, I see that in my file. So, not that I want to, but if I added /usr/local/bin to this directive then I would see it in my path when running via sudo, right? – Justin Ethier Mar 05 '11 at 16:25
  • I just tried it and now I see /usr/local/bin. Thank you so much for explaining this! – Justin Ethier Mar 05 '11 at 16:27
  • What about adding your users's path for scripts and binaries, so you don't have to write the absolute path when you must sudo for example a script in your ~/bin (or whatever path you use)? I just made the change - it works, only thought there might be a flip side to it? – Emanuel Berg Oct 15 '12 at 02:01
  • @mattdm Yes, Ubuntu as well, as I came across that issue in Ubuntu Vivid when playing with VM. The same for Debian. – kenorb Dec 24 '15 at 17:22
  • Very good to know; thanks! As others have mentioned you can add env "PATH=$PATH" to the command to temporarily bring in the current user's path for one command. For more permanent use of a command with sudo, rather than editing /etc/sudoers it might be more surgical to simply do a sudo ln -s /usr/local/bin/cmd cmd inside /usr/sbin to link the command inside a directory already on the sudoers path. Then you can always use the command with sudo without pulling in all the other commands in /usr/local/bin. – Garret Wilson May 16 '20 at 15:53
11

The command su - will execute the root users profile and take on that user's environment including path etc. sudo does not do that.

If you'd like sudo to behave like su - then use the option sudo -i [command which will execute the user's profile

If you'd like su - to behave like sudo then don't use the hyphen - just use su [command]

9

You can check why (it's different) by running sudo sudo -V.

For example on Linux run:

$ sudo sudo -V | grep PATH
Value to override user's $PATH with: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

Note: On macOS/BSD, just run: sudo sudo -V.

The above list is restricted due to default security policy plugin in some Linux distributions.


This is further explained in man sudoers:

If the secure_path option is set, its value will be used for the PATH environment variable.

secure_path - 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.

If that's the case, you can change that by running sudo visudo and editing the configuration file and modifying your secure_path (adding extra path separated by :) or add your user into exempt_group (so you won't be affected by secure_path options).

Or in order to pass user's PATH temporary, you can run:

sudo env PATH="$PATH" my_command

and you can check that by:

sudo env PATH="$PATH" env | grep ^PATH

See also: How to make sudo preserve $PATH?


Other reason why the environment could be different for sudo, is because you could have env_reset option enabled in your sudoers file. This causes commands to be executed with a new, minimal environment.

So you can use env_keep option (not recommended for security reasons) to preserve your user's environment variables:

Defaults        env_reset
Defaults        env_keep += "PATH PYTHONPATH"
kenorb
  • 20,988
2

Q: "Why would the path be different when running via sudo?"

Perhaps the best explanation is found in the Command environment section of man 5 sudoers:

Since environment variables can influence program behavior, sudoers provides a means to restrict which variables from the user's environment are inherited by the command to be run. There are two distinct ways sudoers can deal with environment variables.

This section goes on to explain how to modify the restrictions on environment variables that are passed - or blocked - by sudo. These default restrictions will vary by OS and distro. Consequently, your first stop should be sudo -V to learn the statusof your environment variables. Run this command from the root prompt; for example:

$ sudo -i
# sudo -V

... a long list ...

This output shows each and every environment variable for your system, grouped by its status: removed, preserved, check.

If your PATH environment variable is in the removed list, you may change that with this line:

Defaults env_keep += "path PATH"

You may add this line to your sudoers file configuration by editing with:

$ sudo visudo 

Alternatively - and a bit cleaner to my way of thinking, is to add it as a "code snippet" to /etc/sudoers.d - if it exists. If it doesn't exist, you may be able to create it by adding the line #includedir /etc/sudoers.d to the tail of your sudoers file.

This is basically the same process as editing sudoers; the "code snippets" may be created and edited as follows:

$ sudo visudo -f /etc/sudoers.d/10_mypathsnippet

You can verify the change is set by running sudo -V again (from root prompt!); in this case your PATH variable should now be in the preserved list.

Seamus
  • 2,925
2

In most linuxes, you install programs via the package management, and get updates in a regular way. If you install something circumventing the package management it will be installed in /usr/local/bin (for example, or .../sbin, or /opt) and not get regular updates.

I guess therefore the programs aren't considered to be that secure, and not put into roots PATH by default.

user unknown
  • 10,482
  • +1 - Cool, I was wondering why it was not in the path, and that makes sense. For what it's worth, I was building node.js from scratch to play around with it, so it makes sense why it would have been put there, and why sudo would exclude this directory by default. – Justin Ethier Mar 05 '11 at 16:29
  • @Justin Ethier: off topic, but see https://bugzilla.redhat.com/show_bug.cgi?id=634911 – mattdm Mar 06 '11 at 18:53
1

I've just tried this out for myself and I didn't see the behaviour you were seeing - my path remained the same, so maybe your sudo configuration is different. If you check man sudoers you'll see there is an option called secure_path which resets PATH - it sounds like this option might have been enabled.

1

Because when you use sudo bash, bash doesn't not act as a login shell. Try again with sudo bash -l and you should see the same result as su -.

If that is correct, then the difference in PATH lies in the configuration files: /etc/profile, ~/.bash_profile, ~/.bash_login, ~/.profile are executed (in that order) for a login shell, while ~/.bashrc is executed for a non-login interactive shell.

phunehehe
  • 20,240
0

Old question, I know, but I stumbled in here just now because I was investigating this exact problem.

For some reason /usr/local/bin was only in the PATH when becoming root via sudo su -. When using sudo -i it wasn't there. Of course I now know I can add it to /etc/sudoers, but that still didn't explain why it is already there after su -. Where did this part of PATH come from?

After a lot of grepping and searching I found the answer:

The default path containing '/usr/local/bin' is actually hardcoded in su(1).

So no pam configuration, profile, bashrc or anything was responsible for selectively adding this element. It was always already there when su took over. And since sudo does not invoke su at all but uses it's own configuration, it was missing after sudo -i

I found this to be true on RHEL6 and RHEL7. I didn't check any other version or distribution.

Oscar
  • 19
  • Don't ask me how I verified this.. Okay, if you insist: I hex-edited a copy of the su binary, changed /usr/local/bin into something else and invoked the copy. My PATH now contained the modified string...

    Good kids and non-lazy sysadmins of course just download the source and check in there. ;-)

    – Oscar May 22 '19 at 15:43
  • Thanks, I was wondering how it was working. – emmdee Mar 10 '20 at 20:20