用于特权提升的级联 Sudo

信息安全 验证 网络服务器 授权
2021-08-21 05:10:45

我打算使用最低权限的用户来完成给定的任务。问题是它涉及一种责任链,并sudo在我的脚本中翻译为链,将整体任务分散到许多地方。

sudoer 文件

"www-data"      ALL = (webadmin) NOPASSWD: /home/webadmin/scripts/git-deploy.sh
"webadmin"      ALL = (root) NOPASSWD: /root/scripts/copy-deploy ""

细节

为了使上一段更具体:一些请求被发送到 Apache。worker 以www-data身份运行 通过 mod_php,脚本/home/webadmin/scripts/git-deploy.sh使用

<?php
exec('/'.escapeshellarg($gitRepo))

$gitRepo 由以下脚本提供:

    $bitBucketUrl=json_decode($request->getContent(),true);

    if(isset($bitBucketUrl['url'])){
        $bitBucketUrl=$bitBucketUrl['url'];
        if(!preg_match('/\.git$/',$bitBucketUrl)){
            $bitBucketUrl.='.git';
        }
    }else{
        $result='bad url';
    }

    if(preg_match('/^git@bitbucket.org:TEAM\/[a-zA-Z0-9\.-]+\.git$/',$bitBucketUrl)){
        exec(escapeshellcmd('sudo -u webadmin /home/webadmin/scripts/git-deploy.sh '.escapeshellarg($bitBucketUrl)),$results);
        $result=$results;
    }else{
        $result=$bitBucketUrl;
    }

当然,脚本/home/webadmin/scripts/git-deploy.sh检查$1

#!/bin/bash
valid=^git@bitbucket.org:TEAM/[a-zA-Z0-9-]+\.git$
if [[ $1 =~ $valid  ]]; then
  name=${1#git@bitbucket.org:TEAM/}
  name=${name%.git}
  fullName=/home/webadmin/websites/$name
  if [[ -e $fullName ]] ; then
    echo "$fullName exists, will do a git pull instead"
    echo "cd $fullName && git pull"
  else
    echo "/usr/bin/git clone $1"
  fi
  if [[ -e $fullName/deploy/apache-conf/ ]]; then
    #sudo -u root /root/scripts/copy-deploy
  fi
fi

注意:我知道它只是回显,这是带有虚拟用户的测试版本。

每个脚本仅u+rwx由其所有者 (700) 提供。

问题

  1. 将脚本散布在各个地方是否违反了安全性?(很难理解发生了什么)
  2. 你还看到什么不对劲的地方吗?
1个回答

首先,你想做什么?你在解释你是怎么做的,而不是你的最终目标是什么。考虑修改您的问题以添加更多详细信息。

也就是说,对于这个答案的其余部分,我假设您正在做某种代理(?)来镜像 GIT 存储库。这听起来像是一个已经解决的问题,例如使用git hooks

不过,请按顺序解决您的具体问题:

将脚本散布在各个地方是否有违安全?(很难理解发生了什么)

是的。目前,您有以下“活动部件”:

  1. 解析似乎是通过 HTTP 请求发送的存储库 URL 的 PHP 脚本
  2. 调用其他二进制文件 (git) 的 shell 脚本
  3. 全部包裹在 sudo
  4. 如您所说,在许多服务器上传播

上述任何一个错误都可能导致系统受损。

此外,一个问题可能会在稍后阶段出现:如果您在脚本的一个副本中发现错误并忘记在其他服务器上复制它会发生什么?如果git返回一个您无法解析的模糊错误并且最终导致用户数据损坏(然后备份损坏)怎么办?如何报告错误?是否有集中式日志系统,更重要的是,是否有人监控它?

你看到别的错了吗?

是和不是。您的 PHP 脚本检查 JSON 输入中是否有“url”键,如果没有,则将“返回”设置为“错误值”。然后它继续这是不好的做法:目前本身不是错误,而是等待发生的错误。您应该在此处停止程序工作流程并返回错误,而不是继续。

例如,如果在几个月后有人添加了一些代码,例如:

/* previous code */
if(preg_match('/^git@bitbucket.org:TEAM\/[a-zA-Z0-9\.-]+\.git$/',$bitBucketUrl)){
    exec(escapeshellcmd('sudo -u webadmin /home/webadmin/scripts/git-deploy.sh '.escapeshellarg($bitBucketUrl)),$results);
    $result=$results;
}else{
    $result=$bitBucketUrl;
}

/* new code */
[...]
exec('sudo -u webadmin /home/webadmin/scripts/new-stuff.sh', $bitBucketURL);

我添加的代码有两个错误:你能发现它们吗?

  • 有人可能认为脚本new-stuff.sh只有在bitBucketURL包含有效数据时才会执行。相反,无论事先发生什么,它都会执行,因为您不会在else分支中退出。
  • 没有 escapeshellarg

如果你花了几秒钟的时间来发现错误,想象一下以前从未见过你的代码的人。

我故意escape从我的代码示例中省略了 ,原因是:您应该一劳永逸地清理输入并继续使用它,而不是escape每次需要使用用户输入时都必须调用函数族。忘记对 的调用太容易了escapeshellargs,特别是如果它需要更多的努力(=打字)。相反,尝试正确使用您的代码阻力最小的路径例如,

$bitBucketUrl=json_decode($request->getContent(),true);

可能变成:

$unsafe_user_input=json_decode($request->getContent(),true);
$safe_user_input = escapeshellarg($bitBucketUrl);
[...]
res = do_stuff(safe_user_input);
if (check_error(res))
{
    stop_with_error_message(message);

这样一来,什么和是什么就很明显了。unsafe_user_inputsafe_user_input

您的方法可能还有其他问题;以最低权限执行代码的想法是好的,但它在这里被过度设计并且可能产生令人讨厌的副作用。