如何通过多个 sudo 和 su 命令找到原始用户?
Posted
技术标签:
【中文标题】如何通过多个 sudo 和 su 命令找到原始用户?【英文标题】:How do you find the original user through multiple sudo and su commands? 【发布时间】:2011-06-03 15:16:53 【问题描述】:通过 sudo 或 su 运行脚本时,我想获取原始用户。无论多个sudo
或su
在彼此内部运行,特别是sudo su -
,都应该发生这种情况。
【问题讨论】:
【参考方案1】:结果:
使用who am i | awk 'print $1'
或logname
,因为不保证其他方法。
以自己的身份登录:
evan> echo $USER
evan
evan> echo $SUDO_USER
evan> echo $LOGNAME
evan
evan> whoami
evan
evan> who am i | awk 'print $1'
evan
evan> logname
evan
evan>
普通sudo:
evan> sudo -s
root> echo $USER
root
root> echo $SUDO_USER
evan
root> echo $LOGNAME
root
root> whoami
root
root> who am i | awk 'print $1'
evan
root> logname
evan
root>
须藤苏-:
evan> sudo su -
[root ]# echo $USER
root
[root ]# echo $SUDO_USER
[root ]# echo $LOGNAME
root
[root ]# whoami
root
[root ]# who am i | awk 'print $1'
evan
[root ]# logname
evan
[root ]#
须藤苏-;总结:
evan> sudo su -
[root ]# su tom
tom$ echo $USER
tom
tom$ echo $SUDO_USER
tom$ echo $LOGNAME
tom
tom$ whoami
tom
tom$ who am i | awk 'print $1'
evan
tom$ logname
evan
tom$
【讨论】:
在这种情况下你可以使用who | awk 'print $1'
...如果您是唯一登录的人(而且只有一次)。
您只需要 2 个参数:who am i
与 who smells bad
相同。此外,它仅在 STDIN
与 TTY 关联时才有效。所以如果你运行echo "hello" | who am i
,它根本就行不通。
你不会正常运行echo "hello" | who am i
,除非你的脚本在没有终端的环境中运行。然后您可能会看到who am i
不起作用的错误,因为不可读的标准输入存在某种问题,在这种情况下,您可能会绝望地尝试将数据管道输入who am i
以满足它的标准输入要求。 tylerl 只是注意到他已经走上了这条路,并且管道将无法工作,因为 stdin 必须既可读又与 TTY 相关联。
@even 是的,虽然我希望它需要尽可能少的配置,所以我现在使用logname
,事实证明它确实有效,而who am i
则不行.【参考方案2】:
没有完美的答案。当您更改用户 ID 时,通常不会保留原始用户 ID,因此会丢失信息。一些程序,例如logname
和who -m
实现了一个hack,它们检查哪个终端连接到stdin
,然后检查哪个用户在该终端上登录。
此解决方案经常有效,但并非万无一失,当然也不应该被视为安全。例如,假设who
输出以下内容:
tom pts/0 2011-07-03 19:18 (1.2.3.4)
joe pts/1 2011-07-03 19:10 (5.6.7.8)
tom
使用su
进入root,并运行您的程序。如果STDIN
没有被重定向,那么像logname
这样的程序将输出tom
。如果它被重定向(例如从文件中):
logname < /some/file
那么结果是“no login name
”,因为输入不是终端。然而,更有趣的是,用户可以伪装成不同的登录用户。由于 Joe 在 pts/1 上登录,Tom 可以通过运行来伪装成他
logname < /dev/pts1
现在,它显示 joe
,尽管运行该命令的是 tom。换句话说,如果你在任何类型的安全角色中使用这种机制,你就疯了。
【讨论】:
如果您自己运行脚本(如使用的命令所证明的那样),安全性不是问题。如果是这样,你有更多的问题,因为他们也有 sudo 访问权限。该人可以复制脚本并以他们喜欢的任何方式对其进行修改。这只是一种获取登录名以在脚本中使用的方法。还是我错过了您所说的某些内容? @evan:拥有 sudo 访问权限并不意味着能够覆盖文件。 @Flimzy 在什么情况下root不能覆盖文件? @evan:很明显,任何时候你的 sudo 访问权限都不允许你访问 shell 或任何其他可以覆盖文件的命令。 @evan sudo 访问权限并不总是(在大多数管理情况下不应该是)总 root 访问权限。它是一组可配置的受限执行上下文。【参考方案3】:这是我在 HP-UX 上编写的 ksh
函数。我不知道它如何在 Linux 中与 Bash
一起工作。这个想法是sudo
进程作为原始用户运行,子进程是目标用户。通过循环返回父进程,我们可以找到原始进程的用户。
#
# The options of ps require UNIX_STD=2003. I am setting it
# in a subshell to avoid having it pollute the parent's namespace.
#
function findUser
thisPID=$$
origUser=$(whoami)
thisUser=$origUser
while [ "$thisUser" = "$origUser" ]
do
( export UNIX_STD=2003; ps -p$thisPID -ouser,ppid,pid,comm ) | grep $thisPID | read thisUser myPPid myPid myComm
thisPID=$myPPid
done
if [ "$thisUser" = "root" ]
then
thisUser=$origUser
fi
if [ "$#" -gt "0" ]
then
echo $origUser--$thisUser--$myComm
else
echo $thisUser
fi
return 0
我知道最初的问题是很久以前提出的,但人们(比如我)仍在询问,这看起来是一个解决问题的好地方。
【讨论】:
【参考方案4】:使用 logname(1) 获取用户的登录名怎么样?
【讨论】:
logname(1)
不起作用,但 logname
起作用 - 添加上面的结果
最初我尝试过$LOGNAME
,但没有成功。也添加到上面的结果中。
logname
还需要 tty 吗?通过我的测试,它总是通过。 (也许我出了点问题。)我正在使用 coreutils 8.26 运行 linux。
My logname (GNU coreutils) 8.28 on 总是返回“logname: no login name” (Ubuntu 18.04.2)【参考方案5】:
ORIGIONAL_USER=$(pstree -lu -s $$ | grep --max-count=1 -o '([^)]*)' | head -n 1 | sed 's/[()]//g') && echo $ORIGIONAL_USER
这是唯一对我有用的东西。
pstree
将正在运行的进程显示为树。该树以 pid 为根,这里以 $$
给出,在 bash 中扩展为当前 shell 的进程 ID。所以命令的第一部分用一些有趣的格式列出了当前 shell 的所有祖先进程。该命令的其余部分丢弃了有趣的格式以挑选出拥有最古老祖先进程的用户的名称。
与其他基于pstree
的答案相比,这里的主要改进是输出中不包含多余的括号。
【讨论】:
没有解释,只是从existing answer得到了最低限度的改进【参考方案6】:user1683793 的 findUser() 函数移植到 bash
并扩展,因此它也返回存储在 NSS 库中的用户名。
#!/bin/bash
function findUser()
thisPID=$$
origUser=$(whoami)
thisUser=$origUser
while [ "$thisUser" = "$origUser" ]
do
ARR=($(ps h -p$thisPID -ouser,ppid;))
thisUser="$ARR[0]"
myPPid="$ARR[1]"
thisPID=$myPPid
done
getent passwd "$thisUser" | cut -d: -f1
user=$(findUser)
echo "logged in: $user"
【讨论】:
仅供参考:此函数(以及它所基于的函数)不会循环返回由相互嵌套的 sudo 生成的多个 shell。【参考方案7】:循环返回并给出用户列表
基于 user1683793 的回答
通过排除非 TTY 进程,我跳过 root 作为登录的发起者。我不确定在某些情况下这是否会排除太多
#!/bin/ksh
function findUserList
typeset userList prevUser thisPID thisUser myPPid myPid myTTY myComm
thisPID=$$ # starting with this process-ID
while [ "$thisPID" != 1 ] # and cycling back to the origin
do
( ps -p$thisPID -ouser,ppid,pid,tty,comm ) | grep $thisPID | read thisUser myPPid myPid myTTY myComm
thisPID=$myPPid
[[ $myComm =~ ^su ]] && continue # su is always run by root -> skip it
[[ $myTTY == '?' ]] && continue # skip what is running somewhere in the background (without a terminal)
if [[ $prevUser != $thisUser ]]; then # we only want the change of user
prevUser="$thisUser" # keep the user for comparing
userList="$userList:+$userList $thisUser" # and add the new user to the list
fi
#print "$thisPID=$thisUser: $userList -> $thisUser -> $myComm " >&2
done
print "$userList"
return 0
logname
或 who am i
没有给我想要的答案,尤其是在较长的su user1
、su user2
、su user3
、...
列表中
我知道最初的问题是很久以前提出的,但人们(比如我)仍然在问,这看起来是提出解决方案的好地方。
【讨论】:
【参考方案8】:多次调用 ps 的替代方法:执行一次 pstree 调用
pstree -lu -s $$ | grep --max-count=1 -o '([^)]*)' | head -n 1
输出(以偶数登录时):(evan)
pstree 参数:
-l:长线(不缩短) -u:当用户更改为(用户名)时显示 -s $$: 显示这个进程的父进程使用grep -o
和head
获取第一个用户更改(即登录)。
限制:该命令不能包含任何大括号()
(通常不包含)
【讨论】:
pstree -lu -s $$ |head -n1|sed -e 's/[^(]*(([^)]*)).*/\1/'【参考方案9】:在运行 systemd-logind
的系统上,systemd API provides this information。如果你想从一个 shell 脚本中访问这些信息,需要使用这样的东西:
$ loginctl session-status \
| (read session_id ignored; loginctl show-session -p User $session_id)
User=1000
loginctl
的 session-status
和 show-ssession
系统命令在没有参数的情况下具有不同的行为:session-status
使用当前会话,但 show-ssession
使用管理器。但是,由于其机器可读的输出,使用 show-session
更适合脚本使用。这就是为什么需要两次调用loginctl
。
【讨论】:
【参考方案10】:您可以从控制终端的所有者那里获得它。这是“whowasi”实用程序的旧 C 代码:http://sivann.gr/software/whowasi.c
【讨论】:
以上是关于如何通过多个 sudo 和 su 命令找到原始用户?的主要内容,如果未能解决你的问题,请参考以下文章