允许以 root 身份运行 bash 脚本,但不允许以 sudo 身份运行

PK0*_*001 25 bash root sudo shell-script

我是这里的新手,也是 bash/linux 的新手。

我的老师给了我一项作业,只允许在您“真正”是 root 而不是在使用 sudo 时运行脚本。经过两个小时的搜索和尝试,我开始认为他在欺骗我。只允许 root 很容易,但如何排除使用 sudo 运行它的用户?

这就是我所拥有的:

if [[ $EUID -ne 0 ]]; then
  echo "You must be root to run this script."
  exit
fi
Run Code Online (Sandbox Code Playgroud)

Ark*_*zyk 23

我能想到的唯一方法是检查SUDO_*sudo 设置的环境变量之一:

#!/usr/bin/env sh

if [ "$(id -u)" -eq 0 ]
then
    if [ -n "$SUDO_USER" ]
    then
        printf "This script has to run as root (not sudo)\n" >&2
        exit 1
    fi
    printf "OK, script run as root (not sudo)\n"
else
    printf "This script has to run as root\n" >&2
    exit 1
fi
Run Code Online (Sandbox Code Playgroud)

请注意,这个解决方案当然不是面向未来的,因为您无法阻止任何人在运行脚本之前设置变量:

$ su
Password:
# SUDO_USER=whatever ./root.sh
This script has to run as root (not sudo)
# ./root.sh
OK, script run as root (not sudo)
Run Code Online (Sandbox Code Playgroud)

  • +1,但这仍然可以被诸如`sudo bash -c 'unset SUDO_USER=; 之类的东西所欺骗。my_command;'`。我不会依赖它。 (10认同)
  • 关心!如果用户运行`sudo su -`,变量`SUDO_*` 不再存在!见 https://unix.stackexchange.com/a/626764/27653 (4认同)
  • 小心,如果用户运行`screen`,。分离然后重新连接,你只能看到一个 *root 登录 shell*!见最后一段:https://unix.stackexchange.com/a/626764/27653 (2认同)

Jef*_*ler 19

另一种选择是检查祖父进程名称是否为“sudo”:

#!/bin/sh
if [ "$(id -u)" -eq 0 ]
then
  if [ $(ps -o comm= -p $(ps -o ppid= -p $$)) = "sudo" ]
  then
    echo Running under sudo
  else
    echo Running as root and not via sudo
  fi
else
  echo Not running as root
fi
Run Code Online (Sandbox Code Playgroud)

  • 在“sudo su”或“sudo bash”下运行时会失败。我认为您需要检查多个级别的流程。 (5认同)
  • 的确; 你可以扩展它来循环 PPID 进程树,但我想我会涵盖`./script` 或 `sudo ./script` 的最常见用法,指出检查“sudo”的核心思想为是否为父进程。 (3认同)
  • 这可以通过 sudoing 然后运行一个否认的进程来规避吗? (2认同)

lao*_*lux 13

有关哪个用户登录的信息在 中可用/proc/self/loginuid由于评论而编辑 :该文件似乎并不存在于所有系统上。我进行了测试,它在 Centos 6、Fedora 32、Fedora 33 和 Ubuntu 20.04 上都可用,所有这些都在标准的 x86_64 设置中。 如果我们以用户身份登录,然后使用sudosu成为 root,这不会改变/proc/self/loginuid,它将是一些非零值。如果我们直接以 root 身份登录,那么cat /proc/self/loginuid将返回0. 请注意,此文件不能修改,即使是 root 也不能​​这样做。 由于 Stéphane Chazelas 的评论而进行编辑 :Root 可以使用echo 0 > /proc/self/loginuid. 但是,这可以通过设置来防止auditctl --loginuid-immutable

检查真正根(如果auditctl --loginuid-immutable设置)的脚本可能看起来像

#!/bin/bash
loginuid=$(cat /proc/self/loginuid)
echo $loginuid
if [[ $loginuid -ne 0 ]]; then
    echo "You did not log in as root."
    exit
fi
Run Code Online (Sandbox Code Playgroud)

  • 我相信这在 Linux 上需要 `CONFIG_AUDIT=y`,但在这样的系统上,这是正确的答案(尽管我认为实际目标是阅读手册并找到 SUDO_USER)。 (4认同)
  • 可以使用“auditctl --loginuid-immutable”进行更改。 (3认同)
  • `sudo sh -c 'echo 0 > /proc/self/loginuid && cat /proc/self/loginuid'` 在带有 5.4.0-58-generic Linux 内核的 Ubuntu 20.04 上为我输出 0 (2认同)

F. *_*uri 6

避免sudorootbash 脚本中使用?

序言:关心分享root账号!!

不幸的是,没有抵抗的方式......请仔细阅读最后一段

一旦您授予某人 root 访问权限,他们就可以做任何事情,包括编辑您的脚本!!

例如,如果用户点击sudo su -,则变量SUDO_*不再存在......

第一种快速使用方法 pstree

sudo在整个当前树中搜索存在的更简单方法似乎使用pstree

die() { echo >&2 ${0##*/} Error: "$@"; exit 1;}
pstree -s $$ | grep -q '\bsudo\b' && die "Can't be run under sudo"
Run Code Online (Sandbox Code Playgroud)

随着ps而已,你可以遍历ps ho ppid

die() { echo >&2 ${0##*/} Error: "$@"; exit 1;}
pid=$$
while read pid name foo < <(ps ho ppid,cmd $pid) && ((pid>1));do
    [ "$name" = "sudo" ] && die "Can't be run under sudo"
done
Run Code Online (Sandbox Code Playgroud)

关于更名的评论sudo

如果 sudo 命令被重命名或复制,那么不是查找命令名称,而是在整个父树中查找UID。所以脚本与以前的相同,但UID >= 1000在父树中搜索:

die() { echo >&2 ${0##*/} Error: "$@"; exit 1;}
pid=$$
while read pid uid < <(ps ho ppid,uid $pid) && ((pid>1));do
    ((uid>999)) && die "Can't be run under sudo"
done
Run Code Online (Sandbox Code Playgroud)

因为我们在谈论Un*x

正确地说,避免使用固定的静态数据,使用UID_MINfrom /etc/login.defs

die() { echo >&2 ${0##*/} Error: "$@"; exit 1;}
while read fld val;do
    case $fld in UID_MIN ) UIDMIN=$val ;break ;; esac
done </etc/login.defs
((UIDMIN)) || die Getting UID_MIN.
pid=$$
while read pid uid < <(ps ho ppid,uid $pid) && ((pid>1));do
    (( uid >= UIDMIN )) && die "Can't be run under sudo"
done
Run Code Online (Sandbox Code Playgroud)

sudo无论如何使用执行此操作的解决方法

但这一切都有些脆弱

die() { echo >&2 ${0##*/} Error: "$@"; exit 1;}
pstree -s $$ | grep -q '\bsudo\b' && die "Can't be run under sudo"
Run Code Online (Sandbox Code Playgroud)

现在打Ctrl+ a,然后d分离。键入exit或点击Ctrl+d以在用户模式下返回...

然后简单地:

die() { echo >&2 ${0##*/} Error: "$@"; exit 1;}
pid=$$
while read pid name foo < <(ps ho ppid,cmd $pid) && ((pid>1));do
    [ "$name" = "sudo" ] && die "Can't be run under sudo"
done
Run Code Online (Sandbox Code Playgroud)

现在,您将登录到root 登录会话。没有任何 sudo 的痕迹。

die() { echo >&2 ${0##*/} Error: "$@"; exit 1;}
pid=$$
while read pid uid < <(ps ho ppid,uid $pid) && ((pid>1));do
    ((uid>999)) && die "Can't be run under sudo"
done
Run Code Online (Sandbox Code Playgroud)

结论

正如chepner正确评论的那样:sudo 是为了提供对特定工具的特定访问权限:

sudo 不需要它为您提供 root 访问权限;这只是每个人都熟悉的默认行为。sudo 可以配置为只允许你做非常具体的事情,包括根本没有获得 root 访问权限 – chepner

在使用脆弱的解决方法之前,请注意正确配置它们!

看:

apropos sudo
Run Code Online (Sandbox Code Playgroud)

并仔细阅读

man sudo.conf
man sudoers
Run Code Online (Sandbox Code Playgroud)

关于 logname

看看正确的Stéphane Chazelas 的答案!这可能是家庭作业的最佳答案!

同样,很多解决方法,例如:echo 0 > /proc/self/loginuid...

关于/proc/self/loginuid的Linux

请阅读有趣的laolux 对此的回答

die() { echo >&2 ${0##*/} Error: "$@"; exit 1;}
read lUid </proc/self/loginuid || die "Can't access procfile"
((lUid)) && die "You must be logged as root."
Run Code Online (Sandbox Code Playgroud)

(此语法避免分叉!)

但无论如何

  • 脚本可以复制和编辑
  • 根据配置/内核,此内核条目可能被欺骗
  • Sudoer可以创建cron用于启动特殊screen会话的条目root。(cron并且screen不是做这样的事情的唯一方法!只是我想到的第一个。)

  • 与其他解决方案一样,如果 sudo 命令已重命名,则会失败 (2认同)