OpenSSH, sftp, groups and a jail

Recently, my boss came to me and wanted to update a very old Redhat 9 Sftp server.   There was one little snag tho, the old server was using a jailed approach.  Seeing as this server was quite old, and had a patched SSH binary installation to support the jailed configuration, I figured I was in for a hefty amount of work.  I couldn’t have been more mistaken.  The good folks over at OpenSSH have already implemented the ability to run ssh in a jailed configuration.  The other gotcha was that we also wish to use shared keys, so one more thing, right ;-).

The relevant sshd_config directives are:

Match

ChrootDirectory

Subsystem

ForceCommand

These three are the only ones that really pertain directly to the jail scenario I was building towards.  YMMV.  If all else fails, the OpenSSH guys (and OpenBSD related projects in general) tend to have very good man pages.  You can find the man pages for OpenSSH at http://www.openssh.org/manual.html.

So, we start out by unpacking and compiling SSH.  This is really only if you want to maintain the installation separate from the default system.  In our case, Suse was lagging substantially behind the formal project releases (SLES 10.x was/is using OpenSSH 4.x).

tar zxvf openssh-5.4p1.tar.gz

cd openssh-5.4p1

./configure –prefix=/opt/[your_dest_dir]

make

sudo make install

Once I completed the above steps, I had my new installation of OpenSSH ready.  Now to configure everything and set up our jail.  For this, I needed to build out the jail structure.  I opted to go with /www/jail.  As part of this, I also needed to have the ability for shared keys, so decided on /www/jail/.ssh as the directory for they keys to exist.

mkdir /www

mkdir /www/jail

mkdir  /www/jail/.ssh

Next, I started out with a base sshd_config.  This is per defaults that I have found to work over the years.  Note that you should change [install_dir] to the directory structure you specified as the argument to –prefix.  I modified the port so as not to conflict with the default installation residing at port 22 and also tuned the number of sessions allowed.  Some other things that I modified included X-tunneling, permit user environment, dns resolving of remote clients, etc.

Port 2022
Protocol 2
ListenAddress 0.0.0.0
HostKey /[install_dir]/etc/ssh_host_rsa_key
HostKey /[install_dir]/etc/ssh_host_dsa_key
KeyRegenerationInterval 1h
ServerKeyBits 1024
SyslogFacility AUTH
LogLevel INFO
LoginGraceTime 2m
PermitRootLogin no
StrictModes yes
MaxAuthTries 3
MaxSessions 20
MaxStartups 20
PubkeyAuthentication yes
PasswordAuthentication yes
ChallengeResponseAuthentication no
X11Forwarding no
PrintMotd yes
PrintLastLog yes
TCPKeepAlive yes
UseLogin no
UsePrivilegeSeparation yes
PermitUserEnvironment no
UseDNS no
PidFile /[install_dir]/var/run/[pidname].pid

The next thing that is needed in the sshd_config file is to setup some specifics about our sftp server.

Banner /www/jail/sftp.banner
Subsystem    sftp internal-sftp
AuthorizedKeysFile /www/jail/.ssh/%u

Note above that we have set the Subsystem to use ‘sftp internal-sftp’, versus ‘sftp /[install_dir]/libexec/sftp-server’ as is default in the sshd_config file.  This is where the magic really happens.  As per the man page:

             Alternately the name ``internal-sftp'' implements an in-process
             ``sftp'' server.  This may simplify configurations using
             ChrootDirectory to force a different filesystem root on clients.

Ok, so what does this mean? It means that instead of having to replicate all of our libraries, etc into a jail as has been historically necessary; we can specify internal-sftp and do not need to have the libraries/binaries inside the jail as the in-process sftp server will inherit the libraries already loaded/accessed by the parent sshd process.  Simply stated, this makes it very easy to implement, and minimizes the amount of churn we’d typically have to go through.

Next, we save our file, and start our server up to test.  Note we are issuing ‘-d’ to put the server into debug mode.

/opt/[install_dir]/sbin/sshd -d -f /opt/[install_dir]/etc/sshd_config

you should see some debug output ending with something like:

debug1: Bind to port 2022 on 0.0.0.0.
Server listening on 0.0.0.0 port 2022.

Hit CTRL+C to exit the server.  Next we need to create a couple of groups and some users to place in the groups.

groupadd jailedgroup1

useradd -g jailedgroup1 -c “Jailed user #1” -d / -s `which false` username1

useraadd -g jailedgroup1 -c “Jailed user #2” -d / -s `which false` username2

groupadd jailedgroup2

useradd -g jailedgroup2 -c “Jailed User #1 Group 2” -d / -s `which false` username1g2

passwd username1

passwd username2

passwd username1g2

So lets review what we’ve done.  We have a working ssh installation, and have created two groups (jailedgroup1 and jailedgroup2) , added two users to the first group and one user to the second group.  For each of the users, we set their home directory to / (root).  Normally, this may seem odd, but there is a method to the madness.

When a user logs into a jailed service, we want them to go to the root of that directory, instead of treeing things out as in [jaildir/home/username].  This is of course open to implementation so may work differently depending on the requirements.  For me, I wanted my users to share the same ftp area, so this made the most sense.  At this point, we know that we are going to jail users/groups under /www/jail, but have not decided on the exact directories for each group.  For the purposes of this document, we’ll pick something fairly straight forward.  Lets create two directories, named jailedgroup1 and jailedgroup2.

mkdir /www/jail/jailedgroup1

mkdir /www/jail/jailedgroup2

The next piece of this puzzle is to setup our group definitions in our sshd_config file.  Lets open the file back up and add the following sections.

Match Group jailedgroup1
X11Forwarding no
AllowTcpForwarding no
ForceCommand internal-sftp
ChrootDirectory /www/jail/jailedgroup1

Match Group jailedgroup2
X11Forwarding no
AllowTcpForwarding no
ForceCommand internal-sftp
ChrootDirectory /www/jail/jailedgroup2

Save and exit the sshd_config file.  We now want to perform a simple test.  Lets start sshd as we did earlier.

/opt/[install_dir]/sbin/sshd -d -f /opt/[install_dir]/etc/sshd_config

We should once again have a working sshd server listening on port 2022.  From another window on that server, execute the following.

sftp -o port=2022 username1@localhost

You should be prompted to accept the keys, choose yes here and enter the password for username1.  Once logged in, execute a `pwd` to verify you are in the / (root) area of the “remote” server.  You can then do a ls -l / to verify no files exist in the root.

In a third window, you can log in and create a directory structure, and re-run the ls to verify it shows up, etc (as in mkdir /www/jail/jailedgroup1/somedir).  The nice part here is that if you set your group permissions of the jailed directory, then all of the users in jailedgroup1 will be able to update files in the same directory (chgroup jailedgroup1 /www/jail/jailedgroup1 && chmod 775 /www/jail/jailedgroup1).

Test with other users from the group to observe the behavior.  Login as username1g2 to verify you show up in the other jailed directory.  Note that each time you log out, your debug sshd service listening on port 2022 will exit.  This is normal behavior.  If you want to login with multiple users and verify the binary spawns out a new child process, execute the sshd command without the -d argument.

At this point, its just about wrapping everything up with a couple of scripts which I will leave as an exercise for the reader (hint: /etc/init.d/sshd already exists, why not use that as a base?).

The next phase for me is to implement shared keys.  This is largely open depending on what the client is using to connect to the sftp server.  In our case, I am putting the keys in /www/jail/.ssh/[username].  The only gotcha here is to make sure that the shared key file for that user is owned by that user.  Testing in debug mode will go a long way in helping to determine any issues (key based logins included).

Hope someone else finds this useful.

Leave a comment