为什么sudo会改变PATH?

296

这是没有sudo的PATH变量:

$ echo 'echo $PATH' | sh 
/opt/local/ruby/bin:/usr/bin:/bin

这是使用sudo的PATH变量:

$ echo 'echo $PATH' | sudo sh
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/X11R6/bin
据我所知,sudo 应该不会影响 PATH。但是为什么会出现这种情况呢?我该如何更改?(我的操作系统是 Ubuntu 8.04)。
更新:就我所知,作为 root 启动的脚本都没有修改过 PATH
man sudo中可以看到:
为防止命令欺骗,当在用户的 PATH 中搜索命令时(如果其中一个或两个路径包含在 PATH 中),sudo 会将“.”和“”(表示当前目录)放在最后检查。但是需要注意,实际的 PATH 环境变量不会被修改,并且原样传递给 sudo 执行的程序。

根目录中的.bashrc文件是否有设置PATH的内容?这假设你使用的是Linux系统,sh其实是bash。 - Greg Hewgill
1
请返回已翻译的文本:http://unix.stackexchange.com/questions/83191/how-to-make-sudo-preserve-pathchttp://unix.stackexchange.com/questions/8646/why-are-path-variables-different-when-running-via-sudo-and-suhttp://superuser.com/questions/98686/passing-path-through-sudo - Ciro Santilli OurBigBook.com
18个回答

252

这是sudo在很多发行版上的一个特性,看似有点烦人。

为了避免这个“问题”,我在我的~/.bashrc文件中做了以下操作来解决它:

alias sudo='sudo env PATH=$PATH'

请注意,上述内容适用于不会重置 $PATH 的命令。 然而,`su' 会重置其 $PATH,因此您必须使用 -p 来告诉它不要这样做。例如:

sudo su -p

52
这个“烦人的功能”可以防止你被木马攻击。我认为强制使用特定的$PATH是一种功能而非缺陷——它要求你写出完整路径才能运行不在$PATH中的程序。 - C. K. Young
35
是的,但这完全违反直觉。它很可能会欺骗好人比坏人更多。 - Brian Armstrong
38
不仅直觉上不合理,而且文档记录错误。阅读sudo的man页面并将其与Fedora进行比较后,我认为应该保留路径。实际上,“sudo -V”甚至显示“要保留的环境变量:PATH”。 - Jason R. Coombs
10
很恼人。就是这样。如果它可以通过sudo让你中木马,那么即使没有sudo也可能遭受同样的攻击。当然,这更加困难,但是如果你在普通用户下运行来自错误位置的代码,那么情况已经够糟了。 - gcb
8
不要创建sudo别名;参考@Jacob的答案关于Defaults env_reset。 - greg_1_anderson
显示剩余7条评论

126

如果有人遇到这个问题想要禁用所有用户的路径变量更改,可以按照以下步骤进行操作:

使用命令visudo访问您的sudoers文件。您应该在某个位置看到如下行:

Defaults env_reset

接下来,在其下一行添加以下内容:

Defaults !secure_path

默认情况下启用了secure_path。此选项指定在sudo时如何制定$PATH。感叹号禁用该功能。


6
另一种方式:Defaults env_keep = "PATH"翻译为中文:默认设置 env_keep = "PATH" 保留环境变量。 - gcb
1
默认情况下,“!secure_path” 在现代系统上运行良好;在一个旧的 ubuntu 8.04 系统上,“Defaults env_keep = "PATH"”也可以解决问题。 - greg_1_anderson
34
不需要禁用secure_path,您可以向其添加路径。例如,在我的情况下,我添加了以下行:"Defaults secure_path = /sbin:/bin:/usr/sbin:/usr/bin:/some/custom/directory",其中"some/custom/directory"是我需要让sudo使用的路径。请注意保持原意并尽可能简洁易懂。 - Hector Correa
1
@HectorCorrea 的解决方案在我看来是更好的方式。 - chakrit

33

PATH 是一个环境变量,因此默认情况下会被 sudo 重置。

您需要特殊权限才能执行此操作。

来自 man sudo

       -E 选项(保留环境)将覆盖 sudoers(5) 中的 env_reset 选项。只有匹配命令具有 SETENV 标记或在 sudoers(5) 中设置 setenv 选项时,该选项才可用。
       命令要设置的环境变量也可以以 VAR=value 的形式通过命令行传递,例如 LD_LIBRARY_PATH=/usr/local/pkg/lib。通过命令行传递的变量受到与普通环境变量相同的限制,但有一个重要的例外。如果在 sudoers 中设置了 setenv 选项,要运行的命令具有 SETENV 标记或匹配的命令是 ALL,则用户可以设置本来被禁止的变量。有关更多信息,请参见 sudoers(5)。

使用示例:

cat >> test.sh
env | grep "MYEXAMPLE" ;
^D

sh test.sh 
MYEXAMPLE=1 sh test.sh
# MYEXAMPLE=1
MYEXAMPLE=1 sudo sh test.sh 
MYEXAMPLE=1 sudo MYEXAMPLE=2 sh test.sh 
# MYEXAMPLE=2

更新

man 5 sudoers:
env_reset 如果设置,sudo将重置环境,仅包含LOGNAME、SHELL、USER、USERNAME和SUDO_*变量。然后添加调用者环境中与env_keep和env_check列表匹配的任何变量。当以root身份使用-V选项运行sudo时,将显示env_keep和env_check列表的默认内容。如果sudo是使用SECURE_PATH选项编译的,则其值将用于PATH环境变量。此标志默认打开。

因此,可能需要检查是否已编译。

Gentoo默认启用此功能。

# ( From the build Script )
....
ROOTPATH=$(cleanpath /bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:/opt/bin${ROOTPATH:+:${ROOTPATH}})
....
econf --with-secure-path="${ROOTPATH}" 

18
看起来这个bug已经存在了很长时间!以下是一些有用的bug参考资料(您可能希望订阅/投票,提示,提示...):

Debian bug #85123(“sudo:SECURE_PATH仍然无法被覆盖”)(来自2001年!)

似乎Bug#20996仍然存在于此版本的sudo中。更改日志说它可以在运行时被覆盖,但我还没有发现如何做到。

他们提到在sudoers文件中放置类似于这样的内容:

Defaults secure_path="/bin:/usr/bin:/usr/local/bin"

但至少在Ubuntu 8.10中,当我这样做时,会出现以下错误:

visudo: unknown defaults entry `secure_path' referenced near line 10

Ubuntu bug #50797 ("使用--with-secure-path编译的sudo存在问题")

Worse still, as far as I can tell, it is impossible to respecify secure_path in the sudoers file. So if, for example, you want to offer your users easy access to something under /opt, you must recompile sudo.


Yes. There needs to be a way to override this "feature" without having to recompile. Nothing worse then security bigots telling you what's best for your environment and then not giving you a way to turn it off.


This is really annoying. It might be wise to keep current behavior by default for security reasons, but there should be a way of overriding it other than recompiling from source code! Many people ARE in need of PATH inheritance. I wonder why no maintainers look into it, which seems easy to come up with an acceptable solution.


I worked around it like this:

mv /usr/bin/sudo /usr/bin/sudo.orig

then create a file /usr/bin/sudo containing the following:

#!/bin/bash
/usr/bin/sudo.orig env PATH=$PATH "$@"

then your regular sudo works just like the non secure-path sudo


Ubuntu bug #192651 ("sudo path is always reset")

鉴于这个 Bug 的副本最初是在 2006 年 7 月提交的,我不清楚无用的 env_keep 已经运行了多久。无论强制用户使用上述技巧的优点是什么,sudo 和 sudoers 的手册应反映修改 PATH 的选项事实上是多余的。

将文档修改以反映实际执行非破坏性而且非常有帮助。


Ubuntu bug #226595 ("impossible to retain/specify PATH")

我需要能够在 PATH 中运行其他非标准二进制文件夹的 sudo。当我已经将要求添加到 /etc/environment,并在 sudo 下运行时出现缺少命令的错误时,我感到非常惊讶.....

我尝试以下方法来解决此问题却没有成功:

  1. 使用“sudo -E”选项-没有起作用。我的现有 PATH 仍然被 sudo 重置

  2. 将“Defaults env_reset”更改为“Defaults !env_reset”在 /etc/sudoers 中--也不起作用(即使与 sudo -E 结合使用)

  3. 取消注释 /etc/sudoers 中的 env_reset(例如,“#Defaults env_reset”)--也不起作用。

  4. 将“Defaults env_keep += "PATH"”添加到 /etc/sudoers -- 也不起作用。

显然,尽管手册如此说明,但 sudo 在 PATH 方面完全是硬编码的,不允许保留用户自己设置的 PATH。这非常令人恼火,因为我不能使用 sudo 以 root 权限运行非默认软件。


15
这对我来说似乎有效。
sudo -i 

该命令使用非sudo用户的PATH路径


1
'sudo -i' 在Ubuntu上无效(我已经检查过Ubuntu 14.04.3 LTS)。$PATH仍然会被sudo修改。 - Marcin Raczyński

11
我认为让sudo重置PATH是有益的:否则,攻击者可以通过入侵您的用户帐户,在用户的PATH上放置带后门的各种工具的版本,当使用sudo时这些工具将被执行。

(当然,让sudo重置PATH并不是解决这些问题的完整方案,但它会有所帮助。)

当您使用以下命令时,情况确实是这样:

Defaults env_reset

如何在不使用exempt_groupenv_keep的情况下编辑/etc/sudoers文件。

这也很方便,因为您可以将仅对root有用的目录(例如/sbin/usr/sbin)添加到sudo路径中,而无需将它们添加到用户路径中。要指定sudo使用的路径:

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

1
一个攻击者如果获得了sudoer账户的访问权限,可能会做更糟糕的事情。 - user508546
一个不错的建议。在Ubuntu 12.04服务器上,类似的设置是默认的。 - Tsutomu

7

现在可以使用来自karmic存储库的sudo。以下是我的配置细节:

root@sphinx:~# cat /etc/sudoers | grep -v -e '^$' -e '^#'
Defaults    env_reset
Defaults    secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/opt/grub-1.96/sbin:/opt/grub-1.96/bin"
root    ALL=(ALL) ALL
%admin ALL=(ALL) ALL
root@sphinx:~# cat /etc/apt/sources.list
deb http://au.archive.ubuntu.com/ubuntu/ jaunty main restricted universe
deb-src http://au.archive.ubuntu.com/ubuntu/ jaunty main restricted universe

deb http://au.archive.ubuntu.com/ubuntu/ jaunty-updates main restricted universe
deb-src http://au.archive.ubuntu.com/ubuntu/ jaunty-updates main restricted universe

deb http://security.ubuntu.com/ubuntu jaunty-security main restricted universe
deb-src http://security.ubuntu.com/ubuntu jaunty-security main restricted universe

deb http://au.archive.ubuntu.com/ubuntu/ karmic main restricted universe
deb-src http://au.archive.ubuntu.com/ubuntu/ karmic main restricted universe

deb http://au.archive.ubuntu.com/ubuntu/ karmic-updates main restricted universe
deb-src http://au.archive.ubuntu.com/ubuntu/ karmic-updates main restricted universe

deb http://security.ubuntu.com/ubuntu karmic-security main restricted universe
deb-src http://security.ubuntu.com/ubuntu karmic-security main restricted universe
root@sphinx:~# 

root@sphinx:~# cat /etc/apt/preferences 
Package: sudo
Pin: release a=karmic-security
Pin-Priority: 990

Package: sudo
Pin: release a=karmic-updates
Pin-Priority: 960

Package: sudo
Pin: release a=karmic
Pin-Priority: 930

Package: *
Pin: release a=jaunty-security
Pin-Priority: 900

Package: *
Pin: release a=jaunty-updates
Pin-Priority: 700

Package: *
Pin: release a=jaunty
Pin-Priority: 500

Package: *
Pin: release a=karmic-security
Pin-Priority: 450

Package: *
Pin: release a=karmic-updates
Pin-Priority: 250

Package: *
Pin: release a=karmic
Pin-Priority: 50
root@sphinx:~# apt-cache policy sudo
sudo:
  Installed: 1.7.0-1ubuntu2
  Candidate: 1.7.0-1ubuntu2
  Package pin: 1.7.0-1ubuntu2
  Version table:
 *** 1.7.0-1ubuntu2 930
         50 http://au.archive.ubuntu.com karmic/main Packages
        100 /var/lib/dpkg/status
     1.6.9p17-1ubuntu3 930
        500 http://au.archive.ubuntu.com jaunty/main Packages
root@sphinx:~# echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/opt/grub-1.96/sbin:/opt/grub-1.96/bin
root@sphinx:~# exit
exit
abolte@sphinx:~$ echo $PATH
/home/abolte/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/opt/grub-1.96/sbin:/opt/grub-1.96/bin:/opt/chromium-17593:/opt/grub-1.96/sbin:/opt/grub-1.96/bin:/opt/xpra-0.0.6/bin
abolte@sphinx:~$ 

不需要使用hack技巧就能解决这个问题,真是太棒了。


4
或许你可以考虑改写一下,指导那些使用全新安装系统的人如何更新他们的配置文件来解决这个具体问题。 - Jason R. Coombs

4
# cat .bash_profile | grep PATH
PATH=$HOME/bin:/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin
export PATH

# cat /etc/sudoers | grep Defaults
Defaults    requiretty
Defaults    env_reset
Defaults    env_keep = "SOME_PARAM1 SOME_PARAM2 ... PATH"

4

只需在/etc/sudoers中注释掉“Defaults env_reset”即可。


3

只需编辑/etc/sudoers中的env_keep

它看起来像这样:

Defaults env_keep = "LANG LC_ADDRESS LC_CTYPE LC_COLLATE LC_IDENTIFICATION LC_MEASURE MENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER LC_TELEPHONE LC_TIME LC_ALL L ANGUAGE LINGUAS XDG_SESSION_COOKIE"

只需在末尾添加PATH,因此更改后它应如下所示:

Defaults env_keep = "LANG LC_ADDRESS LC_CTYPE LC_COLLATE LC_IDENTIFICATION LC_MEASURE MENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER LC_TELEPHONE LC_TIME LC_ALL L ANGUAGE LINGUAS XDG_SESSION_COOKIE PATH"

关闭终端,然后重新打开。


等等,PATH需要2吗?为什么PATH需要2 - CMCDragonkai
@CMCDragonkai 这段文字最初是以粗体格式(在Markdown中)编写的,但有人(Stack Overflow不允许我点名道姓)将其标记为代码。 - 1j01

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接