Drush is pretty cool, and super powerful. It can allow you to do a number of things to save time in your day, such as creating scripts and writing up aliases, in addition to being used for its more intended purpose, which is managing your Drupal install. One feature that I use quite a bit is the drush uli command to reset my password (either because I’m lazy or I actually can’t remember my password), and this helps me out of my bind. One of the issues I have though is that, in addition to being super powerful, drush will allow anyone to run it, essentially giving anyone with server access administrator access as well.
In a personal install or in your development sandbox this isn’t an issue most of the time, but we like to educate our clients and allow them some access to make changes to things, and just like you wouldn’t want to give them root access to the server, you wouldn’t want to give them unbridled access to their Drupal install. Sure there are some that have access to everything, and as we educate our customers we allow them to do more and more. Mostly though, it’s nice to have a log file of who is resetting a password or making updates to a site, and when. Using a combination of sudo access and settings in a policy file gives us just the right amount of control over what a user can and can’t do.
Getting started creating a policy file is a bit tough because aside from the documentation that you will find in drush topic policy there isn’t much out there to help. We’ll attempt to remedy that here by walking through the different parts and policies that can be set to give you a more stable install.
The first thing you must decide is where would you like to keep the policy file, and this really depends on your purpose for creating the policy file in the first place. If you are just wanting to prevent yourself from doing something crazy by accident, or if there is special functionality for you, you can add the policy to the .drush directory inside your home directory.
If you are wanting to have a different policy for different sites or Drupal installs, you might think about adding the policy file to the /sites/all/drush directory; this also makes it handy for versioning the file. To have a global policy file that will work for all sites and users on a server, your best bet is to put the policy file in the /usr/share/drush/command directory.
Each of these directories is valid though they aren’t inclusive, meaning that if you have a file in your /sites/all/drush directory, the settings here will overwrite the settings you may have in your ~/.drush directory. Likewise, the policy file in the /usr/share/drush/command directory will overwrite any other policy file, which is what we want, so someone can’t circumvent restrictions by creating their own policy.drush.inc file and adding it to their home directory.
The next thing we need to decide is what actually needs to be put in the policy file. The example file shows a couple of examples, but they aren’t really practical for real-world use.
The example file shows a way to require the user to be running as root prior to running a command, which is helpful if you would like users to sudo a command. Sudo will keep a log of commands that are run, and who ran them.
For our sample, we will take the example drush_policy_make_me_a_sandwich_validate() function and rename it to _needs_root().
<?php
function _needs_root() {
if (drush_is_windows()) {
// $name = getenv('USERNAME');
// TODO: implement check for elevated process using w32api
// as sudo is not available for Windows
// http://php.net/manual/en/book.w32api.php
// http://social.msdn.microsoft.com/Forums/en/clr/thread/0957c58c-b30b-4972-a319-015df11b427d
}
else {
$name = posix_getpwuid(posix_geteuid());
if ($name['name'] !== 'root') {
return drush_set_error('NEED_ROOT', dt('You cannot run this command as the current user.'));
}
}
}
?>This now allows us to simply call _needs_root() without having to duplicate this code for each command we would like to check.
Using the same idea that is in the example file, we can say that anyone wanting to run the user-login command should use sudo to log this. Using the drush_hook_COMMAND_validate() we can now build:
<?php
function drush_policy_user_login_validate() {
_needs_root();
}
?>If the _needs_root function doesn’t set an error, drush will allow the user-login function to be run. Running drush uli as a regular user, you would see:
You cannot run this command as the current user. [error]
Sometimes you don’t want a command to ever be run. Something like the sql-drop command would be bad if it were run on a production server. We can check for that the same way:
<?php
function drush_policy_sql_drop_validate() {
return drush_set_error('BLACKLISTED', dt('This command is not allowed here.'));
}
?>This could get to be a pretty long file if we need to write a separate _validate function for every command that we want to have either run as sudo or not at all. There isn’t a really nice drush_policy_ALL_COMMANDS_validate function, but we can use the _init() function to make our lives just a bit easier.
Using a whitelist/blacklist approach, we can allow certain commands to be run by anyone (help, status) and also block certain commands from everybody (sql-drop).
<?php
// Create a central spot for the lists
function _get_lists() {
return array(
// We don't want anyone ever to drop the database.
'blacklist' => array(
'sql-drop',
),
// These commands can be run by anyone on the system.
'whitelist' => array(
'help',
'status',
),
);
}
?>This list could be much longer, but we are going with the approach here that all commands that are not in either of these two lists must be run with root privileges. Again, for this we will use the policy_drush_init() function:
<?php
function policy_drush_init() {
$current = drush_get_command();
$lists = _get_lists();
if (in_array($current['command'], $lists['blacklist'])) {
_blacklisted();
}
// it's not blacklisted, if it's not whitelisted it must need root.
else if (!in_array($current['command'], $lists['whitelist'])) {
_needs_root();
}
}
?>The drush_get_command() function returns an array with the command that was run, along with quite a few other parameters. The only one we need for our purposes is the [‘command’] variable.
We then check to see if the command is blacklisted and if so we call _blacklisted(), which is just a function that returns the same error we had above for the drush_policy_sql_dump_validate function.
This is a pretty simple way of restricting access to commands, but it might be a bit overkill for some people. Some may just want to add a simple function to say you are only able to run user-login on a user that is not the admin user.
<?php
function drush_policy_user_login_validate() {
$args = drush_get_arguments();
$admin = user_load(1);
if (!isset($args[1]) || $args[1] == $admin->name || $args[1] == '') {
return drush_set_error('NOT_ALLOWED', dt('You are not allowed to login as the admin user.'));
}
}
?>Whether you are looking to make a really simple policy or something really complex, drush allows you to do it through the policy.drush.inc file. We are always interested in how others use the policy file, so please tell us a little about how you use it.

