sudo在19.10版本之后如何处理$HOME的差异?

在Ubuntu 19.10 Eoan Ermine之前的版本中,当我使用sudo命令运行一个命令时,该命令会在$HOME环境变量中接收到我的主目录。这是我长期以来预期的行为,并且已经警告其他人。如果我想让sudo重置$HOME环境变量,使其指向目标用户的主目录而不是我的主目录,则必须传递-H选项(或-i,但它会做更多的事情)。
ek@Kip:~$ lsb_release -d
Description:    Ubuntu 18.04.3 LTS
ek@Kip:~$ sudo printenv HOME  # Shows ek's home, not root's.
/home/ek
ek@Kip:~$ sudo -u as printenv HOME  # Shows ek's home, not as's.
/home/ek
ek@Kip:~$ sudo -H printenv HOME  # Shows root's home.
/root
ek@Kip:~$ sudo -Hu as printenv HOME  # Shows as's home.
/home/as

当我第一次升级到Ubuntu 19.10时,我惊讶地发现无论如何sudo似乎都会重置$HOME现在19.10已经发布并且我已经安装了更新,我继续观察这个问题 - 在新安装的系统和我升级到19.10的系统上都是如此。
ek@Cord:~$ lsb_release -d
Description:    Ubuntu 19.10
ek@Cord:~$ sudo printenv HOME  # Shows root's home, even without -H or -i.
/root
ek@Cord:~$ sudo -u as printenv HOME  # Shows as's home, even without -H or -i.
/home/as
ek@Cord:~$ sudo -H printenv HOME  # Also shows root's home.
/root
ek@Cord:~$ sudo -Hu as printenv HOME  # Also shows as's home.
/home/as

我认为这可能是由于更新的配置文件引起的。但是我检查了一下,我的19.10版本的/etc/sudoers文件中没有任何Defaults行中出现always_set_home为什么在19.10版本中sudo$HOME的处理方式不同,并且为什么会做出这个改变?这是否意味着在以前需要使用sudo -H的情况下,现在可以安全地使用普通的sudo命令了?

如果你和我一样,想要保留所有不卫生的行为:sudo bash -c 'HOME=/home/[用户名] ; [命令]' - user246503
我认为值得一提的是: https://stackoverflow.com/questions/20504662/how-to-get-home-directory-of-different-user-in-bash-script 你可以创建类似于 SUDO_HOME=\eval echo "~$SUDO_USER"`` 的东西。 - Wagner Patriota
1个回答

多年来,Ubuntu一直提供一个修补过的sudo版本,默认情况下保留$HOME。除了Ubuntu及其派生版之外,几乎没有其他操作系统(也许没有其他操作系统)这样做。经过深思熟虑,我们决定这样做带来的问题比解决的问题还要多。从Ubuntu 19.10开始,sudo将不再保留$HOME作为少数环境变量之一。
就变化的具体内容和对用户的影响而言,关键点如下:
  • 自 Ubuntu 19.10 版本开始,sudo 命令 的作用与之前版本中的 sudo -H 命令 相同。 在以前的版本中,建议使用 sudo -H 的情况下,可以采用该方式,包括 以 root 或其他用户身份运行图形界面应用程序。在任何情况下以 root 权限运行图形程序仍然备受争议。但是在 19.10 版本中,你可以通过 sudo gedit 实现与 sudo -H gedit 相同的效果。在 19.10 版本中,类似 sudo gedit 的命令不会再在您的主目录中创建 繁琐的文件所有权问题
  • 即使是从早期版本升级到 19.10 版本的系统也适用于此。 即使升级过程中没有更改 /etc/sudoers 文件,这种变化是在 sudo 程序本身的源代码中进行的,而不是在其默认配置文件中进行的(可以在 sudoers 文件中覆盖此设置,但您可能已经知道是否这样做)。
  • 这不会适用于 19.10 版本之前的发行版。 在 19.10 版本之前,默认情况下,sudo 保留 $HOME,并且将来的 sudo 更新也不会更改这一点。例如,即使在未来的小版本更新中,18.04 LTS 仍然会保留旧的行为。
  • sudo -H 命令 仍然可以正常工作。 如果你习惯使用该方式,没有问题。在 19.10 系统上,你只是不再需要这样做。
  • 大多数使用 sudo 运行的命令不会有任何不同的行为。 在绝大多数情况下,你本来不需要使用 -H,但你确实可以使用。一些用户 依赖或偏好保留 $HOME 的旧行为。但那常常产生意料之外的效果。因此,如果你依赖这个功能,你可能仍然不希望在 sudoers 文件中覆盖此更改。
另请参阅WinEunuuchs2Unix的回答以了解为什么用户不应该使用普通sudo来启动图形应用程序。
为什么要改变
正如更新日志所述(在“sudo (1.8.27-1ubuntu2) eoan”下):

这将恢复sudo对$HOME的处理方式,与其他所有人一样

“其他所有人”指的是上游sudo项目(托管在此处),以及似乎包含sudo所有其他操作系统,除了那些派生自Ubuntu的操作系统。
虽然还有更多内容。这也被认为是修复了一个安全漏洞,如Ubuntu补丁添加HOME到env_keep使自定义命令默认易受攻击所述。历史在这个评论中Steve Langasek(你可能听说过他)简洁地总结了一下,我全文引用如下:
这个改变最初是为了回应bug #760140而引入的。 sudo上游的改变从未伴随着CVE,并且行为改变从未应用于之前的Ubuntu版本,所以当时看起来并不涉及安全问题。 我不反对将其更改为Simon描述的方式,但在这里我会听从安全团队的意见。 维护上游sudo项目的Todd C. Miller(非常积极,并且已经有很多年了)也被要求就此问题提供意见。他解释了sudo重置(即不保留)$HOME的原因。
周四,2019年5月16日07:48:40 -0400,丹·斯特里特曼写道:
我已经抄送了sudo-users,所以可以总结一下向上游sudo列表提出的问题: 上游sudo默认情况下是否有可能将HOME添加到env_keep中?
非常不可能。在sudo 1.7.4之前,HOME和MAIL环境变量默认情况下会被保留在环境中。这可能导致程序使用原始用户的主目录中的配置文件,这具有安全隐患,因此在1.7.4中更改了默认设置。
在过去,sudo几乎只是更改uid。如今,sudo尝试在一个与登录该用户时非常相似的环境中运行命令。这被证明更安全,因为它更符合其他程序的假设。
我们之所以问这个问题,是因为Ubuntu带有一个补丁,将HOME添加到env_keep中,与默认的上游或其他Linux/Unix不同。我们正在考虑删除该补丁,以匹配上游的默认设置,即不包括HOME在env_keep中。
我支持这个想法。我认为重置HOME是更安全的默认设置。
- 托德
(我已经修改了格式,使得原始信息在这个媒介中能够正确显示。)
截至19.10版本,Ubuntu中的下游sudo的行为与上游sudo(以及其他操作系统中的sudo,包括Debian)多年来一直保持一致。有关此更改的历史和前期发展的详细信息,包括大量进一步阅读的链接,请参阅下面的“sudo$HOME:过去20年”部分。

什么?也就是为什么sudo$HOME的处理有时候很重要

你运行的需要知道你主目录位置的程序通常会检查$HOME环境变量的值。一个重要的情况是当一个程序尝试在运行它的用户的主目录中存储和访问配置文件时。程序通常根据$HOME的值进行操作。有时候,当你以替代用户身份运行程序时——例如,作为root账户——让该程序使用你的设置是很吸引人的。但这也可能变得混乱,有两种方式:
  1. 通常,当你以另一个用户身份运行命令时,你希望它的工作方式大致与该用户直接运行它的方式相同。但是,如果某个命令的行为可以被你运行该命令的事实大幅改变(这种情况发生在其行为受到从名为 $HOME 的目录中读取的数据定制的命令上),那么这个目标就无法实现。

    当你被允许以包括 root 用户在内的任何用户身份执行任何操作时(在 Ubuntu 中通过属于 sudo 组而获得),问题主要是意外。然而,如果你是一个有限用户,并且被允许仅使用 sudo 运行特定的命令,那么能够操纵这些命令所做的事情将带来严重的安全隐患。导致 19.10 更改的错误报告 正是出于针对该问题的特殊动机(其中包括 一个引人注目的实例)。

  2. 如果你以另一个用户的身份运行命令,并且该命令在名为 $HOME 的目录中更改其配置,那么会尝试将更改写入该位置的文件。当替代用户不是 root 时,比如 sudo -u 用户名 命令,这通常会失败并产生错误消息,这可能有点烦人但并不严重。

    但是,在替代用户为 root 的常见情况下,比如 sudo 命令,这将成功执行,但如果创建了任何新文件,它们将属于 root,并且你的用户帐户对其自己的所有配置文件将不再具有完全访问权限。可以通过 chown 命令将文件的所有权更改回来,因此该问题在图形应用程序的情况下最严重,其复杂性可能导致涉及更多位置和更多文件(并且已经 有报告说有时会导致登录困难,尽管 通常问题不会那么严重)。

发生了什么变化,为什么所有19.10系统都有这个变化

sudo本身在代码中硬编码了一个短的环境变量白名单,默认情况下不会被重置。在Ubuntu 19.10之前的版本中,一个特定于Ubuntu的补丁$HOME添加到这个白名单中。它被编译成sudo使用的二进制文件,因此升级到没有该补丁的sudo版本将从白名单中移除它。

该补丁在19.10中被移除。因此,即使没有修改与sudo相关的配置文件,升级到19.10或更高版本也会始终应用这个变化。

仍然可以配置任何版本的sudo以保留$HOME(见下文)。在非常罕见的情况下,如果您在19.10之前进行了此配置,并且在升级过程中保持了该配置,$HOME仍将被保留。但是您可能会记得曾经做过这种奇怪的事情。

如果您升级到19.10(或更高版本)时出现失败,并且系统只部分升级,使用的是19.10之前的sudo版本,则该系统上的sudo默认仍保留$HOME。这几乎是唯一的情况,在19.10(或更高版本)中sudo会保留$HOME而您不知道的情况下--尽管在发布升级过程中,您仍会收到无法升级所有软件包的通知。

在Ubuntu 19.10中查看sudo源代码中补丁已被删除的最简单方法是比较https://git.launchpad.net/ubuntu/+source/sudo/tree/debian/patches?h=ubuntu/disco-securityhttps://git.launchpad.net/ubuntu/+source/sudo/tree/debian/patches?h=ubuntu/eoan

然而,如果您不确定您的系统如何配置,可以运行sudo printenv HOME来查找。

重置Ubuntu 19.04及更早版本中的$HOME

尽管Ubuntu 19.04及之前版本中对sudo的更新可能包括对文档的更改以解释情况,但这些版本中sudo$HOME的处理方式不会被任何更新改变,也不会被改变。大多数用户不希望在已经正常使用的系统上手动更改sudo的工作方式。然而,如果您愿意,即使不升级系统,您也可以让sudo在这些系统上重置$HOME每次运行sudo,您可以使用-H/--set-home选项来重置$HOME
sudo -H <em>command</em>

或者你可以使用-i。这不仅会重置$HOMEsudo -i 命令的行为就像你以root用户登录,运行了命令,然后退出;sudo -i单独使用的话,就相当于你以root用户登录并进入一个交互式的root shell。这与sudo -s不同,sudo -s启动的是一个非登录的交互式shell。在Ubuntu 19.10之前,sudo -s会保留$HOME;也就是说,无论版本如何,-s选项都不会影响$HOME的处理方式。同时使用-H-s是有意义的。你还可以使用任何-H-i-s,以及-u 用户来成为用户而不是root。最后,通过图形界面的前端gksugksudo使用sudo(在16.04 LTS中仍可用,尽管您可能需要安装gksu软件包)会重置$HOME

如果你想重新配置sudo,以便它始终重置$HOME,你可以在sudoers文件中启用always_set_home选项。
Defaults    always_set_home

您可以将该行添加到/etc/sudoers或者更好地添加到/etc/sudoers.d/中的新文件中。无论哪种方式,您都应该使用visudo来编辑文件,以便获得语法检查的好处。(任何sudoers文件中的语法错误都会导致sudo完全拒绝工作,这很麻烦,但它可以修复。)
例如,在我一些19.10之前的系统上,我通过运行以下命令创建和编辑/etc/sudoers.d/always_set_home文件:
sudo visudo -f /etc/sudoers.d/<em>always_set_home</em>

在文件中,我写了上面的Defaults行。文件名不需要是always_set_home——它可以是任何你喜欢的,只要它不包含.~字符。当然,在Defaults行上的单词需要确切地是always_set_home
(将新文件放在/etc/sudoers.d/而不是修改现有的/etc/sudoers文件的一个原因是,如果将来的更新确实更改了默认的/etc/sudoers文件,你可以接受新文件而不会丢失你的自定义设置。另一个原因是,这样立即清楚你已经更改了配置,并且可以找到你的更改位置。)
如果你这样做,并且以后希望运行一个保留$HOME的个别sudo命令,你可以像在19.10中那样进行操作(见下文)。
在Ubuntu 19.10及更高版本中保留$HOME 对于一个原因,sudo 对待 $HOME 的方式已经发生了改变。(请参考上面的章节以及下面的详细历史部分。)但是,如果你真的想让 sudo 在19.10及之后版本中继续保留 $HOME,你可以在 sudoers 文件中配置该行为。 无论何时运行 sudo,你可以使用 --preserve-env=HOME 来告诉它保留 $HOME
sudo --preserve-env=HOME <em>command</em>

这是在sudo手册中被记录为--preserve-env=list的形式。也可以使用没有列表操作数的--preserve-env,它与-E相同;它会保留所有环境变量。但很少有好的理由这样做,特别是如果你的目标只是保留$HOME。如果你不喜欢输入--preserve-env=HOME,你可以定义一个shell别名或shell函数,或者编写一个脚本,让你运行一个更短的命令来完成它。更好的方法是很少保留$HOME(请参阅下面关于替代方法的部分)。
更一般地说,您可以使用sudo命令来保留任何特定的环境变量varname,方法是使用--preserve-env=varname。(您还可能看到在sudo运行的命令中通过显式设置来有效保留$HOME的代码,例如sudo HOME="$HOME" command。这也是有效的。这与HOME="$HOME" sudo command非常不同,后者不会阻止sudo重置$HOME。)

或者,如果您真的希望sudo始终保留$HOME,您可以通过将$HOME添加到sudoers文件中的env_keep中来实现:

Defaults    env_keep += "HOME"

这可以放在/etc/sudoers或者/etc/sudoers.d/中的一个文件里。虽然我强调我不建议这样做,但如果你决定这样做,我建议创建和编辑/etc/sudoers.d/keep-home(文件名随意,只要名称不包含.~)通过运行:
sudo visudo -f /etc/sudoers.d/<em>keep-home</em>

然后你可以将那行Defaults放入文件中。
之所以使用+=而不仅仅是=,是因为在sudo本身中有一些其他的环境变量是硬编码的,并且默认情况下会被保留,而且你可能希望保留它们。如果你使用=,那么只有在文件中明确列出的环境变量才会被保留。在这种情况下,只有$HOME会被保留。关于sudoers文件的语法的更多信息,请参阅sudoers(5)
至于为什么我建议在/etc/sudoers.d/中创建一个文件而不是编辑/etc/sudoers,以及为什么无论如何都应该使用visudo,请参阅上面"在Ubuntu 19.04及更早版本中重置$HOME"部分中的我的评论。
保留$HOME的替代方法
大多数情况下,使用sudo时,保留$HOME的最佳替代方法是什么都不做。大多数sudo命令具有相同的效果(有些甚至更好),而无需保留$HOME。然而,我知道有两种常见的用例需要保留$HOME使用文本编辑器配置和/或插件来编辑由root或其他用户拥有的文件。 sudoedit或等效的sudo -e是这方面的理想选择。它将编辑器作为运行,您编辑一个临时副本文件,并在退出编辑器时更新该文件。由于编辑器以您的身份运行,它会自动使用您的配置和插件,而且不会出现权限被拒绝错误或使您的主目录中的文件无法访问的风险。要编辑file
sudoedit <em>file</em>

使用editor而不是默认编辑器来编辑file的方法如下:
SUDO_EDITOR=<strong>editor</strong> sudoedit <em>file</em>

例如,SUDO_EDITOR=vim sudoedit /etc/apt/sources.list使用vim编辑/etc/apt/sources.list
为了决定使用哪个编辑器,sudoedit会查看环境变量$SUDO_EDITOR;如果未设置,则会查看$VISUAL;如果未设置,则会查看$EDITOR;如果未设置,则会尝试从硬编码列表中选择编辑器命令,实际上在Ubuntu中默认使用editor。通常情况下,它会解析为/usr/bin/editor,这是一个符号链接。如果要系统范围内更改默认编辑器,可以通过运行sudo update-alternatives --config editor来更改/usr/bin/editor所指向的内容。您也可以设置这三个环境变量之一,这是一种仅为一个用户更改sudoedit编辑方式的好方法。

让只能以root身份运行的程序使用特定的配置文件。如果一个以root身份运行的程序在$HOME中寻找其配置文件,那么你可以将该配置文件放在(或移动到)root的主目录/root中。

当你不知道所使用的发行版时重置$HOME

有时候你可能会编写一个命令,但不知道它将在哪个Ubuntu版本(或除了Ubuntu之外的其他操作系统)上运行。例如,你可能正在编写一个将在多台机器上运行的脚本。

sudo继续接受-H选项,并具有相同的效果。从19.10开始,sudo没有-H也会执行相同的操作(除非你已经进行了其他配置),就像sudo -H一样。

为了编写可移植的重置$HOMEsudo命令,你可以继续使用:

sudo -H command

(同样地,如果您的脚本中的所有操作都需要以root身份执行,最好不要在脚本中使用sudo命令,而是使用sudo以root身份运行脚本。) sudo$HOME:过去20年
在世纪之交时,上游的sudo项目引入了env_reset选项,该选项使sudo重置大多数环境变量。除非在sudoers文件中明确禁用,否则此选项将被启用。(可以显式启用它,Debian和Ubuntu的/etc/sudoers文件中的Defaults env_reset就是这样做的,但实际上并不必要。)在sudo没有env_reset之前,所有环境变量都保持不变。有了env_reset,只有少数几个变量被保留下来,其中包括$HOME

2010年7月, $HOME 被从那个小的白名单中移除,因此不再默认保留。

2010年9月, 在Debian的sudo软件包中提交了一个关于这一变更的错误报告。据我所知,Debian开发人员对于这一变更本身没有争议或反对意见。(但请参见下文。)

2011年2月, 这一变更已经从Debian进一步传播到Ubuntu,并且Ubuntu开发人员讨论了是否希望保留这一变更。

2011年4月, 这个变化被报告为一个错误,参考了那次讨论。(在此之前,这个变化导致的一些行为也被报告为一个错误。)至少在当时,这被认为是一个退步(“Debian维护者已经尝试过修复一次,但似乎修复不完全”)。我没有找到任何关于这个Debian错误报告,但这并不意味着没有;而且,可能会有变化发生而没有报告。不过,我怀疑这可能是对那个文档错误的错误引用。

第二天, Ubuntu的开发版本更新了一个下游的、仅适用于Ubuntu的补丁,将$HOME重新添加到默认情况下由sudo保留的环境变量列表中。我相信这是为了能够在Ubuntu 11.04发布时快速完成的。其效果是,11.04版的sudo与之前的Ubuntu版本一样,默认情况下保留了$HOME

2011年11月, 有人报告了一个关于Ubuntu中sudo文档说$HOME被重置的错误。也就是说,Ubuntu的man页正确描述了上游sudo的行为,但没有描述Ubuntu中的修补行为。

2014年9月, 有人报告了一个问题,指出默认情况下让sudo保留$HOME存在一些问题,并认为使用普通的sudo运行图形程序的问题不在于它本质上是危险的(经常引用这个维基页面),而在于sudo在Ubuntu中对$HOME的处理方式与众不同,使其变得危险,并应被视为一个bug。这个bug报告似乎引起了相当大的关注,包括来自Ubuntu开发者的关注,尽管修复这个问题还需要一些时间。

2016年3月, 后来成为与sudo保留$HOME相关问题的主要参考的错误被报告。最初,这个错误报告专注于用户安全问题,即那些不是管理员(即不能以root身份运行任意命令)但被允许使用sudo运行特定命令的用户可以恶意更改某些程序的行为,并在某些情况下甚至完全控制系统。它建议进行一个狭窄的改变,专门解决这种情况,同时当sudo组的成员以root或其他用户身份运行命令时,仍然保留$HOME

2019年4月, 有人报告了一个错误,对sudo默认保留$HOME的行为提出了异议,即使使用sudo -s也是如此。

那个月晚些时候, 关于2016年3月的错误的讨论重新开始,以可能解决完全移除Ubuntu特定补丁的问题。这使得该错误报告的焦点超出了它所描述的具体安全漏洞。从Ubuntu 19.10开始,目标是使得Ubuntu中的sudo与上游的sudo(以及其他操作系统中的sudo)对待$HOME的方式相同。

2019年5月, 有一个关于使用launchpadlib的程序(包括非图形化程序)遇到配置文件所有权问题的错误报告,因为sudo保留了$HOME

一周后, 另一个邮件列表讨论关于“sudo如何处理$HOME”的问题(请参见此存档页面),展示了各种观点,关于sudo应该如何处理$HOME。一个无争议的偏好是如果要进行更改,应仅在19.10及以后版本中进行, 不应在任何之前版本的更新中进行更改。 问题出现了,即上游的sudo是否会在未来版本中继续重置$HOME

上游的sudo维护者在被咨询后明确表示,没有计划进行这样的上游更改,并支持Ubuntu中下游版本的sudo默认重置$HOME的观点。该消息最初是在sudo-users邮件列表上发布的,并在2016年3月的错误报告的评论中引用了一条留言

2019年6月, Ubuntu开发者计划移除使得Ubuntu中的sudo保留$HOME的补丁。

大约一周后, 这个改变在Ubuntu的软件仓库中生效。这个改变从19.10版本开始适用。对于早期版本的sudo,唯一的改变是更新文档,以清楚正确地描述这些版本中sudo的行为。

致谢


11你的优秀问答获得了我的点赞。其实,我并不应该因为我的回答是受到你在 Ask Ubuntu 的聊天室中有关 sudo 更改的评论启发而获得第一次回答的荣誉。 - WinEunuuchs2Unix
1根据您的指示,我创建了sudo visudo -f /etc/sudoers.d/keep-home并添加了Defaults env_keep += "HOME"。谢谢! - isapir
我认为值得提到的是: https://stackoverflow.com/questions/20504662/how-to-get-home-directory-of-different-user-in-bash-script 你可以创建类似于 SUDO_HOME=\eval echo "~$SUDO_USER"`` 的东西。 - Wagner Patriota