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

Print Friendly, PDF & Email

Source: nixCraft

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:



1. Login as the root user

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


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:


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

Related Post