【问题标题】:How do you find the original user through multiple sudo and su commands?如何通过多个 sudo 和 su 命令找到原始用户?
【发布时间】:2011-06-03 15:16:53
【问题描述】:

通过 sudo 或 su 运行脚本时,我想获取原始用户。无论多个sudosu 在彼此内部运行,特别是sudo su -,都应该发生这种情况。

【问题讨论】:

    标签: linux bash unix 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 iwho 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,因此会丢失信息。一些程序,例如lognamewho -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 的答案相比,这里的主要改进是输出中不包含多余的括号。

      【讨论】:

      【解决方案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
      }
      

      lognamewho am i 没有给我想要的答案,尤其是在较长的su user1su user2su user3... 列表中

      我知道最初的问题是很久以前提出的,但人们(比如我)仍然在问,这看起来是提出解决方案的好地方。

      【讨论】:

        【解决方案8】:

        多次调用 ps 的替代方法:执行一次 pstree 调用

        pstree -lu -s $$ | grep --max-count=1 -o '([^)]*)' | head -n 1
        

        输出(以偶数登录时):(evan)

        pstree 参数:

        • -l:长线(不缩短)
        • -u:当用户更改为(用户名)时显示
        • -s $$: 显示这个进程的父进程

        使用grep -ohead 获取第一个用户更改(即登录)。

        限制:该命令不能包含任何大括号()(通常不包含)

        【讨论】:

        • 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
        

        loginctlsession-statusshow-ssession 系统命令在没有参数的情况下具有不同的行为:session-status 使用当前会话,但 show-ssession 使用管理器。但是,由于其机器可读的输出,使用 show-session 更适合脚本使用。这就是为什么需要两次调用loginctl

        【讨论】:

          【解决方案10】:

          您可以从控制终端的所有者那里获得它。这是“whowasi”实用程序的旧 C 代码:http://sivann.gr/software/whowasi.c

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2019-12-22
            • 2019-09-26
            • 2016-01-12
            • 2012-12-22
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2017-05-14
            相关资源
            最近更新 更多