[an error occurred while processing this directive]

HOWTO: Creating a jailed SFTP Server on FreeBSD

SFTP is a variant of SSH that offers many advantages over the old FTP: first and foremost, it's secure. All data that's being transmitted is being encrypted. With FTP, nothing is encrypted, not even the passwords. The second advantage is that it's a much simpler protocol as for as network connections go. There's one single TCP connection from the client to the server, and that's it, making the job much easier for firewall administrators.

There is, however, a drawback: There are hardly any dedicated SFTP servers around, the most common implementation is as a sub-system of an SSH server. Giving someone an SFTP account on such a server means having to set up a system account, giving them an SSH account as well, and last but not least, making the entire file system available to them! Clearly, these are undesirable for a SFTP server that's supposed to serve data, not let users poke around the system.

There is, however, a solution: Using FreeBSD jails we can isolate the SFTP server from the rest of the system: We don't have to add system accounts for its users, they won't get shell access, and they won't be able to poke around the file system.

1. Setting up SSH

Once we've started the jail, the SSH server within it will not be able to access any files outside the jail. This means we must copy all files which are necessary to run sshd into the jail: the sshd binary, the libraries it depends on and the configuration files.

You can figure out which libraries are needed to run a program with ldd(1). From the ldd(1) manpage:

The ldd utility displays all shared objects that are needed to run the given program or to load the given shared object.

On my system, ldd(1) prints the following:

$ ldd /usr/sbin/sshd /usr/sbin/sshd: libssh.so.2 => /usr/lib/libssh.so.2 (0x800655000) libutil.so.4 => /lib/libutil.so.4 (0x80078e000) libz.so.2 => /lib/libz.so.2 (0x80089b000) libwrap.so.3 => /usr/lib/libwrap.so.3 (0x8009ae000) libpam.so.2 => /usr/lib/libpam.so.2 (0x800ab7000) libgssapi.so.7 => /usr/lib/libgssapi.so.7 (0x800bbf000) libkrb5.so.7 => /usr/lib/libkrb5.so.7 (0x800cce000) libasn1.so.7 => /usr/lib/libasn1.so.7 (0x800e12000) libcom_err.so.2 => /usr/lib/libcom_err.so.2 (0x800f3b000) libroken.so.7 => /usr/lib/libroken.so.7 (0x80103d000) libcrypto.so.3 => /lib/libcrypto.so.3 (0x80114b000) libcrypt.so.2 => /lib/libcrypt.so.2 (0x801391000) libc.so.6 => /lib/libc.so.6 (0x8014aa000) libmd.so.2 => /lib/libmd.so.2 (0x8016a7000)

Since copying all those libs into the jail would mean lots of typing, I've created a shell script that sets up the jail for us: createjail.sh . Save it to disk and run it as root, like this:

$ ./createjail.sh /usr/sbin/sshd

It will now create the jail directory, /usr/local/jail/sshd, and copy the sshd binary as well as the required libraries (and the library loader, ld-elf.so) into it. It will also set up some /var bits. What remains to be done is copying /usr/libexec/sftp-server and /etc/ssh/sshd_config into the jail.

cd /usr/local/jail/sshd cp -p /usr/libexec/sftp-server libexec mkdir etc mkdir etc/ssh cp -p /etc/ssh/sshd_config etc/ssh

These are not yet all the files necessary to run sshd. What's missing is the device file system and the user databases. We'll set those up in a later chapter, however you can create the mount point for the device file system now:

mkdir /usr/local/jail/sshd/dev

2. Configuration

First, load /usr/local/jail/etc/ssh/sshd_config into an editor, since some changes will be necessary. I'll assume your sshd_config still contains it's default configuration. If you've heavily modified, you can get a pristine one from /usr/src/crypto/openssh/sshd_config, if you have the FreeBSD source installed, or from CVSWeb (if you use CVSWeb, make sure you pick the sshd_config version that matches your sshd!) Now change the file as follows:

sshd will need it's various cryptographic keys to work. You will have to create those manually with the following commands:

$ cd /usr/local/jail/sshd/etc/ssh $ ssh-keygen -t rsa1 -b 1024 -f ssh_host_key -N '' $ ssh-keygen -t dsa -f ssh_host_dsa_key -N '' $ ssh-keygen -t rsa -f ssh_host_rsa_key -N ''

Next, the user database. We only really need the sshd user for privilege separation, but adding the root user adds some convenience (you'll see "root" as owner from your sftp client, instead of just "0". And since setting up an SFTP server without any users that can log into it is pointless, we'll add a demo user too. Run these commands to start editing the password database:

$ cd /usr/local/jail/sshd/etc $ touch master.passwd $ vipw -d .

Now put the following text into the file:

root:*:0:0::0:0:Charlie &:/var/empty:/nonexistant sshd:*:22:22::0:0:Secure Shell Daemon:/var/empty:/nonexistant demo:$1$vxSehNoK$f9nzu9ST82N6pET2iW2Gm0:65000:65000::0:0:Demo User:/home/demo:/libexec/sftp-server

A couple of notes: you'll see that root's passwd entry is non-functional: he has no shell and no password. That's ok, we don't need to log in as root. The password for the demo user is "demo". I've generated the password hash by running:

$ openssl passwd -1 demo

We'll need to create the home directory for the demo user, or logging in won't work. If you just want people to be able to download from your SFTP server, leave root as demo's home directory's owner, so demo can't write to it. Otherwise, give demo ownership (or create an "upload" directory, and only give demo ownership of that directory). We'll keep it simple for now:

$ cd /usr/local/jail/sshd $ mkdir home $ mkdir home/demo

Now the jail should be ready for action!

3. Start Scripts

To start the jailed sshd, we need to do thee things: Mount /dev inside the jail, run jail jail(8) and alias the jail's IP address to a network interface. To facilitate this process, we're going to create a startup script, that also allows us to gracefully stop the jail.

First, Here's a file containing the start stop routines: jail.subr. Copy it into /usr/local/etc/rc.d. Next, create a new file, /usr/local/etc/rc.d/jail-sshd.sh and load it into an editor. Then put the following text into it:

#! /bin/sh jail_if="vr0" jail_dir="/usr/local/jail/sshd" jail_devdir="/usr/local/jail/sshd/dev" jail_devfs_rules="hide path *random* unhide path null unhide" jail_hostname="jail-sshd" jail_ip="" jail_command="/bin/sshd" pid_file="/usr/local/jail/sshd/var/run/sshd.pid" . /usr/local/etc/rc.d/jail.subr

You'll need to adjust the values to match your system:

Now make the script executable with:

$ chmod 755 /usr/local/etc/rc.d/jail-sshd.sh

Some additional notes: We're mounting a devfs into the jail. Since this exposes all devices nodes inside the jail, We also specify a ruleset that shows just the few nodes necessary to run sshd: /dev/random, /dev/urandom and /dev/null.

Alright, that's it! You can now start your jailed SFTP server with:

$ /usr/local/etc/rc.d/jail-sshd.sh start

Only the demo user will be able to log in, and he'll only be able to do so with an SFTP client. SSH access is disabled. He will be able to browse around the file system inside the jail, but there will be nothing interesting to see.

4. Notes

Copyright © 2005 Benjamin Lutz

Permission is granted to copy, distribute and/or modify this document under the terms of the Creative Commons Attribution 2.0 license.

5. Changelog

[an error occurred while processing this directive]