运行 python 在 lighttpd 上使用 sudo 的 cgi 脚本
Running python cgi script with sudo on lighttpd
感谢阅读!
我将 adafruit neopixel 连接到我的 Raspberry Pi 零(第 1 代)并让它们使用测试 python 代码。
作为下一步,我想生成一个网页,其中包含控制 neopixel 的按钮。
我主要关注本教程
https://www.hackster.io/mjrobot/iot-controlling-a-raspberry-pi-robot-over-internet-6988d4#toc-step-5--installing-the-lighttpd-webserver-8
起初我得到一个简单的 bash cgi 脚本 运行ning,它创建当前时间并将其写入文件。
切换到 python cgi 脚本相当容易,无需更改任何配置文件,这让我感到疑惑。
但是 运行ning 来自 html 的测试 python 代码根本不起作用。
与以前的问题一样,我开始阅读和修补,但似乎我尝试过的任何解决方案都不适合我。
我无法重述(工作和阅读过去的日子)我所做的一切,但
我将 www-data 添加到 sudoer 组,我在 /etc/sudoers.d 目录中创建了一个名为 010_www-data-nopasswd 的文件,内容为 www-data ALL=(ALL) NOPASSWD: ALL
。
我将 www-data 添加到组 gpio、i2c 和 spi。
我 运行
sudo visudo
并添加
www-data ALL=(ALL:ALL) ALL
和
www-data ALL = NOPASSWD: /var/www/lighttpd/cgi-bin/neopixelTest.py
还是不行。
我试过 bash cgi 脚本用 sudo
调用测试 python 脚本,它成功了!所以我认为归结为这一点。
我读过,在配置文件中有一行像 ".py" => "/usr/bin/python"
告诉 lighty 为以 .py 结尾的 cgi 脚本调用 /usr/bin/python,所以我想到了将 sudo
放入此行,这样基本上每个 python 脚本都将 运行 作为 sudo。确实不是一件好事,但我认为整个项目比 运行ning lighty 作为 root 更快、更脏、更好。但是我找不到这条线。
这是我的 /etc/lighttpd/lighttpd.conf 文件。
1 server.modules = (
2 "mod_indexfile",
3 "mod_access",
4 "mod_alias",
5 "mod_redirect",
6 )
7
8 server.document-root = "/var/www/lighttpd"
9 server.upload-dirs = ( "/var/cache/lighttpd/uploads" )
10 server.errorlog = "/var/log/lighttpd/error.log"
11 server.pid-file = "/var/run/lighttpd.pid"
12 server.username = "www-data"
13 server.groupname = "www-data"
14 server.port = 80
15
16 # strict parsing and normalization of URL for consistency and security
17 # https://redmine.lighttpd.net/projects/lighttpd/wiki/Server_http-parseoptsDetails
18 # (might need to explicitly set "url-path-2f-decode" = "disable"
19 # if a specific application is encoding URLs inside url-path)
20 server.http-parseopts = (
21 "header-strict" => "enable",# default
22 "host-strict" => "enable",# default
23 "host-normalize" => "enable",# default
24 "url-normalize-unreserved"=> "enable",# recommended highly
25 "url-normalize-required" => "enable",# recommended
26 "url-ctrls-reject" => "enable",# recommended
27 "url-path-2f-decode" => "enable",# recommended highly (unless breaks app)
28 #"url-path-2f-reject" => "enable",
29 "url-path-dotseg-remove" => "enable",# recommended highly (unless breaks app)
30 #"url-path-dotseg-reject" => "enable",
31 #"url-query-20-plus" => "enable",# consistency in query string
32 )
33
34 index-file.names = ( "index.php", "index.html" )
35 url.access-deny = ( "~", ".inc" )
36 static-file.exclude-extensions = ( ".php", ".pl", ".fcgi" )
37
38 compress.cache-dir = "/var/cache/lighttpd/compress/"
39 compress.filetype = ( "application/javascript", "text/css", "text/html", "text/plain" )
40
41 # default listening port for IPv6 falls back to the IPv4 port
42 include_shell "/usr/share/lighttpd/use-ipv6.pl " + server.port
43 include_shell "/usr/share/lighttpd/create-mime.conf.pl"
44 include "/etc/lighttpd/conf-enabled/*.conf"
45
46 #server.compat-module-load = "disable"
47 server.modules += (
48 "mod_compress",
49 "mod_dirlisting",
50 "mod_staticfile",
51 )
这是我的 /etc/lighttpd/conf-enabled/10-cgi.conf 文件。
1 # /usr/share/doc/lighttpd/cgi.txt
2
3 server.modules += ( "mod_cgi" )
4
5 $HTTP["url"] =~ "^/cgi-bin/" {
6 cgi.assign = ( "" => "" )
7 alias.url += ( "/cgi-bin/" => "/var/www/lighttpd/cgi-bin/" )
8 }
9
10 ## Warning this represents a security risk, as it allow to execute any file
11 ## with a .pl/.py even outside of /usr/lib/cgi-bin.
12 #
13 #cgi.assign = (
14 # ".pl" => "/usr/bin/perl",
15 # ".py" => "/usr/bin/python",
16 #)
17
我知道,有 ".py" => "/usr/bin/python"
行,但它被注释掉了。
这是我的 /etc/lighttpd/conf-enabled/10-fastcgi.conf 文件。
1 # /usr/share/doc/lighttpd/fastcgi.txt.gz
2 # http://redmine.lighttpd.net/projects/lighttpd/wiki/Docs:ConfigurationOptions#mod_fastcgi-fastcgi
3
4 server.modules += ( "mod_fastcgi" )
5
我认为这整件事甚至都没有使用 fastcgi。
最后但同样重要的是,这是我的 html 文件。
1 <!DOCTYPE html>
2 <html>
3 <head>
4
5 <!-- answer to favicon.ico request, see -->
6 <link rel="icon" href="data:,">
7
8 <meta charset="utf-8">
9 <!-- description for google -->
10 <meta name="description" content="This is a website to control the RPi zero">
11 <title>RPi Zero index</title>
12 </head>
13
14 <style>
15 body {background-color: lightyellow}
16 h1 {color:blue}
17 button {
18 color: black;
19 background: lightgrey;
20 border: 1px solid #000;
21 border-radius: 8px;
22 position: center;
23 }
24 </style>
25
26 <body>
27 <div style="text-align:center">
28 <h1>Hello world!</h1>
29 <br>
30 <p>please press test button</p>
31 <br>
32 <button onclick="alerttest('hello world!')">Alert!</button>
33 </p>
34 <button onclick="alert('wazzup!')">wazzup!</button>
35 </p>
36 <!-- button to call .cgi-script - it works -->
37 <button style="height: 50px; width: 125px; font-size: 25px" onclick="test_func()">test</button>
38 </p>
39 <!-- button to call .cgi-script which calls python scripts - it works -->
40 <button style="height: 50px; width: 125px; font-size: 25px" onclick="test_func2()">test2</button>
41 </p>
42 <!-- button to call python script directly - it works -->
43 <button style="height: 50px; width: 125px; font-size:25px" onclick="test_py()">python</button>
44 </p>
45 <!-- button to call neopixelTest.py -->
46 <button style="height: 50px; width: 125px; font-size:25px" onclick="test_neopixel()">neopixel</button>
47
48 <!--
49 <img src="/hello.png">
50 -->
51
52 </div>
53
54 <script>
55 var xmlhttp;
56 xmlhttp=new XMLHttpRequest();
57
58 function test_func() {
59 xmlhttp.open("GET","cgi-bin/test.cgi",true);
60 // prevent XML syntax error (in Firefox console, does not prevent execution or something, so only cosmetic co$
61 xmlhttp.overrideMimeType('application/javascript');
62 xmlhttp.send();
63 }
64
64
65 function alerttest(parameter) {
66 alert(parameter);
67 }
68
69 function test_func2() {
70 xmlhttp.open("GET","cgi-bin/python.cgi",true);
71 // prevent XML syntax error (in Firefox console, does not prevent execution or something, so only cosmetic co$
72 xmlhttp.overrideMimeType('application/javascript');
73 xmlhttp.send();
74 }
75
76 function test_py() {
77 xmlhttp.open("GET","cgi-bin/pythontest.py",true);
78 // prevent XML syntax error (in Firefox console, does not prevent execution or something, so only cosmetic co$
79 xmlhttp.overrideMimeType('application/javascript');
80 xmlhttp.send();
81 }
82
83 function test_neopixel() {
84 xmlhttp.open("GET","cgi-bin/neopixelTest.py",true);
85 // prevent XML syntax error (in Firefox console, does not prevent execution or something, so only cosmetic co$
86 xmlhttp.overrideMimeType('application/javascript');
87 xmlhttp.send();
88 }
89 </script>
90 </body>
91 </html>
92
neopixelTest.py文件基本上就是这个文件
https://github.com/adafruit/Adafruit_CircuitPython_NeoPixel/blob/master/examples/neopixel_rpi_simpletest.py。我刚刚在代码的顶部添加了 #! /usr/bin/python
。
我知道,我是一个绝对的初学者,但我仍然会很感激每一个帮助或想法!
提前致谢!
cgi.assign = ( "" => "" )
告诉 lighttpd 直接执行 cgi 脚本(因此它们必须标记为可执行(chmod +x
))并且第一行应该有 #!/usr/bin/python3
或类似的。
对于需要作为 root 运行 的特定 CGI 脚本,您可以在 cgi-bin 中创建一个名为 my-script-name
的包装脚本,exec 的 sudo <renamed-original-script>
另一种方法是将所有特权脚本放入一个子目录,并创建一个 lighttpd 条件
$HTTP["url"] =~ "^/cgi-bin/priv/" {
cgi.assign = ( "" => "/path/to/my-sudo-wrapper-script" )
}
和 my-sudo-wrapper-script
必须从环境中获取 CGI 目标 $SCRIPT_NAME
才能执行。
第三个选项是 运行 你的 python 代码作为守护进程,运行ning 作为 root,并让 lighttpd 通过 FastCGI 连接到它。这将允许您的代码 运行 作为 root,但 lighttpd 继续 运行 作为 www-data
我遇到的第四个选择是像这样简单地将 sudo
添加到 shebang 中:
#!/usr/bin/sudo python
这对我有用。我想执行用户(在我的例子中是 www-data)必须有权使用 sudo
而无需输入密码。
但同样,这是一个安全问题(尽管我不打算将我的 RPi 开放到网络上),所以我想在学习了如何通过 FastCGI 连接 lighttpd 之后我会尝试第三个选项python 守护进程。
谢谢!
感谢阅读!
我将 adafruit neopixel 连接到我的 Raspberry Pi 零(第 1 代)并让它们使用测试 python 代码。
作为下一步,我想生成一个网页,其中包含控制 neopixel 的按钮。 我主要关注本教程 https://www.hackster.io/mjrobot/iot-controlling-a-raspberry-pi-robot-over-internet-6988d4#toc-step-5--installing-the-lighttpd-webserver-8
起初我得到一个简单的 bash cgi 脚本 运行ning,它创建当前时间并将其写入文件。 切换到 python cgi 脚本相当容易,无需更改任何配置文件,这让我感到疑惑。 但是 运行ning 来自 html 的测试 python 代码根本不起作用。 与以前的问题一样,我开始阅读和修补,但似乎我尝试过的任何解决方案都不适合我。
我无法重述(工作和阅读过去的日子)我所做的一切,但
我将 www-data 添加到 sudoer 组,我在 /etc/sudoers.d 目录中创建了一个名为 010_www-data-nopasswd 的文件,内容为 www-data ALL=(ALL) NOPASSWD: ALL
。
我将 www-data 添加到组 gpio、i2c 和 spi。
我 运行
sudo visudo
并添加
www-data ALL=(ALL:ALL) ALL
和
www-data ALL = NOPASSWD: /var/www/lighttpd/cgi-bin/neopixelTest.py
还是不行。
我试过 bash cgi 脚本用 sudo
调用测试 python 脚本,它成功了!所以我认为归结为这一点。
我读过,在配置文件中有一行像 ".py" => "/usr/bin/python"
告诉 lighty 为以 .py 结尾的 cgi 脚本调用 /usr/bin/python,所以我想到了将 sudo
放入此行,这样基本上每个 python 脚本都将 运行 作为 sudo。确实不是一件好事,但我认为整个项目比 运行ning lighty 作为 root 更快、更脏、更好。但是我找不到这条线。
这是我的 /etc/lighttpd/lighttpd.conf 文件。
1 server.modules = (
2 "mod_indexfile",
3 "mod_access",
4 "mod_alias",
5 "mod_redirect",
6 )
7
8 server.document-root = "/var/www/lighttpd"
9 server.upload-dirs = ( "/var/cache/lighttpd/uploads" )
10 server.errorlog = "/var/log/lighttpd/error.log"
11 server.pid-file = "/var/run/lighttpd.pid"
12 server.username = "www-data"
13 server.groupname = "www-data"
14 server.port = 80
15
16 # strict parsing and normalization of URL for consistency and security
17 # https://redmine.lighttpd.net/projects/lighttpd/wiki/Server_http-parseoptsDetails
18 # (might need to explicitly set "url-path-2f-decode" = "disable"
19 # if a specific application is encoding URLs inside url-path)
20 server.http-parseopts = (
21 "header-strict" => "enable",# default
22 "host-strict" => "enable",# default
23 "host-normalize" => "enable",# default
24 "url-normalize-unreserved"=> "enable",# recommended highly
25 "url-normalize-required" => "enable",# recommended
26 "url-ctrls-reject" => "enable",# recommended
27 "url-path-2f-decode" => "enable",# recommended highly (unless breaks app)
28 #"url-path-2f-reject" => "enable",
29 "url-path-dotseg-remove" => "enable",# recommended highly (unless breaks app)
30 #"url-path-dotseg-reject" => "enable",
31 #"url-query-20-plus" => "enable",# consistency in query string
32 )
33
34 index-file.names = ( "index.php", "index.html" )
35 url.access-deny = ( "~", ".inc" )
36 static-file.exclude-extensions = ( ".php", ".pl", ".fcgi" )
37
38 compress.cache-dir = "/var/cache/lighttpd/compress/"
39 compress.filetype = ( "application/javascript", "text/css", "text/html", "text/plain" )
40
41 # default listening port for IPv6 falls back to the IPv4 port
42 include_shell "/usr/share/lighttpd/use-ipv6.pl " + server.port
43 include_shell "/usr/share/lighttpd/create-mime.conf.pl"
44 include "/etc/lighttpd/conf-enabled/*.conf"
45
46 #server.compat-module-load = "disable"
47 server.modules += (
48 "mod_compress",
49 "mod_dirlisting",
50 "mod_staticfile",
51 )
这是我的 /etc/lighttpd/conf-enabled/10-cgi.conf 文件。
1 # /usr/share/doc/lighttpd/cgi.txt
2
3 server.modules += ( "mod_cgi" )
4
5 $HTTP["url"] =~ "^/cgi-bin/" {
6 cgi.assign = ( "" => "" )
7 alias.url += ( "/cgi-bin/" => "/var/www/lighttpd/cgi-bin/" )
8 }
9
10 ## Warning this represents a security risk, as it allow to execute any file
11 ## with a .pl/.py even outside of /usr/lib/cgi-bin.
12 #
13 #cgi.assign = (
14 # ".pl" => "/usr/bin/perl",
15 # ".py" => "/usr/bin/python",
16 #)
17
我知道,有 ".py" => "/usr/bin/python"
行,但它被注释掉了。
这是我的 /etc/lighttpd/conf-enabled/10-fastcgi.conf 文件。
1 # /usr/share/doc/lighttpd/fastcgi.txt.gz
2 # http://redmine.lighttpd.net/projects/lighttpd/wiki/Docs:ConfigurationOptions#mod_fastcgi-fastcgi
3
4 server.modules += ( "mod_fastcgi" )
5
我认为这整件事甚至都没有使用 fastcgi。
最后但同样重要的是,这是我的 html 文件。
1 <!DOCTYPE html>
2 <html>
3 <head>
4
5 <!-- answer to favicon.ico request, see -->
6 <link rel="icon" href="data:,">
7
8 <meta charset="utf-8">
9 <!-- description for google -->
10 <meta name="description" content="This is a website to control the RPi zero">
11 <title>RPi Zero index</title>
12 </head>
13
14 <style>
15 body {background-color: lightyellow}
16 h1 {color:blue}
17 button {
18 color: black;
19 background: lightgrey;
20 border: 1px solid #000;
21 border-radius: 8px;
22 position: center;
23 }
24 </style>
25
26 <body>
27 <div style="text-align:center">
28 <h1>Hello world!</h1>
29 <br>
30 <p>please press test button</p>
31 <br>
32 <button onclick="alerttest('hello world!')">Alert!</button>
33 </p>
34 <button onclick="alert('wazzup!')">wazzup!</button>
35 </p>
36 <!-- button to call .cgi-script - it works -->
37 <button style="height: 50px; width: 125px; font-size: 25px" onclick="test_func()">test</button>
38 </p>
39 <!-- button to call .cgi-script which calls python scripts - it works -->
40 <button style="height: 50px; width: 125px; font-size: 25px" onclick="test_func2()">test2</button>
41 </p>
42 <!-- button to call python script directly - it works -->
43 <button style="height: 50px; width: 125px; font-size:25px" onclick="test_py()">python</button>
44 </p>
45 <!-- button to call neopixelTest.py -->
46 <button style="height: 50px; width: 125px; font-size:25px" onclick="test_neopixel()">neopixel</button>
47
48 <!--
49 <img src="/hello.png">
50 -->
51
52 </div>
53
54 <script>
55 var xmlhttp;
56 xmlhttp=new XMLHttpRequest();
57
58 function test_func() {
59 xmlhttp.open("GET","cgi-bin/test.cgi",true);
60 // prevent XML syntax error (in Firefox console, does not prevent execution or something, so only cosmetic co$
61 xmlhttp.overrideMimeType('application/javascript');
62 xmlhttp.send();
63 }
64
64
65 function alerttest(parameter) {
66 alert(parameter);
67 }
68
69 function test_func2() {
70 xmlhttp.open("GET","cgi-bin/python.cgi",true);
71 // prevent XML syntax error (in Firefox console, does not prevent execution or something, so only cosmetic co$
72 xmlhttp.overrideMimeType('application/javascript');
73 xmlhttp.send();
74 }
75
76 function test_py() {
77 xmlhttp.open("GET","cgi-bin/pythontest.py",true);
78 // prevent XML syntax error (in Firefox console, does not prevent execution or something, so only cosmetic co$
79 xmlhttp.overrideMimeType('application/javascript');
80 xmlhttp.send();
81 }
82
83 function test_neopixel() {
84 xmlhttp.open("GET","cgi-bin/neopixelTest.py",true);
85 // prevent XML syntax error (in Firefox console, does not prevent execution or something, so only cosmetic co$
86 xmlhttp.overrideMimeType('application/javascript');
87 xmlhttp.send();
88 }
89 </script>
90 </body>
91 </html>
92
neopixelTest.py文件基本上就是这个文件
https://github.com/adafruit/Adafruit_CircuitPython_NeoPixel/blob/master/examples/neopixel_rpi_simpletest.py。我刚刚在代码的顶部添加了 #! /usr/bin/python
。
我知道,我是一个绝对的初学者,但我仍然会很感激每一个帮助或想法! 提前致谢!
cgi.assign = ( "" => "" )
告诉 lighttpd 直接执行 cgi 脚本(因此它们必须标记为可执行(chmod +x
))并且第一行应该有 #!/usr/bin/python3
或类似的。
对于需要作为 root 运行 的特定 CGI 脚本,您可以在 cgi-bin 中创建一个名为 my-script-name
的包装脚本,exec 的 sudo <renamed-original-script>
另一种方法是将所有特权脚本放入一个子目录,并创建一个 lighttpd 条件
$HTTP["url"] =~ "^/cgi-bin/priv/" {
cgi.assign = ( "" => "/path/to/my-sudo-wrapper-script" )
}
和 my-sudo-wrapper-script
必须从环境中获取 CGI 目标 $SCRIPT_NAME
才能执行。
第三个选项是 运行 你的 python 代码作为守护进程,运行ning 作为 root,并让 lighttpd 通过 FastCGI 连接到它。这将允许您的代码 运行 作为 root,但 lighttpd 继续 运行 作为 www-data
我遇到的第四个选择是像这样简单地将 sudo
添加到 shebang 中:
#!/usr/bin/sudo python
这对我有用。我想执行用户(在我的例子中是 www-data)必须有权使用 sudo
而无需输入密码。
但同样,这是一个安全问题(尽管我不打算将我的 RPi 开放到网络上),所以我想在学习了如何通过 FastCGI 连接 lighttpd 之后我会尝试第三个选项python 守护进程。 谢谢!