第八章:用户环境与Sudo

用户的shell环境可能不利于良好的系统管理。环境变量的存在是为了改变软件行为。以提升权限运行的软件需要表现良好,而改变这种行为的环境变量可能会威胁到您的系统。因此,sudo默认在运行任何命令之前删除用户的大部分环境。

如果您不确定您的环境中有什么,请运行命令 env 。你应该在那里看到一些熟悉的项目,比如SHELL和PATH,但你也会看到一堆不太知名的变量,比如SHLVL、G_BROKEN_FILENAMES、EDOOFUS或其他什么。其中一些可能很重要。其中许多都不是。你甚至可能不知道这些变量是如何或在哪里设置的。清除环境有助于确保特权命令正常运行。

危险环境变量

环境变量怎么可能是危险的?程序检查环境变量的设置——例如,shell使用$HOME来标识用户的主目录。这些环境变量是系统类Unix的一部分。

另一方面,一些程序使用环境变量LD_LIBRARY_PRELOAD来标识包含其他共享库的目录。但该目录可能包含将身份验证凭据复制到远程服务器的libc版本。在不同的操作系统上使用了一整套LD_变量。像bash这样的shell使用$IFS来提供分隔命令行参数的字符。将IFS更改为精心选择的值可能会使流程做出意想不到的事情。如果你因为一个不正确的环境变量让你的文本编辑器吃掉了你的文件而丢失了学期论文,这很烦人。如果您使用具有特权命令的相同环境,您可能会丢失比自己的文件更多的文件。

程序可以查找任何环境变量。商业软件通常使用数百个环境变量来存储任意配置数据,就像Microsoft Windows使用注册表一样。没有危险环境变量的主列表,因为一个系统上的安全因素可能会破坏另一个系统。

Sudo允许您仔细控制shell环境。

执行环境

Sudo不仅仅为你运行特权命令。它启动一个shell实例,运行命令,退出shell,并将控制权返回给您运行sudo的shell。这就是为什么像 sudo cd /opt/secret 这样的命令不会像你期望的那样工作。假设你的命令提示符在你的主目录中。您运行cd命令。Sudo启动一个shell并切换到所需的目录。然后那个shell就出来了。正在运行的shell实例仍在主目录中,而所需目录中的shell实例已不存在。

你想看看那个秘密目录里有什么吗?尝试使用 sudo ls /opt/secret 。您想运行一系列更复杂的shell命令吗?显式启动一个shell实例,并将命令写成带引号的字符串。

在这里,我启动了一个shell实例,收集 /home 中所有目录的总大小,并按大小对它们进行排序,先是最大的。这个shell命令的确切细节并不重要;关键是我让sudo通过 sh –c 运行了一系列shell命令。您仍然需要特权才能运行sh。

Sudo将新shell实例的初始环境建立在您的环境之上,除非您告诉它不要这样做。您可以告诉Sudo以三种不同的方式建立此环境:获取当前环境并传递选定的环境变量,获取环境并删除选定的环境变量,或者放弃环境并使用目标用户的环境。我们将分别介绍每一个。

将环境变量列入白名单

默认情况下,sudo会删除除$TERM、$PATH、$HOME、$MAIL、$SHELL、$LOGNAME、$USER和$USERNAME之外的所有环境变量。这意味着sudo在您首选的shell中使用常规路径运行命令,并且不会自动将创建的文件转储到root的主目录中。Sudo还会自动删除任何以字符()开头的环境变量,因为这些变量可以被解释为Bash函数。一切都很好……直到你需要其他环境变量。

这就是 env_keep sudoers选项的作用所在。env_keep 允许系统所有者定义sudo应保留的环境变量列表。例如,几个环境变量控制语言和字符集显示选项。如果你是母语为俄语的人,你可能希望在sudo下运行的命令使用你喜欢的字符集。

注意选项名称后的 += 。这意味着“将以下内容添加到任何现有列表中”。如果使用纯等号,该选项将覆盖默认值。您将获得角色集,但会丢失路径、shell和主目录。您还可以使用 -= 从列表中减去环境变量。

您可以根据需要使用任意数量的 env_keep 语句,并将其与特定的用户、计算机、命令和RunAs列表相匹配。也许管理员可以保留他们的SSH环境变量,这样他们就可以通过SFTP在网络上复制特权文件。

或者,你可能被困在代理服务器后面,每个人都需要他们环境中的代理。

您可以将任何需要的环境变量传递到sudo环境中。

将环境变量列入黑名单

除了已知危险的环境变量外,保持用户环境不变是枚举不良的另一个例子。然而,如果你打算射中自己的脚,以下是如何装手枪的方法。

env_reset 选项告诉sudo删除除受信任的少数环境变量之外的所有环境变量。它是默认设置的。要关闭此功能,请在sudoers中明确禁用它。

即使你想毫发无损地传递大多数环境变量,也可能需要从环境中剥离一些变量。使用 env_delete 选项删除环境变量。

用户保留其整个环境,LD_LIBRARY_PRELOAD除外。

运行 sudo sh 将允许新的shell实例从配置文件中读取这些变量的新副本,您当然可以在shell中自己设置它们。但是,当您运行单个命令时,sudo会从环境中删除这些变量。

env_keep 一样, env_delete 允许您根据组、命令等将环境变量添加到删除列表中。

允许用户覆盖

一些用户在运行某些命令时,可能需要以安全策略无法预料的方式自定义他们的环境。应用程序服务器的行为可能因环境变量的存在与否而异,如果软件变化很快,这些值可能需要不断更新。Sudoers允许你编写一个安全策略,上面写着“这是标准的环境设置,但让这些特定的用户为这些特定的命令设置自己的环境变量。”

在命令上使用SETENV和NOSETENV标签,让用户请求sudo不要更改他的环境变量。SETENV标签允许用户根据要求保留他们的环境。在这里,Pete有一个特殊的例外,允许他根据某些命令控制他的环境。

dbtest1机器上,Pete可以使用自己的环境作为 oracle 运行Oracle命令。Oracle软件对环境变量高度敏感。Pete可以探索测试服务器上的任意配置,并在了解自己的需求后,在生产环境中正式请求更新的sudoers策略。

Pete必须明确要求sudo不要使用 -E 标志来改变他的环境。

如果没有 -E 标志,sudo将执行其标准的环境剥离,尽管sudoers中存在NOSETENV。

使用标签NOSETENV覆盖之前的SETENV。

Pete可以控制所有Oracle命令的环境,除了 gennttab 。(记住,sudo规则是最后一场比赛。)

除了SETENV标签,还有一个SETENV选项。像使用其他选项一样使用它。

Thea可以在任何地方覆盖她的环境,只要她使用sudo的 -E 标志。作为高级系统管理员,她已经陷入了系统损坏的困境,她需要灵活性来解决任何可能的问题。赋予自己按需覆盖环境的能力是一个完全合理的例外,特别是因为它只在她特别要求的时候有效。

仅允许高度信任的用户覆盖环境变量,并且仅在测试环境中。记住,sudo策略不仅是为了控制用户,也是为了限制恶意入侵者对系统造成的损害。

目标用户环境

我曾经参加过一次会议,结果归结为“除非Dave重新启动它,否则服务器运行良好。”管理解决方案是解雇Dave,但技术解决方案是修复sudo如何管理Dave的环境。(幸运的是,对Dave来说,技术解决方案占了上风。)

在某些情况下,您不想将任何环境变量带入特权环境。你甚至不需要你的shell或主目录——相反,你需要在目标用户的shell环境中以目标用户的身份运行命令。Sudo允许您使用 -i 选项来实现这一点。

通过使用 sudo –i ,您可以模拟一个新的登录名作为目标用户,读取目标用户的点文件,如 .login.profile ,然后运行请求的命令。您的原始用户环境不会以任何方式保留。

根据我的经验,让sudo以目标用户身份初始化环境是管理高度依赖其启动环境的应用程序服务器的最佳方式。许多Java服务器端应用程序从环境变量中获取配置,而这些变量在您的个人环境中可能不正确。通过在单个帐户中配置该环境,您消除了对应用程序稳定性的一个威胁。

Sudo环境默认值

不同版本的sudo在环境变量方面的行为可能不同。我不希望任何默认的pass环境变量发生变化,但未来版本的sudo可能会添加新的变量。

要了解您的sudo版本上的环境处理默认值,请以root身份运行 sudo –V 。输出告诉您这个系统的特殊sudo二进制文件是如何构建的,以及它如何处理不同的环境变量。您将看到三组变量:要进行健全性检查(sanity-check)的变量、要删除的变量和要保留的变量。

为了进行健全性检查,sudo会检查列出的变量中的字符 %/ ,如果存在,则将其删除。一些环境变量会影响您的基本会话,例如,一个错误的TERM变量会在您键入命令时对其进行加扰。运行没有设置TERM的命令比运行带有垃圾终端的命令要好。

您将看到一个“要删除的环境变量”列表。Sudo正是这样做的。您不能使用 env_keep 覆盖此列表;如果你想在sudo环境中使用这些变量,你必须在目标用户的帐户中设置它们。

要保留的环境变量列表是本章前面给出的列表的补充。您可以保留HOME和PATH等变量,也可以保留特定sudo版本显示的变量。

Sudo特定变量

在sudo下运行的命令会获取四个特定于sudo的环境变量:SUDO_COMMAND、SUDO_USER、SUDO_UID 和 SUDO_GID。

程序或脚本可以检查这些变量的存在,如果它们存在或以某种方式使用它们,则行为会有所不同。例如,您可以在日志消息中使用SUDO_USER。“是的,我是以root运行的,但实际上,我是以mike运行的。怪他。”

环境定制

sudo策略可以做的不仅仅是允许和不允许环境变量;它可以显式设置变量。Sudoer策略允许您设置用户的路径,如果需要,您还可以设置任意环境变量。

管理 $PATH

一个环境变量比大多数变量稍微棘手一些。许多入侵者试图破坏用户的$PATH,这样用户就会运行虚假版本的命令,而不是正确的命令。如果服务台的流氓需要重置用户的密码,但他运行的是 /tmp/.1234/hacker/passwd 程序,而不是 /usr/bin/passwd ,那么就会发生坏事。使用secure_path选项为sudo命令定义受信任的路径。

Sudo尝试使用安全路径运行该命令。如果命令不在安全路径中,则失败。

这会影响通过sudo运行的命令,但不会影响通过sudo启动的shell实例。如果你启动一个完整的交互式shell,shell会在初始化环境时读取目标用户的 .profile 和其他shell启动文件。运行sudo时,安全路径会有所帮助,如下所示:

在这个用例中,secure_path确保正在运行的passwd命令实际上是系统的passwd指令,而不是入侵者的自定义副本。但是,它不会验证用户运行的sudo命令是否正确,因此用户仍然需要处理他们的$PATH。

添加环境变量

有时,您希望为特权用户专门设置环境变量。使用 env_file 选项提供包含新环境变量的文件的完整路径。一种常见的情况是,您位于代理服务器后面。您希望用户始终通过您的代理访问互联网吗?将环境变量添加到其环境中。

环境文件包含一个标准的变量赋值列表,如下所示:

Sudo在剥离环境之前添加了这些环境变量,因此也要在 env_keep sudoers规则中列出所有添加的变量。这也意味着你覆盖了用户自己的环境变量,所以如果用户有不同的设置,你只是替换了它。

使用Sudo启动Shell

有些人使用sudo作为su的替代品。本质上,他们无需使用密码即可成为root。

我不鼓励这样做。Sudo会记录人们使用的命令,但如果没有额外的配置,Sudo不会记录shell会话中发生的事情。(我们将在第12章介绍sudo日志记录。)但既然你们中的一些人无论如何都会这样做,让我们来讨论一下。

su 命令的意思是“switch user —— 切换用户”。运行 su -su -l 会初始化一个新的shell,就像使用 sudo -i 一样。您将获得目标用户的环境。运行简单的 su 会切换您正在运行的用户身份,但会保留您的大部分环境。

如果你想用sudo完全替换su,你可以启用 shell_boards 选项。设置此选项后,运行不带参数的sudo会给您一个root提示符。

当Thea在没有任何命令行参数的情况下运行sudo时,她是root:

您可以使用 -s 标志在命令行上模拟 shell_board

如果用户没有运行root shell的权限,即使存在 shell_oards ,sudo也会拒绝访问。

sudo的另一个常用用法是运行shell,但保留自己的环境:

这将使您的shell保持不变,并保留您的sudoers策略传递的任何环境变量。

你应该使用哪一种?理想情况下:没有。如果必须让用户通过sudo成为另一个用户,请按照第12章配置完整的会话日志记录。

Sudo无终端

有时你想在没有连接终端的情况下运行sudo。您可能希望在桌面管理器中有一个右键单击菜单,通过sudo运行程序。但是,此sudo程序不会在终端中运行,因此sudo无法要求您输入密码。你需要一种方法来获取sudo密码。

Sudo可以运行一个外部程序来提示输入密码。使用 sudo.conf 中的 askpass 路径告诉sudo在哪里可以找到这个密码程序。最有可能在任何具有sudo的桌面系统上找到的图形密码提示软件是OpenSSH的 askpassopenssh-askpass

当sudo需要密码但没有终端请求密码时,它使用 sudo.conf 中的askpass设置。

需要终端

有时,命令在没有完整环境的情况下运行。作为CGI脚本的一部分运行的程序或由cron等调度器运行的程序实际上没有终端可供运行。一般的类Unix系统不会启动shell会话来运行这些命令,而是将其作为子进程运行。如果您不希望自动化进程通过sudo运行任意命令,请查看 requiretty

requiretty 选项告诉sudo只有在终端中运行命令时才工作。在sudoers中启用此选项意味着程序在没有终端的情况下无法运行。用户无法编写调用sudo的CGI脚本——好吧,他们可以编写,但sudo调用不起作用。

现在,您可以管理sudo创建的环境,或者它是否需要环境。现在让我们看看sudo如何保护您的用户免受损坏的系统的影响。