chroot SFTP-only server with rsync and scp

Why?

You want to:

  • give users the possibility to store files on your server (ie. setup a file server)
  • use SSH (SFTP) because it is robust and secure
  • enable rsync and scp (ex. for backing up efficiently)

but you do not want to:

  • give users shell access
  • users to see other users' files
  • users to see the servers' files

Then, proceed reading.

Let's do it

What you should have done already:

  1. set up a Unix-like operating system (instructions will be for Debian GNU/Linux)
  2. set up an OpenSSH server and have it working smoothly
  3. acquired knowledge that all following commands must be run as root (su/sudo) :)

If you encounter any problems/ambiguities/errors, I'd be very happy if you contact me so that we can improve this article instantly.

define a Unix user group

All our clients will be part of that group. Let's call that group noshell.

groupadd noshell

Assume, we want user alice to be part of that group (we'll stick to that example). We then do:

usermod -g noshell alice

Attention: we change the users default group here. However, this should be no problem for users that have no shell access and only can access to their own files anyway.

restrict shell access

Now, one of the key restrictions is to deny shell access for users in noshell except for certain programs (such as rsync, scp etc.). There is a beautiful piece of software that does that already: a shell called rssh.

Install rssh (eventually, use your distribution-specific command):

aptitude install rssh

To allow the use of rsync, sftp and scp in a rssh session, uncomment the following lines in /etc/rssh.conf:

allowscp
allowsftp
allowrsync

Of course, you can uncomment more if you like but do not uncomment chrootpath.

Next, we need to make sure that every user in the group noshell has rssh set as shell:

Alternative a: set shells manually

Manually run the following command for all your users in noshell (verify the path to rssh), example:

chsh -s /usr/bin/rssh alice

Alternative b: set shells automatically

You can use this tool to to automatically set the shell for all users in a group.

The relevant section in the configuration file could look like this:

[user_shell]
check = yes
correct = yes
shell = /usr/bin/rssh

Note: if you run this tool regularly (using cron) accidentally changed shells will be reset and new user accounts will be configured automatically.

setting up a SFTP-only server

The server will chroot all clients that are in a specific user group.

In this example, we will put all chroots under /srv/sshd_chroots.

Add this to your /etc/sshd/sshd_config:

Match group noshell
  ChrootDirectory /srv/sshd_chroots/%u
  AuthorizedKeysFile /srv/sshd_chroots/%u/%h/.ssh/authorized_keys
  X11Forwarding no
  AllowTcpForwarding no

Create the chroots for your users

They must be owned by root and users must not have write permissions! Directories inside the chroot may be writable for the user.

Alternative a: manually

For every user in the group noshell, run:

mkdir --parents /srv/sshd_chroots/alice

Alternative b: automatically

You can use this tool to create the chroots for your users and to set/check the permissions on them.

The relevant part of the configuration could look like this:

# Variables: $u: login name, $h: users home, $g: users primary group name

[main]
home_path = /srv/sshd_chroots/$u
simulate = yes
limit_to_primary_group = yes
primary_group_name = noshell
minimum_users_count = 5

# check if the home exists
[home_existence]
check = yes
correct = yes # create missing

# check the permissions of the home
[home_permissions]
check = yes
correct = yes # set permissions
octal_permissions = 755

# check the owner of the home
[home_owner]
check = yes
correct = yes # set owner
owner = root

# check the group of the home
[home_group]
check = yes
correct = yes # set group
group = root

Note: if you call this tool regularly using cron, you sustainably ensure the existence and permissions of your users chroots.

Set the home directories for your users

Since every user has an own chroot, we set their home to / (yes, it is relative to the chroot directory).

Alternative a: manually

For every user in noshell, run:

usermod -d / alice

Alternative b: automatically

You can use this tool to automatically set the home for all users in a group.

The relevant part in the configuration file may look like this:

[user_home]
check = yes
correct = yes
home_path = /

Note: if you run this tool regularly (using cron), accidentally changed shells will be reset and new user accounts will be configured automatically.

provide the necessary files in the chroots

Puh… alright: we have a chrooting SFTP server with the corresponding chroot directories, a user group where all members have the homes set correctly and end up in a restricted shell upon login.

The very last thing is that we have to provide the binaries and config files in the chroot the user needs to run the permitted commands.

Alternative a: manually

You probably don't want to do this manually but here is how you would:

# define a shortcut to how we copy
# (because we want to preserve the path of a file relative to the chroot)
CP="rsync -RLa"

# copy rssh configuration to chroot
CP /etc/rssh.conf /srv/sshd_chroots/alice

# copy rssh binary to chroot:
CP /usr/bin/rssh /srv/sshd_chroots/alice

# check out what dynamically linked libraries rssh requires:
ldd /usr/bin/rssh

This will output something like:

linux-vdso.so.1 (0x00007fff433f1000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f0e7d8f4000)
/lib64/ld-linux-x86-64.so.2 (0x00007f0e7deb1000)

So we copy all the listed libraries to the chroot:

CP /lib64/ld-linux-x86-64.so.2 /srv/sshd_chroots/alice

Well, except for the copying of the configuration file, you have to repeat this for rsync and scp (sftp will be handled by the OpenSSH server itself).

Alternative b: automatically

Again: You can use this tool to copy all required files, binaries and libraries to the chroots.

The relevant part in the configuration file may look like this:

[home_files]
check = yes
correct = yes
file_list = /some/path/files_to_chroots.txt

[home_binaries_with_libs]
check = yes
correct = yes
file_list = /some/path/binaries_to_chroots.txt

With the contents of /some/path/binaries_to_chroots.txt:

/usr/bin/rsync
/usr/bin/rssh

and /some/path/files_to_chroots.txt:

/etc/rssh.conf

Done.

Making it convenient

As you may have guessed from the modifications you pasted into /etc/ssh/sshd_config, our setup will feature key-based authentication.

Therefore, we create a directory named .ssh in the users home directory, put a file callen authorized_keys into it and finally set permissions accordingly:

mkdir /srv/sshd_chroots/alice/.ssh
touch /srv/sshd_chroots/alice/.ssh/authorized_keys
chmod 700 /srv/sshd_chroots/alice/.ssh
chmod 600 /srv/sshd_chroots/alice/.ssh/authorized_keys

Now alice (or you) can add her public key to that file and she'll be able to login without a password prompt next time.