EN NL

Martijn's website

Creating a chrooted sftp-only ssh user

I wanted to set up an easy and secure way to use the storage on my server as a personal “cloud storage”. I also wanted it to be isolated from everything else on the server. To do this, I used a chrooted sftp-only ssh user. There are probably other ways to do something similar, but I went for this option because I didn’t need to install additional packages.

Prerequisites

You only need a Linux server with ssh. I’m using Ubuntu Server 18.04 LTS with OpenSSH, but this should work on any modern Linux distribution.

Setting up our user

We’re going to need a separate user on our server. I’ll be calling mine sftponly, but you can use whichever name you want.

$ sudo adduser --disabled-password --shell /usr/sbin/nologin sftponly

This creates a user without shell access and without a password. It can only login using ssh key-based authentication.

This is also the moment to add your ssh public key to the authorized_keys file of the newly created user:

$ mkdir /home/sftponly/.ssh
$ echo "YOUR PUBLIC KEY HERE" >> /home/sftponly/.ssh/authorized_keys

We also create a path for the chroot:

$ mkdir -p /srv/sftp/chroot

Make sure this directory is owned by root and not writeable by any other group or user:

$ ll /srv/sftp
drwxr-xr-x  3 root     root     4096 jan 29 21:46 chroot

This is required by the ChrootDirectory option for sshd that we will be using. From the sshd_config(5) man page:

ChrootDirectory

Specifies the pathname of a directory to chroot(2) to after authentication. At session startup sshd(8) checks that all components of the pathname are root-owned directories which are not writable by any other user or group. After the chroot, sshd(8) changes the working directory to the user’s home directory. Arguments to ChrootDirectory accept the tokens described in the TOKENS section.

If sshd gives any bad ownership or modes for chroot directory errors, double check that you have set up the ownership correctly according to the sshd_config(5). Note that you may also get broken pipe errors if the permissions are incorrect.

Editing our sshd configuration

Next we have to edit our sshd_config. Find the line

Subsystem sftp /usr/lib/openssh/sftp-server

and change it into

Subsystem sftp internal-sftp

This makes it possible to use the ChrootDirectory option without any further configuration.

And then we add the last part of our configuration. For our specific user, we set up the chroot directory and force sftp. For security, TCP forwarding and ssh agent forwarding is also disabled.

Match User sftponly
    ChrootDirectory /srv/sftp/chroot
    ForceCommand internal-sftp
    AllowTcpForwarding no
    AllowAgentForwarding no

If you want the user to only have read-only access, use ForceCommand internal-sftp -R instead.

Results

Now if we just try to ssh we’ll be kindly reminded that we can only use sftp:

$ ssh sftponly@example.com
This service allows sftp connections only.

And sftp obviously works like charm:

$ sftp sftponly@example.com
Connected to example.com
sftp>

And we can see that we’re working in a chroot:

sftp> pwd
Remote working directory: /