9

I currently have a WORKING SFTP login, using a private key for login and the user is chroot'ed into their home directory.

Goal: Keep the user chroot but allow WRITE access to the relative chroot directory, without having to specific any path or cd anywhere.

In other words, when the sftp user logs in, I don't want them to have to cd to another path in order to upload a file.

Due to the chroot directory requiring full root ownership (due to chroot design), I'm not certain if this is even possible.

In /etc/passwd:

sftpuser:x:1006:1006:,,,:/home/sftpuser:/bin/false

The users login pubkey is in:

/home/sftpuser/.ssh/authorized_keys

Chroot rules in sshd_confg:

Match User sftpuser
    ChrootDirectory /home/sftpuser
    ForceCommand internal-sftp
    AllowTCPForwarding no
    X11Forwarding no

Home dir permissions:

# ls -l /home/ |grep sftpuser
drwxr-xr-x 5 root      root     4096 May  4 11:24 sftpuser

# ls -l /home/sftpuser/
total 4
drwxrw-r-x 3 sftpuser sftpuser 4096 May  4 11:23 sftp_share

Example current workflow:

Connected to sftp.example.com.
sftp> ls
sftp_share

sftp> put testfile.txt
Uploading testfile.txt to /testfile.txt
remote open("/testfile.txt"): Permission denied

sftp> cd sftp_share

sftp> put testfile.txt
Uploading testfile.txt to /sftp_share/testfile.txt
testfile.txt

Desired workflow:

Connected to sftp.example.com.
sftp> put testfile.txt
Uploading testfile.txt to /testfile.txt
testfile.txt

Is there any way to allow the upload (put) to the default directory, without having to chdir first?

I do have flexibility to move these directories around. For example, the sftp chroot dir doesn't have to be in the users home directory, I can even change around the users home dir (but the user must still be able to use authorized_keys to login).

Please note: I do understand many SFTP clients, and the command line SFTP client allows for defining a relative path at login. That is out of scope for this question, I'm desiring this config be done server-side and the client simply just needs to login.

emmdee
  • 2,187
  • 12
  • 36
  • 60

3 Answers3

16

Hey @emmdee It took me forever to realize there is a simple and nice trick to that. You just have to make root the owner of a parent folder to where you wanna sFTP, then use force command to tell a specific user to load a specific DIR inside the CHROOT directory.

In your case in case you want /home/sftpuser to be the writtable directory, you will have to make root the owner of /home directory then go ahead and Force Command he /sftpuser as a landing directory for that user or a group.

Your /etc/ssh/sshd_conf will look like this:

Match User sftpuser
    ChrootDirectory /home
    ForceCommand internal-sftp -d /sftpuser
    AllowTCPForwarding no
    X11Forwarding no

Once that is done you have to give the right permissions as said earlier, the root should own the parent(chroot) directory /home while the user should own the final(-d) directory /sftpuser. I am goin to assume that you have an sFTP users group called sftpusers, if not; just ommit the group from the next commands or replace it with the users instead (root in the first and sftpusers in the second). As we are using -R in the command line for inheritance, you will have to start with the root ownership before the user ownership as follows:

sudo chown -R root:sftpusers /home

then for the user you can run:

sudo chown -R sftpuser:sftpusers /home/sftpuser

I am not certain that this is needed but you can always restart sshd service to make sure that all settings are reloaded again:

sudo systemctl restart sshd.service

This should work like a charm hopefully, I had a similar problem and the solution was there looking at me in the eyes most of the time.

Cheers!

African Idiot
  • 176
  • 1
  • 5
  • This is cool, but I don't think it exactly solves the question. Maybe it serves his use case, maybe not. I'm looking for this: Goal: Keep the user chroot but allow WRITE access to the relative chroot directory, without having to specific any path or cd anywhere. I've seen this done on a server I'm client on, trying to figure out how to do it. – meelash Feb 18 '20 at 22:07
  • May be you did not see it in my answer above, when specifying Force command, add an argument for a landing directory using -d like `ForceCommand internal-sftp -d /sftp_share` and the SFTP user will not have to cd anywhere, just when they SFTP they land straight in sftp_share folder which is 'writtable' to them. It is by design that SFTP user in Linux cannot write on the chroot. – African Idiot Feb 20 '20 at 06:56
  • 1
    @AfricanIdiot but if they do `cd /` won't we be back right to sqaure one? – Andrew Savinykh Mar 02 '20 at 03:16
  • Well, as @Joe suggested later, you will have to use Linux permissions to disable that property. For every sFTP user's home directory make the permissions 600 so that only the chrooted user can read and write that directory, no executing stuff also to avoid nasty behaviors. – African Idiot Mar 13 '20 at 09:24
  • [Follow-up question](https://serverfault.com/questions/1015956/how-to-specify-user-home-in-sshd-config) – leonheess May 06 '20 at 16:01
  • I think people are better off making a writeable directory under the chroot, as described in https://serverfault.com/questions/497011/sftp-user-cant-edit-or-create-files – Herman J. Radtke III Oct 10 '20 at 23:38
  • 1
    You'd still be able to see all the other home directories, even if permissions prevent you from changing into them or listing their contents. If your SFTP server is used for file upload/download with clients, that could be a problem: you might not want your client to know who your other clients are. – Frans Mar 14 '22 at 18:16
  • Just saved me a couple hrs! – dustbuster Jun 29 '23 at 20:18
6

I know its been a couple of years, but this post got me 98% of the way there. after searching for a long time.

To make it so that I can have users chrooted and automatically get them into their own directory with write permissions I used the method suggested by @African Idiot, and also made the original chroot directory owned by root user and the FTP group.

But the key for me to keep them in that folder and not be able to .. and attempt to browse other users' folders, was to make the folder chmod 710 (rwx--x---) so that the ftp group had execute to do the internal-sftp -d /%u (i.e. cd into the users subdirectory) and now if I attempt to cd .. into the upper directory, because there's no read for anyone but root, it fails.

drwx--x---  6 root        sftpusers   uarch  6 Feb 19 15:34 ./
drwxr-xr-x  5 root        root        uarch  5 Feb  4 09:13 ../
drwxrwx---  3 anotheruser anotheruser uarch  3 Feb 24 15:34 anotheruser/
drwxrwx---  3 ftpuser     ftpuser     uarch 12 Feb 25 10:55 ftpuser/

Hope this helps someone else too.

Dave M
  • 4,514
  • 22
  • 31
  • 30
Joe
  • 61
  • 1
  • 2
  • [Follow-up question](https://serverfault.com/questions/1015956/how-to-specify-user-home-in-sshd-config) – leonheess May 06 '20 at 16:01
  • 1
    You are correct, in my case, because the sFTP user only needs to paste a file or two in my settings, I have given the directories 610 as even the owners are not going to execute anything within the directories. – African Idiot May 21 '20 at 14:31
1

The permissions 'trick' detailed above, where one sets execute permissions exclusively on the chroot directory, is insecure. This allows a user to change directory and work with the content of another website when they can guess a valid username, although they can't list the user directories in their root. This also causes a problem with many SFTP clients that either try to switch to '/' automatically after login, or WinSCP which remembers the last working directory. In the WinSCP case the working directory is automatically remembered at next login so the user has the concept that they were locked out after attempting to navigate to the parent directory.

My solution uses concept from above, whereby user accounts in /etc/passwd refer to the /var/www/www.redacted.com webserver directory. This works with Pure-FTPD chroot jails and Apache, which continue to refer to the original location. SSH however gets configured to override the chroot directory to a jail location (/home/users/%u) where a 'website' sub directory is bind mounted to the actual webserver directory.

Sample user account:

   blahweb:x:50040:33:Blah FTP Web Site User:/var/www/www.redacted.com:/sbin/nologin
       ^^^^^^^^^  ^^^^    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^                ^^^^^^^^^^^^^^ = static

PS: Ensure /etc/shells contains '/sbin/nologin', user ID for group www-data is 33.

SFTP configuration in /etc/ssh/sshd_config:

   Match Group www-data
       X11Forwarding no
       AllowTcpForwarding no
       ChrootDirectory /home/users/%u
       ForceCommand internal-sftp -u 0007 -d /website

I then run the following script on an hourly basis:

[root@webserver ~]# cat /etc/cron.hourly/sftp-create-chroot
#!/bin/sh

basedir='/home/users';
webdir='\/var\/www\/';

# Unmount and then cleanup all mount points (removes old sites):
grep -P " $basedir/\S+web/website " /proc/mounts | cut -d' ' -f2 | while read mountpoint; do
  umount $mountpoint;
done
for dir in $basedir/*; do
  [ `grep -Pc " $dir/" /proc/mounts` -eq 0 ] && find $dir -depth -type d -empty -delete;
done

# Create SFTP user directories and mount websites:
grep -P "^\S+web:x:50\d{3}:33:.*:$webdir" /etc/passwd | perl -pe "s/^([^:]+):.*:($webdir.\S+):.*/\1\t\2/g" | while read user home; do
  if [ ! -d $basedir/$user ]; then
    mkdir -p -m 000 $basedir/$user/website;
    chmod 555 $basedir/$user;
  fi
  [ `grep -Pc " $basedir/$user/website " /proc/mounts` -eq 0 ] && mount --bind $home $basedir/$user/website;
done

SSH is essentially configured to jail SFTP sessions in to /home/users/blahweb, when the user 'blahweb' logins in. He only has read permissions to this directory itself and won't be able to interact with the 'website' directory within it unless it's mounted and his user ID or group then has access to the actual website directory. The script unmounts all active mounts and cleans up old user directories whenever it runs, also has logic in it to avoid stamping on mounts that are active when the script is run.

Script in simple markup, hopefully making it easier to read:

enter image description here

Directory permissions when everything works as expected:

[root@webserver ~]# dir -a /home/users/blahweb
total 4
dr-xr-xr-x  3 root    root       21 Apr 27 13:17 .
drwxr-xr-x 31 root    root     4096 Apr 27 13:17 ..
drwxrwx---  2 blahweb www-data   30 Apr 27 13:05 website

Directory permissions when 'mount --bind' fails:

[root@webserver ~]# dir -a /home/users/blahweb
total 4
dr-xr-xr-x  3 root  root   21 Apr 27 13:17 .
drwxr-xr-x 31 root  root 4096 Apr 27 13:17 ..
d---------  2 root  root    6 Apr 27 13:17 website

User experience in WinSCP:

enter image description here