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)
其实我知道一个方法,比如:
#!/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)
但我觉得它太重了,因为我必须用这种方式注册每个函数。
当export -f func
我在环境中看到以下内容后:
$ env | grep func\nBASH_FUNC_func%%=() \xe2\x80\xa6\n
Run Code Online (Sandbox Code Playgroud)\nBash 将函数导出为变量,其名称类似于BASH_FUNC_foo%%
,值以()
. 我相信在Shellshock名称之前没有任何前缀 ( foo=() \xe2\x80\xa6
)。Apple 前缀似乎是__BASH_FUNC<
. 在任何情况下,该值都以 开头()
。
sudo
尝试对这些名称以及整个环境非常小心。想象一下,您可以sudo
只使用脚本来运行 Bash 脚本。想象一下该脚本合法地使用cat
. 通过导出名为 you 的 shell 函数,cat
可以有效地更改脚本并运行任意代码。
您可以使用自己的方法来做同样的事情$PATH
(并且有多种方法可以做到这一点;但管理员也有一些方法不让您这样做)。或者,如果您设法使用$LD_PRELOAD
(不太容易,请参阅man 5 sudoers
它提到的地方LD_
)。
让我们回到导出的 shell 函数。首先,当我sudo -V
以 root 身份(sudo sudo -V
作为普通用户)运行时,部分输出如下:
Environment variables to remove:\n *=()*\n
Run Code Online (Sandbox Code Playgroud)\n或者可能:
\nEnvironment variables to remove:\n __BASH_FUNC<*\n BASH_FUNC_*\n
Run Code Online (Sandbox Code Playgroud)\n我认为取决于 的版本sudo
。
我可以使用以下行从列表中删除这些模式sudoers
(但暂时不要这样做,请先阅读整个答案):
Defaults env_delete -= "*=()* BASH_FUNC_* __BASH_FUNC<*"\n
Run Code Online (Sandbox Code Playgroud)\n我什至可以将它们添加到“保留”列表中(也不要):
\nDefaults env_keep += "*=()* BASH_FUNC_* __BASH_FUNC<*"\n
Run Code Online (Sandbox Code Playgroud)\n但它并不一定能让您的导出函数与sudo
. 原因是 的模式*=()*
可能是硬编码的,某些版本的sudo
拒绝变量的值以 开头()
。这种行为是进化而来的。可用的解决方案取决于sudo
您拥有的版本。
\n\n命令环境
\n由于环境变量可以影响程序行为,
\nsudoers
因此提供了一种方法来限制要运行的命令继承用户环境中的哪些变量。sudoers 有两种不同的方式来处理环境变量。默认情况下,该
\nenv_reset
选项处于启用状态。[\xe2\x80\xa6]HOME
、SHELL
和环境变量根据目标用户进行初始化,并根据调用用户设置变量LOGNAME
。如果或选项允许,其他变量(例如、和)将从调用用户的环境中保留。这实际上是环境变量的白名单。[\xe2\x80\xa6] 值以开头的环境变量将被删除,除非名称和值部分均由或匹配,因为它们可能被 bash shell 解释为函数。在 1.8.11 版本之前,此类变量始终被删除。USER
SUDO_*
DISPLAY
PATH
TERM
env_check
env_keep
()
env_keep
env_check
但是,如果
\nenv_reset
禁用该选项,则env_check
和env_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 函数可以按如下方式匹配:Run Code Online (Sandbox Code Playgroud)\nenv_keep += "BASH_FUNC_my_func%%=()*"\n
如果没有
\n=()*
后缀,这将不匹配,因为默认情况下不保留 bash shell 函数。
正如你所看到的,env_reset
这很重要。您很可能已启用它。-E
请注意,选项 ( )的全部要点是按需sudo -E
禁用。env_reset
在我的 Kubuntu 18.04.3 LTS 中,我的sudo
版本是 1.8.21。默认情况下,它放置*=()*
在要删除的变量列表中。这一行sudoers
让它停止:
Defaults env_delete -= "*=()*"\n
Run Code Online (Sandbox Code Playgroud)\n然后sudo -E
将保留任何导出的 shell 函数。这是我的主要答案。
我的 Debian 9sudo
版本是 1.8.19。默认情况下,它将BASH_FUNC_*
和__BASH_FUNC<*
放在要删除的变量列表中。删除这些条目不起作用,因为该工具几乎总是拒绝值以无论如何开头的变量()
;这种行为似乎是硬编码的。
该版本的手册提供了一些线索。我找到了一个解决方案:
\nDefaults env_keep += "BASH_FUNC_func%%=()*"\n
Run Code Online (Sandbox Code Playgroud)\n然后,如果导出,则sudo
without -E
将保留这个特定的 shell 函数 ( func
)。缺点(或者在某些情况下可能是优点):
sudo -E
,所以我不能简单地按需保留环境的其余部分;BASH_FUNC_*%%=()*
,BASH_FUNC_*
否则*=()*
将无法工作。此方法在 1.8.21 中也适用。
\n根据手册,在版本 1.8.11 之前,以 开头的变量()
总是被删除。如果您sudo
已经那么老了,那么除了您在问题(或类似问题)中使用的技巧之外,没有其他解决方案。
笔记:
\nsudoers
像任何其他文件一样编辑;使用visudo
。其他预防措施以防万一:\nsudo -i
。如果您sudoers
在主 shell 中做了一些愚蠢的事情,您仍然可以以 root 身份运行而不依赖该文件。tmux
或。screen
如果您做了一些愚蠢的事情并且sudoers
(意外地)连接失败,提升的 shell 将等待您的常规用户重新连接,而不是退出。cp -a
),尤其是当您要尝试它时。sudo
允许 sudoers 运行任意代码的技巧也没有什么意义(因为您可以sudo
自由地运行任意代码,而无需任何技巧,并且您希望这样),请三思而后行你允许太多了。允许sudo
保留任何 shell 功能可能会让您很容易搬起石头砸自己的脚;不一定是现在,而是将来。