一个巨大的应用程序需要在一个特定时间对需要 root 权限的文件执行少量写入。它实际上并不是一个文件,而是一个以文件形式暴露给 Linux 的硬件接口。
为了避免向整个应用程序授予 root 权限,我编写了一个 bash 脚本来执行关键任务。例如,以下脚本将启用硬件接口的端口 17 作为输出:
echo "17" > /sys/class/gpio/export
echo "out" > /sys/class/gpio/gpio17/direction
然而,由于suid
我的系统上的 bash 脚本被禁用,我想知道实现此目的的最佳方法是什么。
使用提供的一些解决方法这里
从主应用程序调用脚本
sudo
,并相应地编辑 sudoers 列表,以避免在调用脚本时需要密码。我对授予 sudo 权限感到有点不舒服echo
。只需编写一个 C 程序,用
fprintf
,并将其设置为 suid root 即可。对字符串和文件名进行硬编码,并确保只有 root 可以编辑它。或者从文本文件中读取字符串,同样确保没有人可以编辑该文件。我没有想到的其他解决方案比上面提出的更安全或更简单?
答案1
您不需要授予sudo
访问权限echo
。事实上,这是没有意义的,因为,例如使用sudo echo foo > bar
,重定向是作为原始用户而不是 root 完成的。
使用 调用小脚本sudo
,NOPASSWD:
仅允许需要访问该脚本的用户访问该脚本(以及任何其他类似的脚本)。
这始终是最好/最安全的使用方式sudo
。将需要 root 权限的少量命令隔离到它们自己的单独脚本中,并允许不受信任或部分受信任的用户仅以 root 身份运行该脚本。
小sudo
脚本应该要么不从用户那里获取参数(或输入)(即它调用的任何其他程序应该有硬编码的选项和参数),要么应该非常仔细地验证它必须的任何参数/输入接受用户的。
在验证中保持偏执——不要寻找“已知的坏”的东西来排除,而只允许“已知的好”的东西,并在任何不匹配或错误或任何远程可疑的情况下中止。
验证应该尽可能早地在脚本中进行(最好是在它执行之前)任何事物否则作为根)。
我真的当我第一次写这个答案时应该提到这一点,但如果你的脚本是一个 shell 脚本必须正确引用全部变量。要特别小心地引用包含用户提供的输入的变量任何方式,但不要假设某些变量是安全的,全部引用。
这包括可能由用户控制的环境变量(例如"$PATH"
,等"$HOME"
。"$USER"
并且肯定包括CGI 脚本中的 等)"$QUERY_STRING"
。"HTTP_USER_AGENT"
事实上,只需引用它们即可。如果必须构建具有多个参数的命令行,请使用数组构建参数列表并引用 - "${myarray[@]}"
。
我是否经常说“引用所有内容”?记住它。做吧。
答案2
检查 GPIO 文件的所有者:
ls -l /sys/class/gpio/
最有可能的是,您会发现它们归 group 所有gpio
:
-rwxrwx--- 1 root gpio 4096 Mar 8 10:50 export
...
在这种情况下,您只需将用户添加到gpio
组中即可授予访问权限,而无需使用 sudo:
sudo usermod -aG gpio myusername
您需要注销并重新登录才能使更改生效。
答案3
一种解决方案(特别是在 Linux 桌面上使用,但也适用于其他情况)是使用D-总线激活以 root 身份运行的小服务并波尔基特进行授权。这基本上就是 polkit 的本质设计的为了;从它的介绍性文档:
polkit 提供了一个授权 API,旨在由特权程序(“机制”)使用,为非特权程序(“客户端”)提供服务。有关系统架构和总体情况,请参阅 polkit 手册页。
大的非特权程序不会执行您的帮助程序,而是会在总线上发送请求。您的助手可以作为系统启动时启动的守护进程运行,或者更好的是根据 systemd 的需要激活。然后,该助手将使用 polkit 来验证请求是否来自授权位置。 (或者,在这种情况下,如果感觉有点矫枉过正,您可以使用其他一些硬编码的身份验证/授权机制。)
我找到了一个关于通过 D-Bus 进行通信的好基础文章,虽然我还没有测试过,这似乎是添加 polkit 的基本示例。
在这种方法中,不需要标记 setuid 任何内容。
答案4
使用 Bless tee 代替 sudo 的 echo 是解决需要限制 root 权限的情况的一种常见方法。重定向到 /dev/null 是为了阻止任何输出泄漏 - T 恤可以满足您的要求。
echo "17" | sudo tee /sys/class/gpio/export > /dev/null
echo "out" | sudo tee /sys/class/gpio/gpio17/direction > /dev/null