Restricting Users to SFTP Only and to Home Directories Using a Chroot

A chroot jail is a miniature environment which provides only just enough resources for one or another program to run. In this case, the sftp program is the one that is chroot’ed. The process can be made easier with tools like rssh or scponly – both of whcih are available in Red Hat Enterprise Linux 5.

However, the problem comes up when you want to use multiple users in this environment and you want to restrict each user so that they cannot see the rest of the environment, including other users. Neither rssh nor scponly will handle this sort of thing.

There is the program MySecureShell but it is not included with either RHEL or Ubuntu Server. One would have to compile it from source or use their packages for these environments.

However, as of OpenSSH 4.9 this problem was resolved: with the addition of the Match and ChrootDirectory options to OpenSSH 4.9 – as well as an internal SFTP server (in contrast to the external helper program otherwise used) – this allows the kind of restrictions wanted here.

Ubuntu Server Lucid Lynx (10.04) and Red Hat Enterprise Server 6 both come with OpenSSH 5.3p1 which includes both of these options. However, Red Hat Enterprise 5 only comes with OpenSSH 4.3p2. To see which version you have, use a command like this:

$ ssh -V
OpenSSH_4.3p2, OpenSSL 0.9.8e-fips-rhel5 01 Jul 2008

Red Hat did backport the ChrootDirectory option into OpenSSH in RHEL 5 back in 2009, but not the Match option. Thus the ChrootDirectory option is good for everyone or no one. It is possible to use the option on the command line or in a specially crafted configuration file, but this requires a special server for SFTP instead of using the normal one – meaning two instances of SSH running if you want standard SSH logins.

It doesn’t seem to be possible to pass such options into rssh; rssh doesn’t allow you to pass options to ssh.

If you are using RHEL 5, it appears that the best option is to compile the most recent OpenSSH from source. To build the source, unpack the archive and copy the file contrib/redhat/openssh.spec from the unpacked openssh directory to the /usr/src/redhat/SPECS directory. Edit the file to your desire (such as disabling all of the X11 capabilities) and then perform this command:

rpmbuild -ba openssh.spec

In my case, the SPEC file contained a error in 5.9p1: there is a line that says:


Since there are no files named WARNING* this fails. Remove that entry from the end of the line:


Then it compiles without problem.

If you want to use a recent version of OpenSSH on RHEL 5, you’ll have to make some configuration changes – and watch out for some changes. Changes include:

  • ServerKeyBits increased from 768 to 1024 (knock back down with specific entry if this worries you)
  • Protocol only supports version 2 by default
  • ShowPatchLevel not supported by standard OpenSSH (requires patch)
  • New ECSDA keys not supported fully or properly; `ssh-keygen -A` generates errors (can be ignored)
  • MaxSessions added to configuration file
  • AuthorizedKeysFile changes meaning slightly
  • UseDNS is included in the configuration file (instead of being hidden – set it to “no” for speed)
  • Banner is set to “none” by default

Change the Subsystem configuration entry for sftp to look like this:

Subsystem sftp internal-sftp

Add this to the end of the configuration:

Match group sftp-only
 ChrootDirectory %h
 AllowTCPForwarding no
 X11Forwarding no
 ForceCommand internal-sftp

Don’t forget to restart the ssh server:

service sshd restart

When you do this, make sure to have a root shell already open on the system so that you don’t get locked out. The user should exist in the password file like so:


The home directory must be owned by user root and group root – including all directories in the path. The shell does not matter, as SSH will take over before the shell is activated; however, if there are other ways to log in with this user aside from SSH, then a proper shell like /bin/nologin or /bin/false is necessary.

For the specified configuration the user must be a member of the sftp-only group – either primary or secondary, it doesn’t matter. The Match option can also be used against a single user instead.

If you want the user – or anyone else – to be able to write files in this tree, create a subdirectory in the home directory with the appropriate permissions. The user – and others – will not be able to write to the home directory, but will be able to write to the subdirectory.

Don’t forget to test the login first – and don’t forget to have a root shell open before you start changing OpenSSH configuration or you may be unable to SSH into the system!

Logging transfers via SFTP

When you install OpenSSH, you also get SFTP for free: a replacement for FTP. (Note that SFTP is related to FTP by name only.)

SFTP is very useful, but as is normally configured, it does not provide any logging of user’s file transfers. For monitoring usage, this can be a problem.

The original setting in sshd_config might look like this:

Subsystem sftp /usr/libexec/openssh/sftp-server

This lack of logging is easily remedied; the details can be found in the man page for sftp-server:

Subsystem sftp /usr/libexec/openssh/sftp-server -l INFO

For best results, check your system’s man pages for the available options.

Watch Out! Using SFTP to Transfer Files to OpenVMS…

The HP ITRC Forums has a thread on problems transferring files to OpenVMS using SFTP. The symptoms were that transferring files to OpenVMS would result in a corrupted file.

The problem’s cause was rather insidious: due to the way file versioning works, the new file inherits the old version’s attributes, which could be incompatible with the UNIX file format. When this occurs, the result is a corrupted file.

The fix is to convert the file attributes to StreamLF. Steven Schweda recommended this utility:

$! 12 December 1999. SMS.
$! CONVERT a file to StreamLF record format.
$ convert 'p1' 'p2' /fdl = sys$input:
FORMAT stream_lf

Steven also suggests this utility, which finds all files that are non-streamlf – with an optional parameter specifying files to look at:

$! 9 November 2006. SMS.
$! Find non-directory files matching P1 which are not
$! Record format: Stream_LF.
$ if (p1 .eqs. "")
$ then
$ p1 = "[...]*.*;*"
$ endif
$ file_old = ""
$ loop_top:
$ file = f$search( p1)
$ if ((file .eqs. "") .or. (file .eqs. file_old)) then goto loop_end
$ file_old = file
$ if (.not. f$file_attributes( file, "DIRECTORY"))
$ then
$ rfm = f$file_attributes( file, "RFM")
$ if (rfm .nes. "STMLF")
$ then
$ write sys$output "''rfm' ''file'"
$ endif
$ endif
$ goto loop_top
$ loop_end:

The rule is: when working with UNIX files on OpenVMS, create a streamlf file first! This will prevent corrupted files.