Yesterday and today I tackled this beast and finally got it working. Here’s what I did:
I had openssh installed via yum, so first I had to uninstall that, along with it’s dependencies:
yum uninstall openssh
…
Erased: netdump
Erased: openssh-server
Erased: openssh
Erased: openssh-clients
Erased: openssh-askpass
After lots of reading through google post and trying various methods, I went to http://chrootssh.sourceforge.net/. In their downloads section, they now have patched source files for openssh with the ability for chroot already patched. For my server, I used this one. Here are the steps, including the config options I used.
After lots of reading through google post and trying various methods, I went to . In their downloads section, they now have patched source files for openssh with the ability for chroot already patched. For my server, I used . Here are the steps, including the config options I used.
cd /usr/local/src
wget http://chrootssh.sourceforge.net/download/openssh-4.5p1-chroot.tar.bz2
tar xjf openssh-4.5p1-chroot.tar.bz2
cd openssh-4.5p1-chroot
./configure \
–prefix=/usr \
–sysconfdir=/etc/ssh \
–with-pam \
–with-tcp-wrappers \
–with-md5-passwords \
–with-pid-dir=/var/run
make
make install
The install script copies default config files (according to the options I used above) to /etc/ssh directory. Within that directory, I tightened down things slightly by adding these lines to sshd_config:
AllowUsers user1 user2 user3 etc
PermitRootLogin no
This install also doesn’t come with a startup script, so I pulled one over from backup of the yum/rpm install of openssh. My /etc/init.d/sshd looks like this:
#!/bin/bash
#
# Init file for OpenSSH server daemon
#
# chkconfig: 2345 55 25
# description: OpenSSH server daemon
#
# processname: sshd
# config: /etc/ssh/ssh_host_key
# config: /etc/ssh/ssh_host_key.pub
# config: /etc/ssh/ssh_random_seed
# config: /etc/ssh/sshd_config
# pidfile: /var/run/sshd.pid# source function library
. /etc/rc.d/init.d/functions# pull in sysconfig settings
[ -f /etc/sysconfig/sshd ] && . /etc/sysconfig/sshdRETVAL=0
prog=”sshd”# Some functions to make the below more readable
KEYGEN=/usr/bin/ssh-keygen
SSHD=/usr/sbin/sshd
RSA1_KEY=/etc/ssh/ssh_host_key
RSA_KEY=/etc/ssh/ssh_host_rsa_key
DSA_KEY=/etc/ssh/ssh_host_dsa_key
PID_FILE=/var/run/sshd.pidrunlevel=$(set — $(runlevel); eval “echo \$$#” )
do_rsa1_keygen() {
if [ ! -s $RSA1_KEY ]; then
echo -n $”Generating SSH1 RSA host key: ”
if $KEYGEN -q -t rsa1 -f $RSA1_KEY -C ” -N ” >&/dev/null; then
chmod 600 $RSA1_KEY
chmod 644 $RSA1_KEY.pub
if [ -x /sbin/restorecon ]; then
/sbin/restorecon $RSA1_KEY.pub
fi
success $”RSA1 key generation”
echo
else
failure $”RSA1 key generation”
echo
exit 1
fi
fi
}do_rsa_keygen() {
if [ ! -s $RSA_KEY ]; then
echo -n $”Generating SSH2 RSA host key: ”
if $KEYGEN -q -t rsa -f $RSA_KEY -C ” -N ” >&/dev/null; then
chmod 600 $RSA_KEY
chmod 644 $RSA_KEY.pub
if [ -x /sbin/restorecon ]; then
/sbin/restorecon $RSA_KEY.pub
fi
success $”RSA key generation”
echo
else
failure $”RSA key generation”
echo
exit 1
fi
fi
}do_dsa_keygen() {
if [ ! -s $DSA_KEY ]; then
echo -n $”Generating SSH2 DSA host key: ”
if $KEYGEN -q -t dsa -f $DSA_KEY -C ” -N ” >&/dev/null; then
chmod 600 $DSA_KEY
chmod 644 $DSA_KEY.pub
if [ -x /sbin/restorecon ]; then
/sbin/restorecon $DSA_KEY.pub
fi
success $”DSA key generation”
echo
else
failure $”DSA key generation”
echo
exit 1
fi
fi
}do_restart_sanity_check()
{
$SSHD -t
RETVAL=$?
if [ ! "$RETVAL" = 0 ]; then
failure $”Configuration file or keys are invalid”
echo
fi
}start()
{
# Create keys if necessary
do_rsa1_keygen
do_rsa_keygen
do_dsa_keygencp -af /etc/localtime /var/empty/sshd/etc
echo -n $”Starting $prog: ”
$SSHD $OPTIONS && success || failure
RETVAL=$?
[ "$RETVAL" = 0 ] && touch /var/lock/subsys/sshd
echo
}stop()
{
echo -n $”Stopping $prog: ”
if [ -n "`pidfileofproc $SSHD`" ] ; then
killproc $SSHD
else
failure $”Stopping $prog”
fi
RETVAL=$?
# if we are in halt or reboot runlevel kill all running sessions
# so the TCP connections are closed cleanly
if [ "x$runlevel" = x0 -o "x$runlevel" = x6 ] ; then
killall $prog 2>/dev/null
fi
[ "$RETVAL" = 0 ] && rm -f /var/lock/subsys/sshd
echo
}reload()
{
echo -n $”Reloading $prog: ”
if [ -n "`pidfileofproc $SSHD`" ] ; then
killproc $SSHD -HUP
else
failure $”Reloading $prog”
fi
RETVAL=$?
echo
}case “$1″ in
start)
start
;;
stop)
stop
;;
restart)
stop
start
;;
reload)
reload
;;
condrestart)
if [ -f /var/lock/subsys/sshd ] ; then
do_restart_sanity_check
if [ "$RETVAL" = 0 ] ; then
stop
# avoid race
sleep 3
start
fi
fi
;;
status)
status $SSHD
RETVAL=$?
;;
*)
echo $”Usage: $0 {start|stop|restart|reload|condrestart|status}”
RETVAL=1
esac
exit $RETVAL
I was getting an error message using this startup script. I’m not sure what it’s for, but this script wants to copy /etc/localtime to /var/empty/sshd/etc/. Make sure this directory exists:
mkdir /var/empty/sshd
mkdir /var/empty/sshd/etc
cp -af /etc/localtime /var/empty/sshd/etc/
With the above, all the usual ssh stuff works, including sftp without chroot.
OK, now to set up chroot. This took me forever to figure out, though as listed here it is simple.
First, set the user’s home directory in /etc/passwd. The key is to use a ‘.’ where you want the user’s root directory to be. For example, change the user’s home directory to something like:
/path/to/chroot_dir/./home/user
of course, create the /path/to/chroot_dir/home/user directory first. When the user logs in via ssh (or sftp, etc), he/she will see their home directory only as /home/user, and will be in “jail” within that directory structure. The rest of the system will be off limits.
Sounds easy enough. The problem is that it takes a lot more. Once the user logs in, they have to have access to system commands (like ls, mkdir, etc., as well as a shell like bash). There are several google references to how to do this, including (in no specific order):
- http://www.brandonhutchinson.com/chroot_ssh.html
- http://chrootssh.sourceforge.net/docs/chrootedsftp.html
- http://www.netadmintools.com/art294.html
The bottom line on all of these is that you have to create /bin, /dev, /etc/, /lib, /usr, and within the new jail, then these should be populated with the appropriate files.
cd /path/to/chroot
mkdir bin dev etc lib usr
mkdir usr/bin usr/lib usr/libexec
cd /bin
cp -a bash cp ls mkdir mv rm rmdir /path/to/chroot/bin
cp -a /usr/bin/ldd /path/to/chroot/usr/bin/
cp -a /usr/libexec/sftp-server /path/to/chroot/usr/libexec/
All web pages now talk about making sure all the appropriate library files are copied over. ldd can tell you which ones you need for each executable (e.g., ‘ldd /bin/cp’ will tell you the libraries needed) for the cp command. These need to be copied to the appropriate directory with the jail. The problem is that ldd apparently doesn’t always tell the whole story, and others are sometimes needed. Symptoms would be that once a connection is made, it immediately drops with an error “Connection closed” with no further explanation, even in the log files. The solution suggested by many is to simply copy over all the lib files:
cp -a /lib/lib*.* /path/to/chroot/lib/
cp -a /usr/lib/lib*.* /path/to/chroot/usr/lib/
On my system, the above steps allow a chroot login to occur via ssh with everything working correctly. Unfortunately, this doesn’t work with sftp, and attempts to connect via sftp with a user in chroot fails with the “Connection closed” error. The secret for me, after a LOT of reading through google posts, was to copy over /etc/passwd to the jail.
cp -a /etc/passwd /path/to/chroot/etc/
Make sure that file is not world-readable:
chmod 640 /path/to/chroot/etc/passwd
OK! Now all seems to work. Hope this helps someone (or me in the future).