为什么'sudo -E'不保留export -f导出的函数环境变量?

Ano*_*non 5 bash sudo environment-variables function

在脚本.sh中:

#!/bin/bash

func(){
  echo "I am here a func."
}

export -f func
export variable="I am here a variable"

sudo -E bash -c "func ; echo $variable"

Run Code Online (Sandbox Code Playgroud)

输出如下:

bash: func: command not found
I am here a variable
Run Code Online (Sandbox Code Playgroud)
  1. 为什么?
  2. 有什么解决方案可以让我通过 sudo 在新生 shell 中调用函数 [在 script.sh 中声明]?

其实我知道一个方法,比如:

#!/bin/bash

func(){
  echo "I am here a func."
}

FUNC=$(declare -f func)
export variable="I am here a variable"

sudo -E bash -c "$FUNC; func ; echo $variable"

Run Code Online (Sandbox Code Playgroud)

但我觉得它太重了,因为我必须用这种方式注册每个函数。

Kam*_*ski 7

export -f func我在环境中看到以下内容后:

\n
$ env | grep func\nBASH_FUNC_func%%=() \xe2\x80\xa6\n
Run Code Online (Sandbox Code Playgroud)\n

Bash 将函数导出为变量,其名称类似于BASH_FUNC_foo%%,值以(). 我相信在Shellshock名称之前没有任何前缀 ( foo=() \xe2\x80\xa6)。Apple 前缀似乎是__BASH_FUNC<. 在任何情况下,该值都以 开头()

\n

sudo尝试对这些名称以及整个环境非常小心。想象一下,您可以sudo只使用脚本来运行 Bash 脚本。想象一下该脚本合法地使用cat. 通过导出名为 you 的 shell 函数,cat可以有效地更改脚本并运行任意代码。

\n

您可以使用自己的方法来做同样的事情$PATH(并且有多种方法可以做到这一点;但管理员也有一些方法不让您这样做)。或者,如果您设法使用$LD_PRELOAD(不太容易,请参阅man 5 sudoers它提到的地方LD_)。

\n

让我们回到导出的 shell 函数。首先,当我sudo -V以 root 身份(sudo sudo -V作为普通用户)运行时,部分输出如下:

\n
Environment variables to remove:\n        *=()*\n
Run Code Online (Sandbox Code Playgroud)\n

或者可能:

\n
Environment variables to remove:\n        __BASH_FUNC<*\n        BASH_FUNC_*\n
Run Code Online (Sandbox Code Playgroud)\n

我认为取决于 的版本sudo

\n

我可以使用以下行从列表中删除这些模式sudoers(但暂时不要这样做,请先阅读整个答案):

\n
Defaults       env_delete -= "*=()* BASH_FUNC_* __BASH_FUNC<*"\n
Run Code Online (Sandbox Code Playgroud)\n

我什至可以将它们添加到“保留”列表中(也不要):

\n
Defaults        env_keep += "*=()* BASH_FUNC_* __BASH_FUNC<*"\n
Run Code Online (Sandbox Code Playgroud)\n

但它并不一定能让您的导出函数与sudo. 原因是 的模式*=()*可能是硬编码的,某些版本的sudo拒绝变量的值以 开头()。这种行为是进化而来的。可用的解决方案取决于sudo您拥有的版本。

\n

man 5 sudoers

\n
\n

命令环境

\n

由于环境变量可以影响程序行为,sudoers因此提供了一种方法来限制要运行的命令继承用户环境中的哪些变量。sudoers 有两种不同的方式来处理环境变量。

\n

默认情况下,该env_reset选项处于启用状态。[\xe2\x80\xa6] HOMEMAILSHELL和环境变量根据目标用户进行初始化,并根据调用用户设置变量LOGNAME。如果或选项允许,其他变量(例如、和)将从调用用户的环境中保留。这实际上是环境变量的白名单。[\xe2\x80\xa6] 值以开头的环境变量将被删除,除非名称和值部分均由或匹配,因为它们可能被 bash shell 解释为函数。在 1.8.11 版本之前,此类变量始终被删除。USERSUDO_*DISPLAYPATHTERMenv_checkenv_keep()env_keepenv_check

\n

但是,如果env_reset禁用该选项,则env_checkenv_delete选项未明确拒绝的任何变量将从调用进程继承。在这种情况下,env_check行为env_delete就像黑名单一样。在版本 1.8.21 之前,()始终删除以 开头的值的环境变量。env_delete从版本 1.8.21 开始,使用模式 in来匹配 bash shell 函数。由于不可能将所有潜在危险的环境变量列入黑名单,因此env_reset鼓励使用默认行为。

\n

env_check由、env_delete、 或指定的环境变量env_keep可能包含一个或多个*字符,这些字符将匹配零个或多个字符。不支持其他通配符。

\n

默认情况下,环境变量按名称匹配。但是,如果模式包含等号 ( =),则变量名称和值都必须匹配。例如,bash shell 函数可以按如下方式匹配:

\n
env_keep += "BASH_FUNC_my_func%%=()*"\n
Run Code Online (Sandbox Code Playgroud)\n

如果没有=()*后缀,这将不匹配,因为默认情况下不保留 bash shell 函数。

\n
\n

正如你所看到的,env_reset这很重要。您很可能已启用它。-E请注意,选项 ( )的全部要点是按需sudo -E禁用。env_reset

\n
\n

在我的 Kubuntu 18.04.3 LTS 中,我的sudo版本是 1.8.21。默认情况下,它放置*=()*在要删除的变量列表中。这一行sudoers让它停止:

\n
Defaults       env_delete -= "*=()*"\n
Run Code Online (Sandbox Code Playgroud)\n

然后sudo -E将保留任何导出的 shell 函数。这是我的主要答案。

\n
\n

我的 Debian 9sudo版本是 1.8.19。默认情况下,它将BASH_FUNC_*__BASH_FUNC<*放在要删除的变量列表中。删除这些条目不起作用,因为该工具几乎总是拒绝值以无论如何开头的变量();这种行为似乎是硬编码的。

\n

该版本的手册提供了一些线索。我找到了一个解决方案:

\n
Defaults       env_keep += "BASH_FUNC_func%%=()*"\n
Run Code Online (Sandbox Code Playgroud)\n

然后,如果导出,则sudo without -E将保留这个特定的 shell 函数 ( func)。缺点(或者在某些情况下可能是优点):

\n
    \n
  • 这不适用于sudo -E,所以我不能简单地按需保留环境的其余部分;
  • \n
  • 我需要显式指定变量的全名,不带通配符;BASH_FUNC_*%%=()*BASH_FUNC_*否则*=()*将无法工作。
  • \n
\n

此方法在 1.8.21 中也适用。

\n
\n

根据手册,在版本 1.8.11 之前,以 开头的变量()总是被删除。如果您sudo已经那么老了,那么除了您在问题(或类似问题)中使用的技巧之外,没有其他解决方案。

\n
\n

笔记:

\n
    \n
  • 不要sudoers像任何其他文件一样编辑;使用visudo。其他预防措施以防万一:\n
      \n
    • 以 root ( ) 身份启动一个单独的“备用”shell sudo -i。如果您sudoers在主 shell 中做了一些愚蠢的事情,您仍然可以以 root 身份运行而不依赖该文件。
    • \n
    • 通过 SSH 连接时,请在生成提升的 shell 之前使用tmux或。screen如果您做了一些愚蠢的事情并且sudoers(意外地)连接失败,提升的 shell 将等待您的常规用户重新连接,而不是退出。
    • \n
    • 首先制作文件的备份副本 ( cp -a),尤其是当您要尝试它时。
    • \n
    • 当您认为已经完成时,请确保在退出“备用”提升的 shell 之前可以重新获得 root 访问权限。
    • \n
    \n
  • \n
  • 即使您是唯一的用户,并且强化防范sudo允许 sudoers 运行任意代码的技巧也没有什么意义(因为您可以sudo自由地运行任意代码,而无需任何技巧,并且您希望这样),请三思而后行你允许太多了。允许sudo保留任何 shell 功能可能会让您很容易搬起石头砸自己的脚;不一定是现在,而是将来。
  • \n
\n