Last update: 2004-09-19

// C O N T R O L - A L T - D E L . O R G

"Fate strikes down the strong man, Everyone weep with me " - O Fortuna
NAVIGATION>>
HOME PICS ABOUT NEWS CODE BOOKS MAIL
:: CODE
The three chief virtues of a programmer are: Laziness, Impatience and Hubris
-- Larry Wall
CONTACT
:. SFTP+chroot jail How-To

This How-To details the procedure for setting up a chroot'ed account which only has access to the server via SFTP.


The main features of this approach are:

- chroot login : Users are jailed in their home directory using the systems chroot system call.
- small footprint: No need for a full shell with executables as this setup uses busybox for the shell.
- secure: Users access the server using SSH, all portions of the data transfer is encrypted. This setup is easy to audit as it involves very few components.

- Users don't end up having shell access to the server.


Note: This setup was developped on RedHat 8.0, but should work exactly the same way on other Linux distributions (may also work on the *BSDs). The only thing which may need to be tweaked is the library paths and the sftp paths.

 


Step 1. Create the chroot shell /bin/chroot-shell:

#!/bin/bash

if [ "$1" = "-c" ]; then
i=0;
PARAMS="";
for param in $*; do
if [ $i -gt 0 ]; then
PARAMS="$PARAMS $param";
fi
let i++;
done;
exec sudo /usr/sbin/chroot $HOME /bin/su - $USER -c "$PARAMS"
else
exec sudo /usr/sbin/chroot $HOME /bin/su - $USER
fi;

 

Make it executable:
chmod +x /bin/chroot-shell

 


Step 2. Create the chroot environment

 

mkdir /test; cd /test; mkdir -p etc lib usr/libexec/openssh home


Step 3. Download/Compile busybox

 

http://www.busybox.net/

 

tar xvfz busybox-X.tar.gz
cd busybox-X
make menuconfig

Here are the options you'll need when compiling busybox:

General Config:
 [*] Use the devpts filesystem for Unix98 PTYs 
 [*] Support for SUID/SGID handling 
 [*] Runtime SUID/SGID configuration via /etc/busybox.conf 
 [*] Suppress warning message if /etc/busybox.conf is not readable 
Build Options:
   [*] Build BusyBox as a static binary (no shared libs)
   [*] Build with Large File Support (for accessing files > 2 GB)
   (-O6) Any extra CFLAGS options for the compiler?
 Installation Options:
   [*] Don't use /usr 
 Coreutils:
   [*] chmod
   [*] chown
   [*] chroot
   [*] cp 
   [*] dirname
   [*] ls
   [*] Enable filetyping options (-p and -F)
   [*] Enable symlinks dereferencing (-L) 
   [*] Enable recursion (-R)
   [*] Sort the file names
   [*] Show file timestamps
   [*] Show username/groupnames
   [*] mkdir
   [*] mv
   [*] pwd
   [*] pwd
   [*] realpath
   [*] rm 
   [*] rmdir 
   --- Common options for cp and mv
   [*] Preserve hard links
 Login/Password Management Utilities:
	 [*] Use internal password and group functions rather than system functions
   [*] su 
 Another Bourne-like Shell
   Choose your default shell (ash)
   [*] Standalone shell
   [*] Standalone shell -- applets always win

make dep; make; cp busybox /test/bin; ln -s /test/bin/busybox /test/bin/su

 


Step 4. Prepare the SFTP environment

 

You have two choices here, if your sftp-server binary is statically compiled, you can simply copy it over. To figure out if your sftp-server is statically compiled:

 

ldd /usr/libexec/openssh/sftp-server
     not a dynamic executable

(statically compiled)

OR

 

ldd /usr/libexec/openssh/sftp-server
libresolv.so.2 => /lib/libresolv.so.2 (0x4001b000)
libutil.so.1 => /lib/libutil.so.1 (0x4002e000)
libz.so.1 => /usr/lib/libz.so.1 (0x40031000)
libnsl.so.1 => /lib/libnsl.so.1 (0x4003f000)
libcrypto.so.2 => /lib/libcrypto.so.2 (0x40055000)
libkrb5.so.3 => /usr/kerberos/lib/libkrb5.so.3 (0x40129000)
libk5crypto.so.3 => /usr/kerberos/lib/libk5crypto.so.3 (0x40187000)
libcom_err.so.3 => /usr/kerberos/lib/libcom_err.so.3 (0x40198000)
libc.so.6 => /lib/i686/libc.so.6 (0x42000000)
libdl.so.2 => /lib/libdl.so.2 (0x4019a000)
/lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)

(dynamically compiled)

A) Statically compiled steps:

 

cp /usr/libexec/openssh/sftp-server /test/usr/libexec/openssh/sftp-server

 

B) Dynamically compiled steps:

 

In order to get the executable to work properly, all libraries which are linked to sftp-server must be available to the executable at run time. This means you have to copy all the libraries from the output of ldd to the chroot jail's library folder.

 

cp /usr/libexec/openssh/sftp-server /test/usr/libexec/openssh/sftp-server
cp /lib/libc.so.6 /lib/libdl.so.2 /lib/libnsl.so.1 /lib/libresolv.so.2 \
/lib/libutil.so.1 /lib/ld-linux.so.2 /usr/kerberos/lib/libcom_err.so.3 \
/usr/kerberos/lib/libk5crypto.so.3 /usr/kerebos/lib/libkrb5.so.3 \
/usr/lib/libz.so.1 /lib/libcrypto.so.2 /test/lib/

Ideally, I would suggest recompiling sftp-server as a static binary.

 


Step 5. Create a user

 

useradd -s /bin/chroot-shell -d /test test-sftp
passwd test-sftp
**********


Step 6. Get the UID/GID for this newly created account

id test-sftp

uid=508(test-sftp) gid=511(test-sftp) groups=511(test-sftp)


Step 7. Create a 'fake' passwd and group file (putting the appropriate UID/GID values):


echo -e "root:x:0:0::/:/bin/false\n" >/test/etc/passwd
echo -e "test-sftp:x:508:511::/home:/usr/libexec/" >>/test/etc/passwd
echo -e "openssh/sftp-server" >>/test/etc/passwd
echo -e "root:x:0:\ntest-sftp:x:511:" >/test/etc/group


Step 8. Make sure the permissions are correct


chown -R root.root /test
chown -R test-sftp.test-sftp /test/home
chattr -R +i /test/etc /test/bin /test/usr /test/lib

 


Step 9. Add the user to /etc/sudoers


echo -e "test-sftp ALL = NOPASSWD: /usr/sbin/chroot /test /bin/su - test-sftp*" \
>>/etc/sudoers

 


That's it, you now have a working user who can only access the server using SFTP.

 

Here's how it all fits together:

 

In order for a user to be able to login to a system, first we need to have a system account with a valid shell. In this case, we don't want the user to have shell access, instead we only want him to be able to access the system by SFTP.

 

To achieve this, we create a small 'fake' shell (/bin/chroot-shell) who's job is simply to temporarily become root using sudo, them issue the chroot call which jails the user to the filesystem hierarchy we have setup.

 

Once the user is jailed, we switch back from root to the original user. At this point, the user's root directory is /test, and the only executables available to the user are busybox and sftp-server. During the login process, the 'fake' password file (/test/etc/passwd) is read and the user's shell is executed (sftp-server). This approach could also be used to provide users with a chrooted shell by changing the entry in the 'fake' passwd, I don't recommend it however, as allowing your users shell access has many security implications.

 

See also:

http://www.kegel.com/crosstool/current/chrootshell.c

© copyright 2004 Mark Steele