SFTP chroot

From ArchWiki

OpenSSH 4.9+ includes a built-in chroot for SFTP, but requires a few tweaks to the normal install.


Install and configure OpenSSH. Once running, make sure sftp-server has been set correctly:

Subsystem sftp /usr/lib/ssh/sftp-server

Access files with sftp or SSHFS. Many standard FTP clients should work as well.


Setup the filesystem

  • Readers may select a file access scheme on their own. For example, optionally create a subdirectory for an incoming (writable) space and/or a read-only space. This need not be done directly under /srv/ssh/jail - it can be accomplished on the live partition which will be mounted via a bind mount as well.
  • It is also possible chrooting into /home directory thus skipping the usage of bind, however the desired user home directory should be owned by root:
# chown root:root /home/<username>
# chmod 0755 /home/<username>

Bind mount the live filesystem to be shared to this directory. In this example, /mnt/data/share is to be used, owned by user root and has octal permissions of 755:

# chown root:root /mnt/data/share
# chmod 755 /mnt/data/share
# mkdir -p /srv/ssh/jail
# mount -o bind /mnt/data/share /srv/ssh/jail

Add entries to fstab to make the bind mount survive on a reboot:

/mnt/data/share /srv/ssh/jail  none   bind   0   0

Create an unprivileged user

Note: You do not need to create a group, it is possible to use Match User instead of Match Group.

Create the sftponly user group:

# groupadd sftponly 

Create a user who is a member of the sftponly group and has shell login access denied:

# useradd -G sftponly -s /usr/bin/nologin -d /srv/ssh/jail username
Note: SSH authentication by password (not recommended) only works if users have access to a login shell (i.e. -s /usr/bin/nologin not set).

Set a (complex) password to prevent account is locked error (may appear even with key authentication):

# passwd username

Configure OpenSSH

Note: You may want to use Match User instead of Match Group as been given in the previous step.
Subsystem sftp /usr/lib/ssh/sftp-server

Match Group sftponly
  ChrootDirectory %h
  ForceCommand internal-sftp
  AllowTcpForwarding no
  X11Forwarding no
  PasswordAuthentication no

Restart sshd.service to confirm the changes.

Fixing path for authorized_keys

Tip: Use the debug mode of OpenSSH on the client and server in case of (pre)auth error(s).

With the standard path of AuthorizedKeysFile, the SSH keys authentication will fail for chrooted-users. To fix this, append a root-owned directory on AuthorizedKeysFile to /etc/openssh/sshd_config e.g. /etc/ssh/authorized_keys, as example:

AuthorizedKeysFile /etc/ssh/authorized_keys/%u .ssh/authorized_keys
PermitRootLogin no
PasswordAuthentication no
PermitEmptyPasswords no
Subsystem sftp /usr/lib/ssh/sftp-server

Create authorized_keys folder, generate a SSH-key on the client, copy the contents of the key to /etc/ssh/authorized_keys (or any other preferred method) of the server and set correct permissions:

# mkdir /etc/ssh/authorized_keys
# chown root:root /etc/ssh/authorized_keys
# chmod 755 /etc/ssh/authorized_keys
# echo 'ssh-rsa <key> <username@host>' >> /etc/ssh/authorized_keys/username
# chmod 644 /etc/ssh/authorized_keys/username

Restart sshd.service.

Tips and tricks

Write permissions

The bind path needs to be fully owned by root, however files and/or subdirectories do not have to be. In the following example the user www-demo uses /srv/ssh/www/demo as the jail-directory:

# mkdir /srv/ssh/www/demo/public_html
# chown www-demo:sftponly /srv/ssh/www/demo/public_html
# chmod 755 /srv/ssh/www/demo/public_html

The user should now be able to create files/subdirectories inside this directory. See File permissions and attributes for more information.

Allow upload only

To allow only uploading files via sftp and deny downloading files, change the ForceCommand internal-sftp line:

Match Group sftponly
  ForceCommand internal-sftp -u 0666 -p realpath,open,write,close,lstat


The user will not be able to access /dev/log. This can be seen by running strace on the process once the user connects and attempts to download a file.

Create sub directory

Create the sub-directory dev in the ChrootDirectory, for example:

# mkdir /usr/local/chroot/user/dev
# chmod 755 /usr/local/chroot/user/dev

Now you should create socket at /usr/local/chroot/user/dev/log which will be used by openssh. You may directly bind this socket to /dev/log (or /run/systemd/journal/dev-log in case you are using journald) or create using syslog-ng/rsyslog.

Bind to journald

# touch /usr/local/chroot/user/dev/log
# mount --bind /run/systemd/journal/dev-log /usr/local/chroot/user/dev/log

To make it permanent, add an entry to fstab:

/run/systemd/journal/dev-log /usr/local/chroot/user/dev/log none bind,nofail,x-systemd.requires=sshd.service

Syslog-ng configuration

Add to /etc/syslog-ng/syslog-ng.conf a new source for the log and add the configuration, for example change the section:

source src {


source src {

and append:

#sftp configuration
destination sftp { file("/var/log/sftp.log"); };
filter f_sftp { program("internal-sftp"); };
log { source(src); filter(f_sftp); destination(sftp); };

(Optional) If you would like to similarly log SSH messages to its own file:

#sshd configuration
destination ssh { file("/var/log/ssh.log"); };
filter f_ssh { program("sshd"); };
log { source(src); filter(f_ssh); destination(ssh); };

(From Syslog-ng#Move log to another file)

OpenSSH configuration

Edit /etc/ssh/sshd_config to replace all instances of internal-sftp with internal-sftp -f AUTH -l VERBOSE.

Restart service

Restart service syslog-ng and sshd.

/usr/local/chroot/theuser/dev/log should now exist.

Alternatives to SFTP

Secure copy protocol (SCP)

Installing openssh provides the scp command to transfer files. SCP may be faster than using SFTP [1].

Install rsshAUR or scponly as alternative shell solutions.


install scponly.

For existing users, simply set the user's shell to scponly:

# usermod -s /usr/bin/scponly username

See the Scponly Wiki for more details.

Adding a chroot jail

The package comes with a script to create a chroot. To use it, run:

# /usr/share/doc/scponly/setup_chroot.sh
  • Provide answers.
  • Check that /path/to/chroot has root:root owner and r-x for others.
  • Change the shell for selected user to /usr/bin/scponlyc.
  • sftp-server may require some libnss modules such as libnss_files. Copy them to chroot's /lib path.

See also