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

Posted on in Categories , , last updated June 11, 2015

I 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
Fig.01: Finding Linux distro version and name command

1. Login as the root user

Type any one of the following command:
$ su -
OR
$ 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:

	linux-vdso.so.1 (0x00007ffdbb1bc000)
	libncurses.so.5 => /lib/x86_64-linux-gnu/libncurses.so.5 (0x00007f1349bc6000)
	libtinfo.so.5 => /lib/x86_64-linux-gnu/libtinfo.so.5 (0x00007f134999c000)
	libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f1349797000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f13493ee000)
	/lib64/ld-linux-x86-64.so.2 (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/{libncurses.so.5,libtinfo.so.5,libdl.so.2,libc.so.6} $D/lib/

Sample outputs:

‘/lib/x86_64-linux-gnu/libncurses.so.5’ -> ‘/home/jails/lib/libncurses.so.5’
‘/lib/x86_64-linux-gnu/libtinfo.so.5’ -> ‘/home/jails/lib/libtinfo.so.5’
‘/lib/x86_64-linux-gnu/libdl.so.2’ -> ‘/home/jails/lib/libdl.so.2’
‘/lib/x86_64-linux-gnu/libc.so.6’ -> ‘/home/jails/lib/libc.so.6’

Next, copy /lib64/ld-linux-x86-64.so.2 to /lib64/ directory:
# cp -v /lib64/ld-linux-x86-64.so.2 $D/lib64/
Sample outputs:

‘/lib64/ld-linux-x86-64.so.2’ -> ‘/home/jails/lib64/ld-linux-x86-64.so.2’

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
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:
D=/home/jails
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:

[email protected]'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
/
-bash-4.3$ 

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/
wget http://www.cyberciti.biz/files/lighttpd/l2chroot.txt
# 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 [email protected]
sftp [email protected]
sftp [email protected]

Sample outputs:

[email protected]'s password: 
Connected to server1.cyberciti.biz.

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

13 comment

    1. rbash does not accomplish the compartmentalization that the chroot jail accomplishes. It also doesn’t restrict someone from running an unrestricted shell from the restricted shell which potentially exposes information that you do not want a user (or daemon) to have the potential to access. chroot jail ensures that files outside of the jail are not accessible from the user/daemon session.

      Good article in my opinion. Time to get the old skool back into play here when sysadmins were not so darn lazy..j

  1. I am looking for a refresher course on Linux/ Unix , I already have a basic understanding from a windows environment where in I created directories , libraries and files. How to do the same on a Unix/Linux console.

  2. Try the Linux Foundation MOOC on edX, Faiz. Seems like a good start.

    Great tutorial, Vivek, thanks for this. One thing I’d like to add, please: everything is explained in great detail, except for the part 6, –> vi /etc/ssh/sshd_config <– where the author assumes pre-existing vi knowledge.

    I could have used some indications there as well since I'm a complete newb and I will have to google for vi commands.

    Again, thanks for this. Really helpful.

  3. ssh [email protected]
    This service allows sftp connections only.
    sftp [email protected]
    usage: sftp [-1246aCfpqrv] [-B buffer_size] [-b batchfile] [-c cipher]
    [-D sftp_server_path] [-F ssh_config] [-i identity_file] [-l limit]
    [-o ssh_option] [-P port] [-R num_requests] [-S program]
    [-s subsystem | sftp_server] host
    sftp [user@]host[:file …]
    sftp [user@]host[:dir[/]]
    sftp -b batchfile [user@]host

    It doesn’t work.

  4. What doesn’t work?
    Are you really using “[email protected]”?
    If so, maybe you need to stick to watching TV.
    Nice article, thx

  5. Thank,

    I had same problem. I think this is only restrition to use sftp, so afer removing this argument from config it works like should.
    This service allows sftp connections only.

    ## Allow sftp to chrooted jail ##
    ForceCommand internal-sftp

  6. thanks for the great tutorial.
    I wanted so user can not go back to their own directory
    like for foo we have a directory /home/jails/home/foo
    so what i wanted is user should not able to go back to /home/jails/home directory

  7. I have problem when use clear command in ssh connection, example
    i create test user and jail in /home/test, and use the next command:

    – cp -v /usr/bin/clear /home/test/bin
    – l2chroot /home/test/bin/clear

    ssh [email protected]
    test# clear
    ‘xterm-256color’: unknown terminal type.

    But use the same way for ls, grep, find, and work fine.

    Note: in server command clear work fine.

    Thanks for the tutorial

Leave a Comment