Debian/Ubuntu Linux: Restrict an SSH user session to a specific directory by setting chrooted jail


setup a web-server. I need to grant a user ssh access but I do not trust users. How can I limit user session to a specific directory such as /home/httpd/$USERNAME? How do I set up a ssh chroort jail on a Linux operating systems?


You can interactive shell with special root directory on a Linux or Unix-like systems. You can set the pathname (such as /home/httpd/foo) of a directory to chroot to after authentication. All components of the pathname must be root owned directories that are not writable by any other user or group. After the chroot, sshd changes the working directory to the user’s home directory.

Say hello to ChrootDirectory directive

From the sshd_config man page:

The ChrootDirectory must contain the necessary files and directo ries to support the user’s session. For an interactive session this requires at least a shell, typically sh(1), and basic /dev nodes such as null(4), zero(4), stdin(4), stdout(4), stderr(4), arandom(4) and tty(4) devices. For file transfer sessions using “sftp”, no additional configuration of the environment is necessary if the in-process sftp server is used, though sessions which use logging do require /dev/log inside the chroot directory.

You may grant a user ssh access, whom you do not completely trust. You can limit what that user can see or run only ls, date, and internal bash commands by setting up a SSH chroot jail. Let us see how to create the chrooted jail for OpenSSH server on a Debain or Ubuntu Linux server. The following tutorial is tested on a Debian Linux server v8.1:

# lsb_release -a


Sample outputs:

Fig.01: Finding Linux distro version and name command

  1. Login as the root user
  2. Type any one of the following command:
  3. $ su –


$ sudo -s

2. Create the chroot jail

I’m going to set /home/jails/ directory to restrict an ssh user session to this directory:

# D=/home/jails

# mkdir -p $D


As per the sshd man page you need following files too:

# ls -l /dev/{null,zero,stdin,stdout,stderr,random,tty}


Sample outputs:

crw-rw-rw- 1 root root 1, 3 Jun 11 03:11 /dev/null

crw-rw-rw- 1 root root 1, 8 Jun 11 03:11 /dev/random

lrwxrwxrwx 1 root root   15 Jun 11 03:11 /dev/stderr -> /proc/self/fd/2

lrwxrwxrwx 1 root root   15 Jun 11 03:11 /dev/stdin -> /proc/self/fd/0

lrwxrwxrwx 1 root root   15 Jun 11 03:11 /dev/stdout -> /proc/self/fd/1

crw-rw-rw- 1 root tty  5, 0 Jun 11 04:43 /dev/tty

crw-rw-rw- 1 root root 1, 5 Jun 11 03:11 /dev/zero

To create required /dev nodes entries use the following mknod command:

# mkdir -p $D/dev/

# mknod -m 666 $D/dev/null c 1 3

# mknod -m 666 $D/dev/tty c 5 0

# mknod -m 666 $D/dev/zero c 1 5

# mknod -m 666 $D/dev/random c 1 8

3. Set permissions

Type the following command so that the chroot $D directory, and all its components, must be owned by root user and not writable by any non-root user or group:

# chown root:root $D

# chmod 0755 $D


Verify it:

# ls -ld $D


Sample outputs:

drwxr-xr-x 2 root root 4096 Jun 11 03:14 /home/jails

4. Install bash shell in $D

Type the following command to create bin directory in $D path:

# mkdir -p $D/bin


Copy /bin/bash to $D/bin/ directory:

# cp -v /bin/bash $D/bin


Sample outputs:

‘/bin/bash’ -> ‘/home/jails/bin/bash’

Copy required shared libs to $D directory. The syntax is as follows to find out what bash needed:

# ldd /bin/bash


Sample outputs: (0x00007ffdbb1bc000) => /lib/x86_64-linux-gnu/ (0x00007f1349bc6000) => /lib/x86_64-linux-gnu/ (0x00007f134999c000) => /lib/x86_64-linux-gnu/ (0x00007f1349797000) => /lib/x86_64-linux-gnu/ (0x00007f13493ee000)

/lib64/ (0x00007f1349e0d000)

Copy highlighted files one-by-one as follows using the cp command:

# mkdir -p $D/lib/

# mkdir -p $D/lib64/

# mkdir -p $D/lib/x86_64-linux-gnu/

# cp -v /lib/x86_64-linux-gnu/{,,,} $D/lib/


Sample outputs:

‘/lib/x86_64-linux-gnu/’ -> ‘/home/jails/lib/’

‘/lib/x86_64-linux-gnu/’ -> ‘/home/jails/lib/’

‘/lib/x86_64-linux-gnu/’ -> ‘/home/jails/lib/’

‘/lib/x86_64-linux-gnu/’ -> ‘/home/jails/lib/’

Next, copy /lib64/ to /lib64/ directory:

# cp -v /lib64/ $D/lib64/


Sample outputs:

‘/lib64/’ -> ‘/home/jails/lib64/’

Finally, copy /lib/x86_64-linux-gnu/libnss_files*, enter:

# cp -va /lib/x86_64-linux-gnu/libnss_files* $D/lib/x86_64-linux-gnu/

5. Add user to the the system

You also need to copy /etc/passwd, and /etc/group files to $D/etc/ directory:

# mkdir -p $D/etc/

Add a user called tom and jerry:

# adduser tom

# adduser jerry


Sample outputs:

Fig.02: Add a user on a Debian Linux 8 server


Finally, copy updated /etc/{passwd,group} files to $D/etc/ directory:

# cp -vf /etc/{passwd,group} $D/etc/


Sample outputs:

‘/etc/passwd’ -> ‘/home/jails/etc/passwd’

‘/etc/group’ -> ‘/home/jails/etc/group’

Warning: if you add or delete or made any changes to the user or password in /etc/passwd file, recopy /etc/{passwd,group} files again by running the following two commands:


cp -vf /etc/{passwd,group} $D/etc/

6. Configure sshd

Edit /etc/ssh/sshd_config file, enter:

# vi /etc/ssh/sshd_config


Append the following two directives:

Apply the chrooted jail to the user called tom and jerry ##

Match User tom,jerry

ChrootDirectory /home/jails

Allow sftp to chrooted jail ##

ForceCommand internal-sftp

7. Restart sshd service

For Debian Linux version 8.x, enter:

# systemctl restart ssh.service


For Debian version 7.x and older, enter:

# /etc/init.d/ssh restart

8. Test it

The syntax is:

ssh user@sever

ssh user@sever-ip-here

ssh tom@localhost

Sample outputs:

tom@localhost s password:


Last login: Thu Jun 11 04:32:32 2015 from localhost

Could not chdir to home directory /home/tom: No such file or directory

-bash-4.3$ ls

-bash: ls: command not found

-bash-4.3$ date

-bash: date: command not found

-bash-4.3$ pwd



9. Install additional commands

The tom user now able to log into the server but can not run other commands such as ls, date, and so on. The user is restricted to /bin/bash only. If you need ls or any other commands, you need to install them in /home/jails/ directory as I did for /bin/bash. The easiest way is as follows:

# cd /root/


# mv l2chroot.txt l2chroot

# chmod +x l2chroot

# vi l2chroot


Find BASE line and change it as follows:

BASE= /home/jails

Save and close the file. Install /bin/ls in $D/bin/ directory:

# cp -v /bin/ls $D/bin/

# cp -v /bin/date $D/bin/

# /root/l2chroot /bin/ls

# /root/l2chroot /bin/date


Create $D/home/tom and $D/home/jerry directories:

# mkdir -p $D/home/{tom,jerry}

# chown -R tom:tom $D/home/tom/

# chown -R jerry:jerry $D/home/jerry/

# chmod -R 0700 $D/home/tom/

# chmod -R 0700 $D/home/jerry/

10. Verify and test it again

The syntax is as follows for sftp command:

sftp user@server

sftp user@server-ip-here



Sample outputs: s password:

Connected to


sftp> pwd

Remote working directory: /home/tom

sftp> ls

sftp> cd /home

sftp> ls

jerry  tom

sftp> pwd

Remote working directory: /home

sftp> ls -l

drwx——    2 jerry    jerry        4096 Jun 11 08:55 jerry

drwx——    2 tom      tom          4096 Jun 11 08:53 tom

sftp> cd jerry

sftp> pwd

Remote working directory: /home/jerry

sftp> ls

remote readdir( /home/jerry ): Permission denied

sftp> ls

remote readdir( /home/jerry ): Permission denied

sftp> put /etc/resolv.conf .

Uploading /etc/resolv.conf to /home/jerry/.

remote open( /home/jerry/. ): Permission denied

sftp> cd /home/tom

sftp> put /etc/resolv.conf .

Uploading /etc/resolv.conf to /home/tom/./resolv.conf

/etc/resolv.conf                                                 100%   70     0.1KB/s   00:00

sftp> ls -l

-rw-r–r–    1 tom      tom            70 Jun 11 09:01 resolv.conf

sftp>  quit

How do I map users web-server (DocumentRoot) to /home/jails/ directory?

Say, /home/httpd/tom_web is DocumentRoot for tom user, then:

# mkdir $D/home/tom/web

# mount –bind /home/httpd/tom_web $D/home/tom/web

update fstab file so that it can mount after server reboot ##

# echo  /home/httpd/tom_web/ $D/home/tom/web none bind  >> /etc/fstab



Leave a Reply

Your email address will not be published. Required fields are marked *