This is me, Wu!
In this page
Rate this page!
Search inside the wiki!
  Home >> hacks >> SSH Chroot in FreeBSD

SSH Chroot In FreeBSD?


In this hack, I'll show you how to limit access to a server through a SSH link. This is useful when we need to provide some users with FTP-like access to a server, just for upload or download files, but limiting their scope inside the server filesystem.

The problem

Everyone knows that FTP is an old and insecure protocol, but most lazy sysadmins out there just keep using it because a ftpd server is easy to set up and most users already know how to connect to that kind of server (using well-known FTP clients).

Here, we will use sftp instead of FTP to provide users with full access to our server. By default, a user accesing a server using sftp would have access to every part of the filesystem, with the only restriction the classic unix permissions provide. That is, users creating content somewhere in the filesystem are resposible for setting up the correct permissions, so no allowed users can get access to their files.

To minimize such problem, we will use individual chroots for users connecting to our server, and to set up those chroots, we will use rssh, a shell that will jail users connecting through sftp (or even using some commands over SSH) in a given directory.

Installing rssh

In FreeBSD we can install rssh from the ports system:

[prunus] ~> cd /usr/ports/shells/rssh/
[prunus] /usr/ports/shells/rssh> sudo make install

[ ... ]

===>   Registering installation for rssh-2.3.2
      This port has installed the following binaries which execute with
      increased privileges.

      If there are vulnerabilities in these programs there may be a security
      risk to the system. FreeBSD makes no guarantee about the security of
      ports included in the Ports Collection. Please type 'make deinstall'
      to deinstall the port if this is a concern.

      For more information, and contact details about the security
      status of this software, see the following webpage:
[prunus] /usr/ports/shells/rssh>

The port itself already add /usr/local/bin/rssh to the /etc/shells file, so it became a valid shell. If we have installed from sources, we should add it manually by ourselves.

[prunus] /usr/ports/shells/rssh> cat /etc/shells | grep rssh
[prunus] /usr/ports/shells/rssh>

Setting up rssh

Before getting into action, it is a good idea to take a look at rssh(1), the man page for rssh. There, we could read a little bit more about their funcionalities and how to use them.

So, following rssh(1), we have to replace the shell parameter for each user we want to chroot. So, we will have to open the passwd file using vipw, and then modify the required lines.

In this example we will change the shell for the user rsshexample from:

rsshexample:$1$wA8BSxd9$C.Vd0RE6YXSrAfJmN6bHH.:10005:10005::0:0:testing rssh in freebsd:/home/rsshexample:/bin/tcsh


rsshexample:$1$wA8BSxd9$C.Vd0RE6YXSrAfJmN6bHH.:10005:10005::0:0:testing rssh in freebsd:/home/rsshexample:/usr/local/bin/rssh

(we have change the user default shell from /bin/tcsh to /usr/local/bin/rssh).

NOTE: this will apply for existing users, when adding new users with adduser_ once we have installed rssh, it will appear as a valid shell:

Shell (sh csh tcsh rssh nologin) [sh]:

After setting up the correct shell for each user we want to chroot in its home directory, we would have to take a look over rssh.conf. In FreeBSD located under /usr/local/etc, in this file we can configure some aspects of rssh. As always, it is a good idea to take a look over rssh.conf(5) (the man page for the rssh config file).

Here, we will change some lines. first, we will uncomment those ones:


Just to allow users to use both scp and sftp. Then we will add a line like that to the bottom of the file:


Here we are telling rssh that each time the user rsshexample connects to our server, he will be able to use only the scp and sftp commands, and he will be chrooted inside /home/rsshexample. We also set the umask for the files and directories created by that user to 022, which results in files created with mode 744 and directories created with permissions 755.

We will have to add so many lines like that one as users we would like to chroot.

NOTE: Take care that we do not uncomment the chrootpath parameter. That parameter is used when all chroot users go inside the same chroot, which is not what we want in this case.

Setting up the chroot environment

Now we have to create to chroot environment, where our users will be jailed as soon as they connect to the server. But before doing that, we should know that we have to posible setups there:

1- One single chroot_ for all users. In this case all users will be chrooted inside the same directory, and it is their task to manage permissions correctly, or other users could get access to their files. Using this setup, all users could move themselves around the chroot, having each one a home directory where they could save files, etc. With a single chroot, it is a good idea to set up a correct umask for users, so files created by them have correct permissions (like 750, for example).

2- One chroot per user. In this case each user has it's own private environment, where only such user will have access. This is a safer environment, but has the drawback of maintenance, because we will have to maintain so many chroot environments as chrooted users. That means that we will have to upgrade each chroot environment when upgrading the server userland.

For the purpose of this hack, we will set up one chroot per user. To create a valid chroot environment inside a user home directory, we will have to follow the following steps:

1- We have to create the needed directories inside the chroot, that is, directories where we will copy the needed binaries and libraries:

[prunus] /home/rsshexample> sudo mkdir -p usr/bin usr/libexec usr/local/libexec usr/lib lib libexec bin dev etc
[prunus] /home/rsshexample>

2- Then we have to copy over those directories the needed binaries:

[prunus] /home/rsshexample> sudo cp /usr/bin/scp usr/bin/
[prunus] /home/rsshexample> sudo cp /usr/libexec/sftp-server  usr/libexec/
[prunus] /home/rsshexample> sudo cp /usr/local/libexec/rssh_chroot_helper  usr/local/libexec/
[prunus] /home/rsshexample>

3- Then we have to copy the libraries those binaries are linked against too:

[prunus] /home/rsshexample> ldd /usr/bin/scp
/usr/bin/scp: => /usr/lib/ (0x2807e000) => /lib/ (0x280b3000) => /lib/ (0x280cb000) => /lib/ (0x281be000) => /lib/ (0x281cf000) => /usr/lib/ (0x282b6000) => /usr/lib/ (0x282c4000) => /usr/lib/ (0x282f8000) => /usr/lib/ (0x28319000) => /usr/lib/ (0x2831b000) => /lib/ (0x28327000)
[prunus] /home/rsshexample>
[prunus] /home/rsshexample> sudo cp /usr/lib/ /usr/lib/ /usr/lib/ /usr/lib/ /usr/lib/ /usr/lib/ usr/lib/
[prunus] /home/rsshexample> sudo cp /lib/ /lib/ /lib/ /lib/ /lib/ lib/
[prunus] /home/rsshexample>

We should check that sftp-server is linked against the same libraries as scp, if not, we will have to copy over the needed libraries from sftp-server too. rssh_chroot_helper should be statically linked, so no big deal, but you should take a look before proceeding.

4- Then we have to copy the ld loader:

[prunus] /home/rsshexample> sudo cp /libexec/ libexec/
[prunus] /home/rsshexample>

5- Now we have to copy over a default shell (sh) and the libs it is linked against:

[prunus] /home/rsshexample> sudo cp /bin/sh bin/
[prunus] /home/rsshexample> ldd /bin/sh
/bin/sh: => /lib/ (0x28092000) => /lib/ (0x280a6000) => /lib/ (0x280e7000)
[prunus] /home/rsshexample> sudo cp /lib/ /lib/ lib/
[prunus] /home/rsshexample>

NOTE: if you experience problems when connecting to a chroot account, getting a message about something wrong with wordwrap(), try copying over the /bin/tcsh and /bin/csh shells too (and the libs their are linked against too, obviously)

6- Almost finished, we have now to create a devfs filesystem inside the /dev directory in the chroot environment. This is just a matter of adding a line to rc.conf, then set the mount point in /etc/fstab and finally mount the filesystem manually to check it is ok:

[prunus] /home/rsshexample> cat /etc/rc.conf|grep dev
[prunus] /home/rsshexample> cat /etc/fstab | grep devfs
none                    /home/rsshexample/dev devfs   rw      0       0
[prunus] /home/rsshexample> sudo mount_devfs devfs /home/rsshexample/dev
[prunus] /home/rsshexample> mount |grep devfs
devfs on /dev (devfs, local)
devfs on /var/named/dev (devfs, local)
devfs on /home/rsshexample/dev (devfs, local)
[prunus] /home/rsshexample>

7- Finally, we have to copy both /etc/passwd and /etc/master.passwd to the chroot. It is a good idea to edit them and remove all existing accounts except the one(s) for the user(s) that will have access to the chroot. After edited the files, we have to use the pwd_mkdb tool to generate the password database inside the chroot:

[prunus] /home/rsshexample> sudo cp /etc/passwd /etc/master.passwd etc/

[ ... ]

[prunus] /home/rsshexample> pwd_mkdb -d /home/rsshexample/etc /home/rsshexample/etc/master.passwd
[prunus] /home/rsshexample>

NOTE: If we do not use pwd_mkdb, we will get a Unknown user message when trying to log in as the chrooted user.

Testing the chroot

Finally, we could use any sftp client to connect to our server and check the chroot:

[Fenris] ~> sftp rsshexample@Prunus
Connecting to Prunus...
sftp> pwd
Remote working directory: /
sftp> ls -l
drwxr-xr-x    2 0        10006         512 Dec 11 10:01 bin
dr-xr-xr-x    4 0        0             512 Dec 11 10:29 dev
drwxr-xr-x    2 0        10006         512 Dec 11 10:35 etc
drwxr-xr-x    2 0        10006         512 Dec 11 10:32 lib
drwxr-xr-x    2 0        10006         512 Dec 11 09:24 libexec
drwxr-xr-x    6 0        10006         512 Dec 11 09:22 usr
sftp> cd ..
sftp> pwd
Remote working directory: /
sftp> ls -l
drwxr-xr-x    2 0        10006         512 Dec 11 10:01 bin
dr-xr-xr-x    4 0        0             512 Dec 11 10:29 dev
drwxr-xr-x    2 0        10006         512 Dec 11 10:35 etc
drwxr-xr-x    2 0        10006         512 Dec 11 10:32 lib
drwxr-xr-x    2 0        10006         512 Dec 11 09:24 libexec
drwxr-xr-x    6 0        10006         512 Dec 11 09:22 usr

Nice!, our user will be jailed inside his chroot!