I am working on a server where more than one user has ssh access, and there is more than one project. Only one user has root access, other users are restricted to specific projects.
So, here is how the permissions are configured. All files have group read and write access.
project_A/* is in group_A
project_A/sites/sitename/files/* is in group www-data
project_B/* is in group_B
project_B/sites/sitename/files/* is in group www-data
www-data user is NOT in any other group. Only the files directory can be written on by the www-data user.
So far, so good.
Problem:
Each time user_1 does drush up or drush dl, the newly downloaded or overridden files in project_A get owner=user_1 and group=user_1. The owner is not a problem, but group=user_1 means that other users in group_A can no longer edit those files.
Solution:
It would be very sweet if there was a solution to automatically chgrp those added or updated files back to project_A (or a group name specified in a settings file) after drush dl or drush up.
Any idea how to implement this in drush? Or should this be done outside of drush, in custom shell scripts?
Thanks.
Comments
Comment #1
donquixote CreditAttribution: donquixote commentedThinking about it, it would be even better to have
project_A/sites/sitename/files/* in group www-data-project_A,
and the www-data user being a member of this group. Or the owner should be www-data, and the group should be group_A ..
Problem is that every operation of any user can mess up groups and owners. Not just drush, but also normal file copy, ftp upload, web upload etc.
Comment #2
donquixote CreditAttribution: donquixote commentedSee also #439480: run commands as the web server user.
Comment #3
moshe weitzman CreditAttribution: moshe weitzman commentedFirst, its trivial to use a POST hook to run code after any dl or updatecode command. See the API docs. Next, you might consider a bash alias for drush such as
su - wwwdata -c drush
. I haven't thought much about that. I think perms is pretty site specific and the best we're gonna do is point people to blog posts and such where folks are sharing their own code.Comment #4
Slovak CreditAttribution: Slovak commentedProblem:
Each time user_1 does drush up or drush dl, the newly downloaded or overridden files in project_A get owner=user_1 and group=user_1. The owner is not a problem, but group=user_1 means that other users in group_A can no longer edit those files.
This can be done with permissions by setting the suid bit on directories. Assuming you are in the directory above your projects directory:
find ./project_A -type d -exec chmod g+s {} \;
All files and directories made after this within project_A will have the same group as project_A.
You'll have to set the user:group on the site/files directory separately, as www-data needs to be able to write to it. I would suggest setting the group to www-data and adding your users into that group. Unfortunately, that opens access for other users into other site/files directories as well - though they are technically exposed by Drupal to the public.
I've tried a similar approach to setting up another group www-data-project_A and adding www-data user to it. The problem is that the web server will not respect the suid on the files directory, therefore any new files created through the web site (i.e. uploaded file) will be www-data:www-data which will make it inaccessible to the people working on the project. Some will say that the sites/files directory should be off limits to developers, but I haven't found that to be practical either (especially since some modules like CiviCRM place their custom and template files within sites/files directory).
I am still looking for the most optimal solution.
Comment #5
Anonymous (not verified) CreditAttribution: Anonymous commentedThis could be solved by removing the call to rename in drush_move_dir(). If the rename doesn't execute, drush_move_dir() falls back to using drush_copy_dir and drush_delete_dir.
The copy command respects the SETGID bit (chmod g+s) on the parent directory, thus changing the group id on the new files and directories. The move command leaves the group id as it was in the temporary directory.
Comment #6
jm.federico CreditAttribution: jm.federico commentedI'm scaling this to major, security perms are crucial in multi-user environments.
I like @g.idema idea, removing the call to drush_move_dir solves the problems.
Probably this can be made a parameter, so that we can instruct Drush to use copy/delete instead of move.
Somthing like this
drush @alias dl --copy alias
Wouldn't know how to do that, :( can't provide a patch.
Cheers
Comment #7
greg.1.anderson CreditAttribution: greg.1.anderson commentedSee #3; patches welcome, but file perms are site-specific, and therefore a bit beyond the scope of drush core. Un-setting 'major'.
Comment #8
weseze CreditAttribution: weseze commentedI'm not very experienced in *nix file permissions, owners and groups. But I don't think that drush should ever change the original owner and group of any file or any directory that it updates.
If my server tools have setup proper permissions, created users and groups and so on, then Drush shouldn't change any of that, regardless of what user I'm currently logged in as.
If I understand #5 correctly then it is possible to fix this and it really is a bug in Drush and not a problem with us - the user - not understanding owners and groups or not having it setup correctly.
Just my opinion on the matter ;)
Comment #9
blackspiraldancer CreditAttribution: blackspiraldancer commentedTo me that's a major issue. I had to put an alias to
chown www-data:www-data -R /var/www/whatever
because all of the drush commands were resetting (as expected) user and group to root.Isn't there any way to accept in the command line a valid system user and run drush with a (eg)
su -m www-data -c <command>
prefix?Comment #10
moshe weitzman CreditAttribution: moshe weitzman commented@blackspiraldancer - I answered your question in #3. Use a bash alias for drush.
Comment #11
anarcat CreditAttribution: anarcat commentedI do not think drush should mess around with permissions. It makes drush less portable, and less understandable. How will we know which group to use? Which permission?
Now, you are asking how *you* can do this, and that's actually fairly simple. Aegir does this all the time.
You just need to drop a foo.drush.inc file in your .drush directory with the following:
or similar.
If this needs to be fixed at all, it's only that files should be downloaded in place instead of a tmp directory, so it follows the setuid settings. Running chmod or chown is just bound to fail.
Comment #12
anarcat CreditAttribution: anarcat commentedI have opened the feature request #1168812: Respect filesystem permissions about this, so that files are downloaded in place.
Comment #13
jonhattanPer #4 and #1168812-6: Respect filesystem permissions. Group is respected as far as you configure parent directories with g+s, that's out of drush scope.
@Skavare you can set webserver's user umask to 002. Note also that drupal 7 does a chmod of files and dirs, by default to 0775 and 0664. See http://api.drupal.org/api/drupal/includes--file.inc/function/drupal_chmod/7
Comment #14
JordanMagnuson CreditAttribution: JordanMagnuson commentedCould someone help me out with this? I'm incredibly confused/frustrated.
I'm a pretty experienced web developer, with limited sysadmin/Linux experience.
I used to use drush as root, and then discovered that I shouldn't do that. So I set up a user for myself ("magjor") and added this user to the www-data group.
Now when I use drush as magjor to download modules etc (via "dl"), the module folders end up owned by magjor:magjor, instead of magjor:www-data, which means that Apache can not read the files.
I've been looking around for the last couple of hours trying to figure out how best to deal with this, without a whole lot of success. This thread is somewhat helpful, but it seems that people are throwing around a variety of solutions, none of which are explained fully, and all of which seem to have various "issues."
I'd rather not use the bash alias solution, as it seems like a pain to enter a password every time I do something with drush. Seems preferable to have drush just set permissions manually after a dl, as per #11, but I don't understand how to implement this solution.
I've created a file called group_chown.drush.inc and placed it in /home/magjor/.drush . Inside the file I have:
But this doesn't seem to do anything. I'm sure I'm doing something wrong.
Any help is much appreciated!
Comment #15
greg.1.anderson CreditAttribution: greg.1.anderson commentedTo patch in after pm-download, you must name your hook after the long command name (pm-download), not its shortcut (dl). In other words, you want:
Immediately after you create your group_chown.drush.inc file, it won't work; you need to run
drush cc drush
to clear the Drush commandfile cache before your new commandfile is recognized. After you do that, your code will run, and you could put in the code from #14, and it would work.That is, however, perhaps not the best way, as the only arguments you get in the post pm-download hook is the list of project names downloaded. A better hook to implement is drush_pm_post_download, which gives you the full $request and $release data structures. $request will tell you where Drush placed each download file:
The 'drush_print' line shows you the output for the chgrp operation; this should be empty if everything works okay. If you get 'operation not permitted', note that you need to be a member of the target group in order for this to work.
Marking this support request as 'fixed'; we could potentially put the example above in the examples folder, or perhaps include it with the related issue #1343892: Add a `drush topic` entry for best practice in permissions using drush between production and development.
Comment #16
greg.1.anderson CreditAttribution: greg.1.anderson commentedComment #17
greg.1.anderson CreditAttribution: greg.1.anderson commentedI should also add, though, that if you want to solve your permissions issues after dl per #15, you might also want to make similar adjustments in a pm_post_update hook, as pm-updatecode does not call pm-download to update projects.
Comment #18
greg.1.anderson CreditAttribution: greg.1.anderson commentedSee also: #990812: Add a "permissions" subcommand to fix/set all file permissions
Comment #19
JordanMagnuson CreditAttribution: JordanMagnuson commentedThanks a lot for the help Greg! It would definitely be great to have more documentation regarding permissions and Drush. Drush itself works like a charm, but the permissions situation has made everything fairly confusing (to my mind).
Your example helped immensely. After messing around a bit further, I've created a post_dl hook that changes permissions as well as owner for downloaded files. It seems to work as desired (see below: does it look okay?).
As per your suggestion, I tried creating function owner_perms_drush_pm_post_update($request, $release) as well (in the same file), and pasting the same code into it, but it doesn't seem to be doing the trick (permissions are not changed after performing pm-update). Does that hook need to be handled differently somehow?
Here's my owner_perms_drush_pm_post_download():
Comment #20
greg.1.anderson CreditAttribution: greg.1.anderson commentedThe post-update hook should be declared like this:
Unfortunately, the post-update hook is not very useful for this purpose, as it does not pass you the location of the updated project. That information must be recovered from the DRUSH_PM_UPDATED context. This context contains an array with info on all of the projects that were updated; unfortunately, the items in this array are not keyed by project name, so you have to search through the whole record to find the one you want. If you're going to do that, you might as well just process all of the updated projects in the post-pm-updatecode hook:
Insert your code there, and it should work just fine. Finally,
drush_log(dt('message'), 'error');
is lazy debug code. Don't follow my example in your actual hooks; always abort on errors withreturn drush_set_error('ERROR_CODE_YOU_INVENT', dt('message'));
.Comment #21
JordanMagnuson CreditAttribution: JordanMagnuson commentedAh, thank you--you've been a miracle-worker Greg! Here's the final code I'm using, which has made my life much, much happier.
(The only thing I'm still a bit confused about is drush_print(implode("\n", drush_shell_exec_output())); --> is that line necessary as I have put it in, below, or can I leave it out in my use case?)
Comment #22
greg.1.anderson CreditAttribution: greg.1.anderson commenteddrush_print(implode("\n", drush_shell_exec_output()));
dumps the output of the previous command, which is usually empty. Any reported error messages will show up here, so I think it's useful to leave it in, just in case.Thanks for the hook implementations; those might make a good addition to the 'examples' folder. Already tracking this issue from #1343892: Add a `drush topic` entry for best practice in permissions using drush between production and development.
Comment #23
JordanMagnuson CreditAttribution: JordanMagnuson commentedJust changing title to be more clear.
Comment #25
ShaunDychko CreditAttribution: ShaunDychko commentedThanks a lot for sharing this Jordan. Another piece of the puzzle is to put the code from #21 in a file named
owner_perms.drush.inc
in the~/.drush
directory.Comment #26
Slovak CreditAttribution: Slovak commentedAssuming you are dl into project_A/modules for example. Make sure that this modules directory has permissions g+s (2775) and group=project_A
Add the desired users to project_A group.
drush dl as desired, no need for chgrp.
* Note that setting the setgid permission on a directory only affects the group ID of new files and subdirectories created after the setgid bit is set, and is not applied to existing entities.
Comment #27
ShaunDychko CreditAttribution: ShaunDychko commentedHere's an update to #21 that adds support for updating Drupal core. It search for all
sites/*/files
directories and sets770
directory permissions and660
files permissions recursively. This patch also removes the$results
parameter from the hook implementation since it isn't necessary according to http://api.drush.org/api/drush/docs%21drush.api.php/function/drush_hook_..., and it was throwing an error message.note also that a more complicated solution to this issue it being worked on here: http://drupal.org/node/990812