PHP shell_exec, sudo, and remote servers
When working with legacy, or even current software, it's not unusual to have a few scripts dedicated to accomplishing tasks. Often these can be things that simply rsync resources across nodes of a server, help migrate data between different data sources, back up code or data, or really anything you can think of.
It's easy enough for a developer to get onto a machine and run their tools from a command line interface (CLI), but sometimes you don't want to be bothered by emails during the day when you're coding. For example let's say you've got a few scripts that connects to multiple servers and runs a command on each. It's easy to run a bash script from the CLI, but you'd save yourself grief if you exposed the tool to whoever is always emailing you asking for the task to be done!
So, you think to yourself, Ah! I can write a quick script in PHP to do this! And you'd be right. Imagine something like this:
<?php $logfile = '/tmp/util.log'; $scriptpath = dirname(__FILE__) . '/script'; if ($_SERVER['REQUEST_METHOD'] == 'POST'): //code to do param checking and such $exe = $scriptpath . $someparameters . ' >> ' . $logfile . ' 2>&1'; $result = shell_exec($exe) if (is_null(result)): //handle bad result else: //display HTML form and such
And you've got a simple shell script that does something like this:
#!/bin/bash source /some/path/to/a/listof/servers/to/connnect/to.sh for host in $SERVERS do ssh $host "cmd to do for a param $1" echo "Done! $1" done
While running the above script from the CLI will work, but should you attempt to run it from a server like apache or nginx, then you're going to hit error:
Could not create directory '/var/www/.ssh'. Host key verification failed. Done! <Whatever Params>
Being the good developer that you are, you realize that of course the web server can't ssh! It doesn't have an ssh folder or keys like your user. Let's assume you run as the root user when you do your CLI work (though you shouldn't) so you decide that to run your script you simply need to sudo. Okay...
$scriptpath = 'sudo sh ' . dirname(__FILE__) . '/script';
And when you go back to refresh the page what happens?
sudo: sorry, you must have a tty to run sudo
Well darn. So now you think back to yourself and realize that the
server doesn't really have a shell to work with. And sudo wants one
of those. The simplest way around this is to disable the requiretty
for the user. This is easily done via
visudo and updating your web
#Give permission to apache to run sh as root apache ALL=NOPASSWD: /bin/sh #Don't require a tty for apache Defaults:apache !requiretty
Once you've done this, then you'll be able to run your shell script. Here are some caveats to this though.
- You've given permission to apache to run a shell as root without restriction
- apache no longer needs a terminal
This is bad. However, being the moderately inteligent developer you
are, you know that you can restrict the allowed commands. So you
update the sudoers file again with a quick
apache ALL=NOPASSWD: /path/to/your/script
But until you update your PHP code you'll get the
sudo: no tty present and no askpass program specified
error message. Why? Because you're calling
//no sh! $scriptpath = 'sudo ' . dirname(__FILE__) . '/script';
Make sure that your script file is
chmod +x executable, then you're
much safer. In addition, if you use safe_mode in php, you should
safe_mode_exec_dir in your .ini file appropriately.
Depending on your script, you may have a limited set of parameters. For
example, say you're talking to
/etc/init.d/nginx and you want to be
able to restart the service from a form you built. So your form submits
start. If that's the
case, you can lock things down a bit more by using the wildcards found
in the sudo manual or you can simply list each command individually.
Also, if you do open up your scripts as utilities, be mindful of who can access the pages. Lock them down with at least basic HTTP authentication, if possible, only expose such scripts to roles in the company that need that usage. Always follow the principle of least privilege and audit your systems regularly! When you grant a user the power to use your CLI tools, you give them a piece of your responsibility and take on more at the same time. Developers should strive to be lazy (in this case, reducing your effort in dealing with emails and distractions), but it's important to keep in mind that security and safety come first.