views:

2043

answers:

6

Running Fedora 9/10, Apache 2, PHP 5...

Can I run a shell script as root, from a PHP script using exec()?

Do I just give Apache root priveleges, and then add "sudo" in front of them command?

Specifically, I'm trying to start and stop a background script.

Currently I have a shell script that just runs the app, start.sh:

#!/bin/bash 
/path/to/my/app/appname

And a script that kills the app, stop.sh:

#!/bin/bash 
killall appname

Would I just do:

<?php
exec("sudo start.sh");
?>

Thanks in advance.

A: 

That would be huge security risk. Why run the script as root anyways?

vartec
I need to be able to kill the script from the web interface I have. So if it was started by other means, I still need to be able to kill it. i.e, if someone starts the script from the console, I need to be able to kill it.
SkippyFire
+1  A: 

I'm not professional in this field, but it looks like you need SUID flag.

Read here for examples or google

Sergej Andrejev
Can anybody explain why my answer is being downvoted? You create a file with root as owner and a SUID and allow apache to execute it. This way you won't have to create passwordless root or store root password in the code while still have an ability to run something as root. But still there is a risk
Sergej Andrejev
+7  A: 

You can't just sudo like that, you need to setup passwordless sudo first in /etc/sudoers file. This can be done with visudo command for example. Make sure you set up privileges in sudoers file in such way to constrain the apache user to that single command you wish to run (i.e. your shell script).

Even then, it poses a security risk, because anyone could create a PHP script and run your shell script in turn. So make sure that shell script itself is secure from alteration by Apache user.

The second part, killall, is even more problematic. You shouldn't just allow Apache to run killall with root privileges. You should wrap killall in another shell script and grant access to that in sudoers.

In the end: do not run Apache with root account and do not use setuid. Both open a can of worms, and since you are a newbie (given the question you asked) you are very likely to miss some of small details that would create potential problems.

Milan Babuškov
+1  A: 
  1. Don't run Apache as root. Apache has been designed to cope very well with starting as root and then dropping its privileges just as soon as it can

  2. Don't use sudo within your script either - it'll be too easy to end up with sudo misconfigured such that any script running on your server gets to run any program it likes with root privileges

  3. Look at making your own program run "setuid", so that it gets root privileges, but then drops them (just like Apache does) when it doesn't need them any more

  4. Make sure your "setuid" executable can't be run by anybody who isn't supposed to be able to run it.

Alnitak
+1  A: 

You need a layer of abstraction to provide a little security at least!...

The way I do this is to write a simple UDP server* with root privs in Python which: watches out for incoming UDP packets on a given port compares them to a whitelist if they match carry out the operation

You then have a little bit of PHP that messages the Python server with pre-defined messages...

<?php
  $handle = fsockopen("udp://localhost",12345);
  fwrite($handle,"Start Script");
  fclose($handle);
?>

The python server watches for packets on port 12345 but just ignores any that aren't either "Start Script" or "Stop Script", as it runs as root it can happily start your bash script. You ABSOLUTELY MUST use white-listing though, it is REALLY NOT SAFE to send ANY input from a UDP socket to the command line directly!

Do note that UDP can be spoofed so if your firewall permits spoofed inbound traffic (it realy ought not to!) someone could send forged packets to your Python server and stop/start your service. This is unlikely to be a problem but if you can't fix your firewall and you want to guard against it you could rework the above using TCP/IP which can't be spoofed.

Roger Heathcote.

*It's a really trivial server to write ( ~20 lines ) but if you don't know how to then just message me and I will send it to you or post it here.

I'm not sure if I will use this method for now, but it could definitely prove useful in the future! Could you post the code for it? Also, if you're feeling ambitious, do you have a TCP/IP version?
SkippyFire
OK... will post below.
+1  A: 

As requested, here's the python server...

#!/usr/bin/python
import os 
import socket
print "  Loading Bindings..."
settings = {}
line = 0 
for each in open('/path/to/actions.txt', 'r'):
 line = line + 1
  each = each.rstrip()
  if each <> "":
    if each[0] <> '#':
      a = each.partition(':')
      if a[2]:
        settings[a[0]] = a[2]
      else:
        print "    Err @ line",line,":",each
print "  Starting Server...",
port = 12345
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.bind(("", port))
print "OK."
print "  Listening on port:", port
while True:
    datagram = s.recv(1024)
    if not datagram:
        break
    print "Rx Cmd:", datagram
    if settings.has_key(datagram):
      print "Launch:", settings[datagram]
      os.system(settings[datagram]+" &")
s.close()

The config file "actions.txt" uses the format "action-name:corresponding-shell-command" i.e.

# Hash denotes a comment
webroot:nautilus /var/www
ftp:filezilla
edit_homepage:gedit /var/www/homepage/htdocs/index.php

This code doesn't check the originating IP of the incoming UDP packets as I have it running on localhost, I am firewalled of from anyone else and checking would provide no protection against spoofing anyway.

I don't have time to rewrite it to use TCP/IP but Python is a language that's worth getting to know so if you really want that functionality I'll leave it to you to have a google for 'Python' and 'SOCK_STREAM'. It's probably not worth your trouble though, it's easier to configure your firewall so that no spoofed localhost packets can get through and modify the code to make sure it only listens to packets from the loopback.