2008-05-22 08:22:04

Chroot SFTP using OpenSSH

The open source community has been in rather desperate need of a good, clean method of chrooting SFTP sessions, but at the same time, denying shell access. In the past, people have gotten around this lack by hacking the SSH or SFTP binaries, or creating a restricted shell by various means. The basic problem with these methods is a lack of standardization and integration. Fortunately, the OpenSSH team (which is part of the OpenBSD Project), have included the ability to do just that in the recent versions of OpenSSH. And what's more, it can do it without all the /dev, /etc and shell requirements typically required for a chroot environment.

This guide will go through the entire process of setting up a chroot (aka jailed) SFTP environment for Fedora, from beginning to end.

The major benefits of utilizing this method of chrooting SFTP:

  • Denies shell access
  • Simplified installation/setup/maintenance
  • Forces users to use an encrypted session for file transfers
  • Can be installed as a separate sshd binary, leaving your current SSH configuration intact

While this method of chrooting is better than what was previously available, it isn't perfect:

  • Utilizes local (or potentially LDAP) accounts, instead of an account file, ala many FTP servers
  • The SFTP binary is tied to the SSH binary, and can't be upgraded independently
  • Doesn't allow SCP (SCP requires shell access, which this method blocks)

Notes about this guide:

  • This guide is for Fedora Core, but can be used for other distros, such as Debian or Unbuntu. Just change the software installers as required.
  • The current version of OpenSSH at this point in time is 5.0p1 (the "p" denotes that it is compatible with Operating Systems other than OpenBSD, such as Linux or Solaris), and was released on April 3rd, 2008. This is the version that will be used for this guide.

Configure binaries:

Configure the binaries so that it will not install over the existing SSH installation:
wget ftp://ftp.openbsd.org/pub/OpenBSD/OpenSSH/portable/openssh-5.0p1.tar.gz
tar zxpvf openssh-5.0p1.tar.gz
cd openssh-5.0p1
./configure --with-tcp-wrappers --prefix=/opt/sftponly --sysconfdir=/etc/sftponly
If the configuration fails due to lack of tcp wrappers, run the following command
yum install -y tcp_wrappers\*

Compile the binaries:

Perform the build and then install it:
make
make install

Configure the /etc/sftponly/sshd_config file.

Modify the following variables:
Port 2222
ServerKeyBits 2048
AllowTcpForwarding no
TCPKeepAlive no
PidFile /var/run/sftponly.pid
Remove the following lines at the end of the config file:
Subsystem sftp /opt/sftponly/libexec/sftp-server
Add the following lines:
Subsystem sftp internal-sftp
Match Group sftponly
        ChrootDirectory %h
        ForceCommand internal-sftp
        AllowTcpForwarding no

Daemon setup (to prevent the normal ssdh from getting squashed)

Rename the new sshd binary to sftponly:
mv /opt/sftponly/sbin/sshd /opt/sftponly/sbin/sftponly
Create the sftponly init.d file:
cp -pr /etc/init.d/sshd /etc/init.d/sftponly
Edit /etc/init.d/sftponly and replace the following variables:
prog="sftponly"
KEYGEN=/opt/sftponly/bin/ssh-keygen
SSHD=/opt/sftponly/sbin/sftponly
RSA1_KEY=/etc/sftponly/ssh_host_key
RSA_KEY=/etc/sftponly/ssh_host_rsa_key
DSA_KEY=/etc/sftponly/ssh_host_dsa_key
PID_FILE=/var/run/sftponly.pid
Fix all of the subsys references:
sed -i -e 's/\/var\/lock\/subsys\/sshd/\/var\/lock\/subsys\/sftponly/g' /etc/init.d/sftponly
Cleanup the status message:
sed -i -e 's/openssh-daemon/sftponly/g' /etc/init.d/sftponly
Now generate the link files for the rc#.d directories:
ln -s /etc/init.d/sftponly /etc/rc0.d/K25sftponly
ln -s /etc/init.d/sftponly /etc/rc1.d/K25sftponly
ln -s /etc/init.d/sftponly /etc/rc2.d/S55sftponly
ln -s /etc/init.d/sftponly /etc/rc3.d/S55sftponly
ln -s /etc/init.d/sftponly /etc/rc4.d/S55sftponly
ln -s /etc/init.d/sftponly /etc/rc5.d/S55sftponly
ln -s /etc/init.d/sftponly /etc/rc6.d/K25sftponly

Group setup

Install the sftponly group with a specific GID:
groupadd -g 999 sftponly

Chroot filesystem setup

Make the chroot filesystem, and modify ownership and permissions:
mkdir /chroot
chown root:root /chroot
chmod 755 /chroot

User setup

Create the user, temporarily allowing shell access:
useradd -m -r -N -d /chroot/twinkles -s /bin/bash -g 999 -c "Chroot_User twinkles" twinkles
passwd twinkles
su - twinkles
As the user "twinkles", not root, perform the following commands:
/opt/sftponly/bin/ssh-keygen -b 2048 -t rsa
/opt/sftponly/bin/ssh-keygen -b 1024 -t dsa
touch ~/.ssh/authorized_keys
mkdir -p ~/incoming
mkdir -p ~/outgoing
exit
As root, change the shell to /sbin/nologin, and clean up permissions:
usermod -s /sbin/nologin twinkles
chown root:root /chroot/twinkles
chmod 755 /chroot/twinkles

SSH key authentication

If you want to test the account without having to use passwords:
cat ~/.ssh/id_rsa.pub >> ~twinkles/.ssh/authorized_keys

Ladies and Gentlemen, start your daemons!

Start the daemon:
[root@navi ~]# service sftponly status
sftponly is stopped

[root@navi ~]# service sftponly start
Starting sftponly:        [ OK ]
Verify the daemon is running and attached to the correct TCP port:
[root@navi ~]# service sftponly status
sftponly (pid 11729) is running...

[root@navi ~]# ps -ef | grep sftponly
root        11729        1 0 08:57 ?        00:00:00 /opt/sftponly/sbin/sftponly

[root@navi ~]# netstat -pan | grep 2222
tcp        0        0 0.0.0.0:2222        0.0.0.0:*        LISTEN        11729/sftponly

Testing

With the configuration complete and the daemon started, you obviously would want to test it. Here are examples of what you should see:

[root@navi openssh-5.0p1]# sftp -oPort=2222 twinkles@localhost Connecting to localhost...
sftp>

sftp> pwd
Remote working directory: /

sftp> ls -l
drwxr-xr-x        2 490        999        4096 May 22 02:32 incoming
drwxr-xr-x        2 490        999        4096 May 22 02:32 outgoing

sftp> put sshd.c
Uploading sshd.c to /sshd.c
Couldn't get handle: Permission denied

sftp> cd incoming

sftp> pwd
Remote working directory: /incoming

sftp> put sshd.c
Uploading sshd.c to /incoming/sshd.c
sshd.c                100% 57KB 56.7KB/s 00:00

sftp> exit

[root@navi openssh-5.0p1]# scp --port=2222 sshd.c twinkles@localhost:/
This account is currently not available.

[root@navi openssh-5.0p1]# cd ~twinkles/incoming/

[root@navi incoming]# ls -AFlh
total 64K
-rw-r--r-- 1 twinkles sftponly 57K 2008-05-22 08:44 sshd.c

A couple of important things to note in this example:

  • Make sure to SFTP to port 2222, since that's the TCP port your sftponly binary is configured to, in /etc/sftponly/sshd_config!
  • The user cannot modify the "root" directory. This is because the directory is actually owned by the user root. The user can, however, modify the sub-directories (incoming, outgoing) because he does own those.
  • The scp program won't work, because the user's shell is set to /sbin/nologin.
  • When inside the sftp shell, notice that an ls -l shows UIDs and GIDs, instead of names. This is due to the chroot not having access to the /etc filesystem.

And there you go ... a nicely chrooted SFTP environment.

Tags:   linux     |    Perm Link:   Chroot SFTP using OpenSSH



James Conner