Python – What’s the safest way to kick off a root-level process via cgi on an Apache server

apache-2.2cgipythonrootsetuid

The problem: I have a script that runs periodically via a cron job as root, but I want to give people a way to kick it off asynchronously too, via a webpage. (The script will be written to ensure it doesn't run overlapping instances or such.)

I don't need the users to log in or have an account, they simply click a button and if the script is ready to be run it'll run. The users may select arguments for the script (heavily filtered as inputs) but for simplicity we'll say they just have the button to choose to press.

As a simple test, I've created a Python script in cgi-bin. chown-ing it to root:root and then applying "chmod ug+" to it didn't have the desired results: it still thinks it has the effective group of the web server account… from what I can tell this isn't allowed.

I read that wrapping it with a compiled cgi program would do the job, so I created a C wrapper that calls my script (its permissions restored to normal) and gave the executable the root permissions and setuid bit. That worked… the script ran as if root ran it.

My main question is, is this normal (the need for the binary wrapper to get the job done) and is this the secure way to do this? It's not world-facing but still, I'd like to learn best practices.

More broadly, I often wonder why a compiled binary is more "trusted" than a script in practice? I'd think you'd trust a file that was human-readable over a cryptic binaryy. If an attacker can edit a file then you're already in trouble, more so if it's one you can't easily examine. In short, I'd expect it to be the other way 'round on that basis. Your thoughts?

Best Answer

First of all: don't do this, or at least do not allow any parameters to be passed from the web to your root-running application.

Now to your actual question: When you write a script (Python in your case), it gets executed by the interpreter. So to be able to run the script as root, you would need to set the python interpreter suid-root, but you really do not want to do this. This is because your script isn't an executable as such, but just a set of rules for the interpreter. When you wrap your script with a binary executable, you now again have an executable that gains root rights and the python interpreter called from the executable has root too. More information on this can be found in Cannot Set UID on Shell Scripts and http://www.diablotin.com/librairie/networking/puis/ch05_05.htm

That said, calling the script via sudo as @SvenW suggested should work fine.