chrooted SFTP with OpenSSH 5
Recently, I had a requirement to create a chroot’d server for multiple people to access file securely. Here’s the article describing the configuration for this new feature in OpenSSH >4.9. The long-awaited, must praised chrooted SFTP!! Read on for the tutorial.
UPDATE: Sorry All, I moved from Joomla to Wordpress recently, and the formatting in this article became a bit broken in the process. I’ll fix it soon, I promise!
First of all, credit for this document also belongs to my colleague David Stahl. He co-wrote and co-developed the procedure.
Problem:
We need to have an SFTP server, that chroots only some users, and also is externally authenticated. Some will be dropped into a chroot and some will not- which will be is decided by group membership. Additionally user home directories must be automatically created upon login with the correct permissions.
In our example we are using external authentication via AD/pam_winbind; but this procedure should work just as fine for local authentication or other name services as well (nis/pam_ldap).
This tutorial will help you automatically create the home directory for the user upon first log in if it doesn’t already exist, and then copy various files from /etc/skel. The home directory auto-creation is accomplished with the pam_mkhomedir module. After the user home directory has been created, we will use another pam module called pam_script to change ownership on the top-level of the user’s home dir so that the sftp chroot will work correctly.
note: chrooted sftp/scp is a new feature in openssh >4.9. Newer linux distributions already include this, but RHEL5/Centos does not.
(this is important!!)Openssh tests to ensure the chrooted home directory base is owned by root, and is not writable by any other user or group. This must also be the case for each parent directory in the path from the user’s home up to the / on your system. We are going to use pam_script to run a script that will chown the users home to root.
However for upload/download dirs they need to be writable/readable by the user account. so we will accomplish this by placing upload/download directories in /etc/skel so the user has an area with correct permissions.
This tutorial assumes that you already have a group named sftponly, and accounts that you want to have chrooted sftp are already members of that group.
compile fedora openssh5
Since RHEL5 does not include openssh 5.0 yet, we’re going to manually back-port openssh 5.0 from
fedora 9.
wget ftp://mirror.nyi.net/fedora/linux/releases/9/Fedora/source/SRPMS/openssh-5.0p1-1.fc9.src.rpm
wget ftp://mirror.nyi.net/fedora/linux/releases/9/Fedora/source/SRPMS/
libedit-2.10-4.20070831cvs.fc9.src.rpm
mkdir -p /usr/src/redhat/RPMS/i386 && mkdir /usr/src/redhat/BUILD
rpm -ivh openssh-5.0p1-1.fc9.src.rpm && rpm -ivh libedit-2.10-4.20070831cvs.fc9.src.rpm
yum -y install gtk2-devel libX11-devel ncurses-devel nss-devel xauth rpm-build (and any other dependencies)
rpmbuild -bb /usr/src/redhat/SPECS/libedit.spec
cd /usr/src/redhat/RPMS/i386
rpm -ivh libedit-2.10-4.20070831cvs.i386.rpm && rpm -ivh libedit-devel-2.10-4.20070831cvs.i386.rpm
rpmbuild -bb /usr/src/redhat/SPECS/openssh.spec
rpm -Uvh /usr/src/redhat/RPMS/i386/openssh*
edit sshd_config
per these instructions http://www.minstrel.org.uk/papers/sftp/builtin/
#Use the following line to *replace* any existing 'Subsystem' line
Subsystem sftp internal-sftp
# These lines must appear at the *end* of sshd_config
Match Group sftponly
ChrootDirectory %h
ForceCommand internal-sftp
AllowTcpForwarding no
restart the sshd
service sshd start
The “ChrootDirectory %h” option will drop the user into the homedir that you have configured in your
naming service.
Add file staging areas to skel
In this tutorial, we’re going to add incoming and outgoing dirs in /etc/skel
mkdir /etc/skel/from_client && mkdir /etc/skel/to_client
This way when new user accounts are created, they’ll always have the “to” and “from” directories.
configure pam_mkhomedir
pam_mkhomedir is already included in RHEL5, all we need to do is enable it.
Edit /etc/pam.d/system-auth and add the following line:
#%PAM-1.0
# This file is auto-generated.
# User changes will be destroyed the next time authconfig is run.
auth required pam_env.so
auth sufficient pam_unix.so nullok try_first_pass
auth requisite pam_succeed_if.so uid >= 500 quiet
auth sufficient pam_winbind.so cached_login use_first_pass
auth required pam_deny.so
account required pam_unix.so broken_shadow
account sufficient pam_localuser.so
account sufficient pam_succeed_if.so uid < 500 quiet
account [default=bad success=ok user_unknown=ignore] pam_winbind.so cached_login
account required pam_permit.so
#this creates the homedir if it doesn't exist
session required pam_mkhomedir.so skel=/etc/skel/
password requisite pam_cracklib.so try_first_pass retry=3
password sufficient pam_unix.so md5 shadow nullok try_first_pass use_authtok
password sufficient pam_winbind.so cached_login use_authtok
password required pam_deny.so
session optional pam_keyinit.so revoke
session required pam_limits.so
session [success=1 default=ignore] pam_succeed_if.so service in crond quiet use_uid
session required pam_unix.so
install/configure pam_script
install pam_script
pam_script.so is available via rpmforge. if you need instructions on setting up rpmforge repos go to here:
http://wiki.centos.org/AdditionalResources/Repositories/RPMForge\\
yum install -y pam_script
configure our chown action script
Since openssh chroot requires that the chrooted homedir is owned by root, we have a script that checks
group membership and chowns the directory depending on membership of the sftponly group. In this
example we have stored the script as /root/scripts/chown_home.sh
Here are the contents of that script:
#!/bin/bash
/usr/bin/id $1 | /bin/grep -q "sftponly"
if [ $? -eq 0 ]
then
/bin/chown root `eval echo ~$1`
exit 0
fi
exit 0
And don’t forget to chmod +x your script.
When mod_script runs this, it passes a couple of arguments. In our script, $1 is the username who
originally authenticated.
edit system-auth and bring it all together
edit /etc/pam.d/system-auth to look something like this
#%PAM-1.0
# This file is auto-generated.
# User changes will be destroyed the next time authconfig is run.
auth required pam_env.so
auth sufficient pam_unix.so nullok try_first_pass
auth requisite pam_succeed_if.so uid >= 500 quiet
auth sufficient pam_winbind.so cached_login use_first_pass
auth required pam_deny.so
account required pam_unix.so broken_shadow
account sufficient pam_localuser.so
account sufficient pam_succeed_if.so uid < 500 quiet
account [default=bad success=ok user_unknown=ignore] pam_winbind.so cached_login
account required pam_permit.so
#this creates the homedir if it doesn't exist
session required pam_mkhomedir.so skel=/etc/skel/
#this changes permissions of user's home to root so chroot will work
session required pam_script.so runas=root onsessionopen=/root/scripts/chown_home.sh
password requisite pam_cracklib.so try_first_pass retry=3
password sufficient pam_unix.so md5 shadow nullok try_first_pass use_authtok
password sufficient pam_winbind.so cached_login use_authtok
password required pam_deny.so
session optional pam_keyinit.so revoke
session required pam_limits.so
session [success=1 default=ignore] pam_succeed_if.so service in crond quiet use_uid
session required pam_unix.so
pam_script must run this script as root, since the chown action needs superuser permissions to run
correctly.
finished, and testing it out
Now when you SFTP into the server as a chrooted user, you will be dropped into a secured chroot. Also
try logging in as a user who is not a member of the sftponly group. Their homedir should get created with
usual permissions.







Thanks for posting this howto. I’m going through it now. One thought: it looks like “rpmbuild -bb SPECS/libedit.spec” should be “rpmbuild -bb /usr/src/redhat/SPECS/libedit.spec”. Also, I’m building on a minimal install of Centos, which didn’t come w/ devel tools, so the line above fails when trying to find a compiler. I’m installing “Developer Tools” now (#yum groupinstall “Developer Tools”), in an effort to remedy this. Fingers crossed. Thx again.
Thanks! Remember, you can always compile the RPM on another server so that you don’t have to muck up your production machine with unessential stuff.
Another thing you might want to keep in mind is that this tutortial uses a fairly old release of OpenSSH. I don’t know if there are any security problems with that version, so you might want to use the updated SRPM that came out later.
http://ftp.ndlug.nd.edu/pub/fedora/linux/updates/9/SRPMS.newkey/openssh-5.1p1-3.fc9.src.rpm
Thanks for your follow-up comments. I kept running into trouble w/ an openssh dependency called “tcp_wrapper-devel”, which I was able to track down, but the versions I found conflicted w/ the installed “tcp_wrapper” on my machine (sigh) …. This might have been sorted out in the update you mentioned. Out of curiosity, do you know if I set up a traditional chroot jail for a user, is the openssh chroot still required for sftp, ie does sftp subvert chroot jails, or is it just a convenient way to create a jail if sftp is the only functionality required? Thanks!
I was able to compile and install openssh using a combination of your tutorial and this one: http://www.dotnux.com/?p=3. I’m still able to ssh in – so far so good.