简体   繁体   English

允许用户www-data(apache)调用需要来自CGI脚本的root特权的python脚本

[英]Allowing user www-data (apache) to call a python script that requires root privileges from a CGI script

The python script script.py is located in /usr/bin/monitor/scripts and it's main function is to use subprocess.check_call() and subprocess.check_output() to call various administrative tools (both c programs located in /usr/bin/monitor/ created specifically for the machine, and linux executables in /sbin like fdisk -l and df -h ). python脚本script.py位于/usr/bin/monitor/scripts ,其主要功能是使用subprocess.check_call()subprocess.check_output()调用各种管理工具(位于/usr/bin/monitor/ c程序) /usr/bin/monitor/是专门为机器创建的,以及/sbin linux可执行文件,例如fdisk -ldf -h )。 It was written to run as root and print output from these programs in a useful way to the command line. 它被编写为以root身份运行,并以一种有用的方式将这些程序的输出打印到命令行。

My project is to make the output from this script viewable through a webpage. 我的项目是使该脚本的输出可通过网页查看。 I'm on a Beaglebone Black using Apache2, which executes files as user www-data from its DocumentRoot, /var/www/html/ . 我在使用Apache2的Beaglebone Black上,它以用户www-data身份从其DocumentRoot /var/www/html/执行文件。 The webpage is set up like this: 网页设置如下:

  1. index.html uses an iframe to display the output of a python CGI script which is also located in /var/www/html/ index.html使用iframe来显示python CGI脚本的输出,该脚本也位于/var/www/html/

  2. script.cgi attempts to call/display output from script.py output using the subprocess module script.cgi尝试使用子流程模块从script.py输出中调用/显示输出

The problem is that script.py is being called just fine, but each of the calls within script.py fail and return script.py's error messages because I presume they need to be run as root when apache is running them as user www-data . 问题在于script.py的调用很好,但是script.py中的每个调用都失败并返回script.py的错误消息,因为我认为当apache以用户www-data身份运行它们时,它们需要以root身份运行。 。

To try to get around this, I created a new group called bbb , added www-data to the group, then ran chown :bbb script.py to change its group to bbb. 为了解决这个问题,我创建了一个名为bbb的新组,将www-data添加到该组,然后运行chown :bbb script.py将其组更改为bbb。 Unfortunately it was still causing the same problems, so I tried changing permissions from 755 to 775, which didn't work either. 不幸的是,它仍然会引起同样的问题,因此我尝试将权限从755更改为775,这也没有用。 I tried running chown :bbb * on the files/programs that script.py uses, also to no avail. 我尝试在script.py使用的文件/程序上运行chown :bbb * ,也无济于事。 Also, some of the executables script.py uses are in /sbin and I am cautious to just give it blanket root access to directories like this. 另外,script.py所使用的一些可执行文件位于/sbin ,我谨慎地给予它像这样的目录根目录访问权限。

Since my attempts at fixing ownership issues felt a bit like 1000 monkey code, I created new version of the script in which I create a list of html output, and after each print statement in the original code, I append the same line of text as a string with html tags to the html output list, then at the end of the script (in whatami) I have it create and write to a .txt file in /var/www/html/ , and call os.chmod("/var/www/html/data.txt", 0o755) to give apache access. 由于我尝试解决所有权问题的感觉有点像1000个猴子代码,因此我创建了该脚本的新版本,在其中创建了html输出列表,并在原始代码中的每个print语句之后,添加了与一个带有html标签的字符串到html输出列表,然后在脚本末尾(在whatami中)创建并写入/var/www/html/的.txt文件,然后调用os.chmod("/var/www/html/data.txt", 0o755)授予apache访问权限。 The CGI then calls subprocess.check_call() on script.py, then opens, reads, and prints each line with html formatting to the iframe in the webpage. 然后,CGI在script.py上调用subprocess.check_call() ,然后打开,读取并将html格式的每一行打印到网页的iframe中。 This attempt at least resulted in accurate output but... it only updates when it is run in terminal as root, rather than re-running script.py ever time the page is refreshed, which kind of undermines the point of the webpage. 这种尝试至少导致了准确的输出,但是...仅当它以root身份在终端中运行时才更新,而不是每次刷新页面时都重新运行script.py,这会破坏网页的重点。 I assume this means the subprocess check_call in the CGI script is not working correctly, but for some reason, the subprocess call itself doesn't throw any errors or indications of failure, yet the text file returns without being updated. 我认为这意味着CGI脚本中的子流程check_call无法正常工作,但是由于某种原因,该子流程调用本身不会引发任何错误或失败的迹象,但是该文本文件未更新就返回了​​。 Even with the subprocess call in a “try” block succeeded by a “print('call successful')”, it returns the success message and then the not updated text file. 即使在“ try”块中的子流程调用后接“ print('call success')”,它也会返回成功消息,然后返回未更新的文本文件。

I'm a bit at a loss trying to figure out how to just force the script to run and do it's thing in the background so that the file will update without just giving apache root access. 我有点不知所措,试图弄清楚如何仅强制脚本运行并在后台执行该操作,以便文件更新而不仅仅是授予apache根访问权限。 I've read a few things about either wrapping the python script in a shell that causes it to be run as root, or to change sudoers to give www-data sudo priviledges, but I do not want to introduce security issues or make what was intended to be a simple script allowing output to a webpage to become more convoluted than it already has. 我已经阅读了一些有关将python脚本包装在导致其以root身份运行的外壳中的信息,或者更改了sudoers来赋予www-data sudo特权,但是我不想引入安全性问题或进行任何操作。旨在成为一个简单的脚本,使网页的输出变得比现有的更加复杂。 Any advice or direction would be greatly appreciated. 任何建议或指示将不胜感激。

Best way IMO would be to "decouple" execution, by creating a localhost-only service which you "call" from the apache process by connecting via a local socket. IMO的最佳方法是通过创建本地主机服务来“分离”执行,该服务是通过本地套接字连接从apache进程中“调用”的。

Eg if using systemd : 例如,如果使用systemd

Create: /etc/systemd/system/my-svc.socket 创建:/etc/systemd/system/my-svc.socket

[Unit]
Description=My svc socket

[Socket]
ListenStream=127.0.0.1:1234
Accept=yes

[Install]
WantedBy=sockets.target

Create: /etc/systemd/system/my-svc@.service 创建:/etc/systemd/system/my-svc@.service

[Unit]
Description=My Service
Requires=my-svc.socket

[Service]
Type=simple
ExecStart=/opt/my-service/script.sh %i
StandardInput=socket
StandardError=journal
TimeoutStopSec=5

[Install]
WantedBy=multi-user.target

Create /opt/my-service/script.sh: 创建/opt/my-service/script.sh:

#!/bin/sh
echo "id=$(id)"
echo "args=$*"

Finish setup with: 完成设置:

$ sudo chmod +x /opt/my-service/script.sh
$ sudo systemctl daemon-reload

Try it out: 试试看:

$ nc 127.0.0.1 1234
id=uid=0(root) gid=0(root) groups=0(root)
args=55-127.0.0.1:1234-127.0.0.1:32938

Then from your cgi , you'll need to do the equivalent of the nc command above (just a tcp connection). 然后,从您的cgi ,您需要执行与上面的nc命令等效的操作(只是一个tcp连接)。

--jjo --jjo

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM