Linux – Managing Linux Directory Permissions & SFTP

linuxpermissionssetgidsftp

Good morning;
I have a RHEL 5.7 web server configured to allow SSH/SFTP only by specific groups. I'd like for content managers to upload content to their respective directories and have that content inherit the user/group ownership of the directory regardless of upload method or application. For example:

  • John is in group "web" for SSH/SFTP rights and "finance" for directory permissions, and uploads to directory "webstuff" via SFTP.
  • Directory "webstuff" has permissions of "2760" (rwxrws—), and ownership of "apache:finance".

If John uploads an update to an existing file in "webstuff", the ownership of the file stays at "apache:finance".
If John uploads a new file to "webstuff", the ownership of the file is "john:finance".

My desire is to have any file from John uploaded to "webstuff" to change to the directory's owner. I've tried with setuid and setgid both set, but the user-ownership didn't take.
I've seen mentions on ServerFault of using ACL's, or a chrooted jail for SFTP but I have yet to configure and test them, and I don't know if they're a viable solution (they could be, I just don't know because I've never done either). Any thoughts and assistance would be greatly appreciated.

Best Answer

The problem is that sftp runs as the user's id -- first, the sftp client ssh's into the target host as the given user, then runs sftp-server. Since sftp-server is running as a regular user, it has no way to "give away" a file (change owner of a file).

However, if you are able to use scp, and assign a key pair to each user, you can get around this. This involves adding a user's key to root's ~/.ssh/authorized_keys file, with a "command=" parameter to force it to run a script that sanitizes and alters the arguments of the server-side scp program. I've used this technique before to set up an anonymous scp dropbox that allowed anyone to submit a file, and ensure that no one could retrieve submitted files and also prevent overwrites.

If you are open to this technique, let me know and I'll update this post with a quick recipe.

Edit: Based on comments, here's my recipe that I originally used to set up an anonymous dropbox. But it should work for this purpose also.

If you want John to write to upload files to /var/www/webstuff, and have any files written to it owned by the user id apache, then:

1) Generate an ssh key pair:

ssh-keygen -t rsa -f john-scp-key  

2) Edit the public key file "john-scp-key.pub", and insert a "command=" statement at the beginning as follows:

command="/usr/local/bin/strictscp ^/var/www/webstuff" ssh-rsa AAAA..... (reset
of key follows)  

3) Add the contents of john-scp-key.pub to ~apache/.ssh/authorized_keys file (create the file if it doesn't exist, making sure that both the .ssh directory and authorized_keys are owned by apache, and not writable by either group or world)

4) Create the script /usr/local/bin/strictscp as follows:

#!/bin/sh
read cmd arg1 arg2 <<EOT
$SSH_ORIGINAL_COMMAND
EOT
if [ "$cmd" = scp -a "$arg1" = -t ] && echo "$arg2" |grep "$1" >/dev/null 2>&1
then
    eval $cmd $arg1 $arg2
fi

5) Now give the private key file john-scp-key to John, and have him copy files to your web server like this:

scp -i john-scp-key some_file.html apache@yourserver:/var/www/webstuff/

The way this works is when you use scp to copy a file to a server, the scp client uses ssh to execute "scp -t /path/name" on the server. When using a key pair, you can override the command that gets executed and force a specific command to run when that key is used. The original command that was requested, along with its arguments, is in the environment variable "$SSH_ORIGINAL_COMMAND". So we point COMMAND= to a script that checks for allowed arguments (in this case, it verifies that the third argument is the specific directory we want to write stuff to), so that this key is only good for its intended purpose and can't be used to run arbitrary commands on the server.

You can lock down the strictscp script a bit further by checking if the target file exists or not, and you can also add a chgrp command to the end to set the file's group ownership (assuming that apache is a member of that group).

Hope this helps -- if you have any problems with it, let me know and I'll see if I can improve this answer further.