Creating an SFTP jail
UserSecurity
As gatekeepers of the data center, Unix administrators sometimes receive a request to create a Secure File Transfer Protocol (SFTP) account that will only allow the user to view files within that directory. SFTP is preferred over the standard FTP in most customer-facing environments because the username and password are not transmitted in cleartext, nor is the data in transit. Standard FTP has provisions within the .ftpaccess
file to create a more restrictive user environment. However, when using SFTP out of the box, users may change directories (cd
) and view (ls
) whatever they choose within the server, even /
.
Danger
To clamp down on users and thereby restrict them to a specific home directory so they can't operate outside of that home directory requires the creation of a chrooted, or "jailed," directory.
"Chroot" is the term for this type of restricted directory. With chroot, users are unable to move outside their "cell" and can only view their surroundings. Just think of how you feel in a cubicle. On Linux, this setup is fairly straightforward. However, for those of us who are tethered to a Solaris environment, this task requires some configuration gymnastics to actually get it done. Like any good Unix disciple, I trolled the Internet for weeks before I found enough bits and pieces to consolidate the fragmented virtual Google filesystem of information into a more contiguous aggregated cookbook method.
Set Your Environment
Before you proceed, the most important step is to ensure your environment variables are set. For newbies, environment variables tell the current shell where to find everything. I prefer to set mine in .bash_profile
in my $HOME
directory; thus, with each new instantiation of a shell, the parameters will remain. Listing 1 shows an excerpt of the crucial variables in my .bash_profile
. Note that this setup assumes you will later install OpenSSH in /usr/local/
.
Listing 1: .bash_profile Variables
01 CC=gcc 02 CPPFLAGS=-I/usr/local/ssl/include 03 EDITOR=vi 04 LD_LIBRARY_PATH=/usr/local/lib:/usr/local/include:/usr/local/lib/sparcv9 05 PATH=/usr/local/bin:/usr/local/mysql:/usr/local/mysql/bin:/usr/local/sparc-sun-solaris2.10/bin:/usr/local/ssl:/usr/local/ssl/lib:/usr/local/sbin:/usr/local/include:/usr/local/include/libxml2:/usr/bin:/usr/sbin:/usr/lib:/usr/openwin/bin 06 LDFLAGS=-lstdc++
You must ensure you have the GNU GCC compiler [1]. Additional dependencies exist, but I'll assume you are familiar with GCC.
Install TCP Wrappers
The next step is to install TCP Wrappers [1], which is a host networking Access Control List (ACL) system used to screen (filter) access to Unix TCP/IP servers. Essentially, this tool enhances the native abilities of inetd, allowing additional logging support and the ability to return messages for each connection. It also permits inetd to accept only specific connections. Inetd [2] is what controls automatic starting of all Internet services, such as FTP, telnet, POP, SMTP, and so on.
Solaris makes installing TCP Wrappers relatively easy if you opt to download the package from the website (Listing 2).
Listing 2: Installing TCP Wrappers
01 bash#> cd /tmp 02 bash#> ls tcp_wrap* 03 tcp_wrappers-7.6.sol10-sparc-local 04 bash#> 05 bash#> pkgadd -d tcp_wrappers-7.6-sol10-sparc-local 06 07 The following packages are available: 08 1 SMCtcpdwr tcp_wrappers 09 (sparc) 7.6 10 11 Select package(s) you wish to process (or 'all' to process all packages). (default: all) [?,??,q]: all 12 13 Processing package instance <SMCtcpdwr> from </home/bpatridge/dev/tcp_wrappers-7.6-sol10-sparc-local> 14 ...
Typically, I'm a source compiling sort of guy, but I found the package more expeditious for TCP Wrappers.
Install and Configure OpenSSH
The next step is to download and install OpenSSH [3]. The OpenSSH suite of software allows you to run an SSL encryption-based web server, allows SSH secure terminal access as opposed to telnet, and provides SFTP, a file- and password-encrypted method of transmitting data between hosts. After downloading, enter:
tar -xzvf openssh-ver.tar.gz
The -xzvf
option extracts and gunzips the file in one command. By default, it installs in /usr/local
, where you can then enter:
./configure --with-tcp-wrappers make make install
Next, I recommend setting the following options into a new sshd_config
. Be sure to back up the original,
cp sshd_config sshd_config.ORIGINAL
which can now be replaced solely with the sshd configuration directives shown in Listing 3. Line 16 specifies that SFTP will listen on port 2202 instead of the default SSH port 22. This option is configurable and left at 2202 for testing. Upon completion of the initial test, you can reset this to Port 22
, then restart OpenSSH.
Listing 3: sshd Configuration
01 bash#>cd /usr/local/etc 02 bash#> echo >sshd_config 03 bash#> vi sshd_config 04 AllowTcpForwarding no 05 ClientAliveCountMax 3 06 ClientAliveInterval 0 07 Compression delayed 08 LoginGraceTime 60s 09 LogLevel DEBUG3 10 MaxAuthTries 2 11 PasswordAuthentication yes 12 PermitEmptyPasswords no 13 PermitRootLogin no 14 PermitTunnel no 15 PermitUserEnvironment no 16 Port 2202 17 Protocol 2 18 StrictModes yes 19 SyslogFacility AUTH 20 TCPKeepAlive yes 21 UseDNS no 22 UsePrivilegeSeparation yes 23 Subsystem sftp /usr/local/libexec/sftp-server 24 Match Group sftponly 25 ChrootDirectory /export/home/jail/sftpuser/home/sftpuser 26 ForceCommand internal-sftp 27 X11Forwarding no 28 AllowTcpForwarding no 29 bash#>
The most critical part is the last stanza (lines 24-28). This section specifies the new chroot directory and limits the user to the internal SSH SFTP process. The last two lines disallow X11 (graphical) forwarding and TCP port forwarding.
Information regarding the other directives can be found within the man pages for sshd_config [4].
Add a New SFTP User
After you've completed the preceding steps, you can configure the new SFTP user. As a proud command-line vi junkie, I prefer the old school method of editing /etc/password
to add a new user:
sftpuser:x:30680:121213:Jail user :/export/home/jail/sftpuser/./home/sftpuser/:/usr/local/libexec/sftp-server
Appended to the user's home directory is ./home/sftpuser
, which is required to signify that the chrooted directory for this user will now be perceived as /home/sftpuser
rather than the full path.
Also, the shell is not a login shell (/bin/bash
, /bin/sh
, etc.) and must be set to the internal sftp-server process (/usr/local/libexec/sftp-server
).
Add group 121213 to /etc/group
,
sftponly::121213:
then set the password for the user:
passwd sftponly ...
Optionally, you can ensure the default umask is set appropriately, if required (i.e., inside the /etc/default/login
).
Create the chrooted Environment
To further simplify the process, create a chrooted environment for your new user, sftpuser, with the jail.ksh
script shown in Listing 4. This script was originally published by Kent Cowgill, but the website has since disappeared. Note that you only need to modify lines 32 and 33. The remaining commands in the script create all the devices necessary for a virtual system environment for the chroot user.Because chroot users must not view anything outside of their own directory, they must have available all the tools and devices necessary to execute the commands ls
, cd
, and the like within their chrooted directory.
Listing 4: jail.ksh
001 #!/bin/ksh 002 003 # script: jail.ksh 004 # version: 1.0 005 # date: 9/27/2002 006 # author: Kent Cowgill 007 # 008 # Description: 009 # This script sets up a minimal jail for chrooted users for ssh and 010 # sftp. Minimal error checking is performed -- Most errors are 011 # harmless: Adding existing groups, creating existing directories, 012 # etc. Please modify the JAILUSER and JAILGROUP variables in the 013 # script according to your specific needs and requirements. 014 # 015 # Disclaimer: Use this script at your own risk. I cannot ensure that 016 # it will perform exactly as described on your system. By using this 017 # script, you acknowledge that you have read and understand all the 018 # commands contained herein, and waive any claim against Kent Cowgill 019 # for any harm to your system. 020 # 021 # (c) 2002 Kent Cowgill. Permission to modify and distribute is 022 # granted on condition the copyright message is included and 023 # modifications are clearly identified. 024 # 025 # For suggestions, additions, and corrections, I thank Alex Kramarov, 026 # Steven M. Christianson, james@firstaidmusic, Gabriele Facciolo, 027 # Eileen Coles, Hugh McLenagh, and Walter G. Aiello. 028 # 029 # For changes, suggestions, corrections, enhancements, comments, or 030 # criticisms, email kent@c2group.net 031 # CHANGE THESE! 032 JAILUSER=jailuser 033 JAILGROUP=jailgroup 034 #################################################################### 035 # DO NOT CHANGE ANYTHING BELOW HERE! 036 #################################################################### 037 038 /usr/sbin/groupadd $JAILGROUP 039 040 mkdir /export/home/jail 041 chown root:$JAILGROUP /export/home/jail 042 chmod 750 /export/home/jail 043 044 /usr/sbin/useradd -g $JAILGROUP -c "Jail user $JAILUSER" \ 045 -d /export/home/jail/$JAILUSER/./home/$JAILUSER -s /bin/sh $JAILUSER 046 047 mkdir /export/home/jail/$JAILUSER 048 chown $JAILUSER:$JAILGROUP /export/home/jail/$JAILUSER 049 050 cd /export/home/jail/$JAILUSER 051 mkdir etc 052 mkdir bin 053 mkdir usr 054 mkdir usr/bin 055 mkdir usr/local 056 mkdir usr/local/bin 057 mkdir usr/local/libexec 058 mkdir usr/local/sbin 059 mkdir usr/local/lib 060 mkdir usr/local/ssl 061 mkdir usr/local/ssl/lib 062 mkdir usr/lib 063 mkdir usr/platform 064 mkdir usr/platform/`uname -i` 065 mkdir usr/platform/`uname -i`/lib 066 mkdir dev 067 mkdir devices 068 mkdir devices/pseudo 069 mkdir home 070 071 cd /export/home/jail/$JAILUSER 072 APPS='bin/cp bin/ls bin/mkdir bin/mv bin/pwd bin/rm bin/rmdir bin/sh' 073 for i in $APPS; do 074 cp /$i ./$i 075 LIBS=`ldd ./$i | awk '{print $3}'` 076 for l in $LIBS; do 077 if [[ ! -d ./`dirname $l` ]]; then 078 mkdir ./`dirname $l` > /dev/null 079 fi 080 cp $l .$l 081 done 082 done 083 084 cd /export/home/jail/$JAILUSER/devices/pseudo 085 mknod mm@0:zero c 13 12 086 mknod mm@0:null c 13 2 087 cd /export/home/jail/$JAILUSER/dev 088 ln -s ../devices/psuedo/mm@0:zero zero 089 ln -s ../devices/pseudo/mm@0:null null 090 091 cd /export/home/jail/$JAILUSER 092 BINS="usr/local/bin/ssh usr/local/libexec/sftp-server usr/local/sbin/sshd usr/local/lib/libz.so usr/local/ssl/lib/libcrypto.so.0.9.6 usr/lib/ld.so.1 usr/platform/`uname -i`/lib/libc_psr.so.1 usr/lib/nss_files.so.1" 093 for i in $BINS; do 094 cp /$i ./$i 095 done 096 097 mkdir /export/home/jail/$JAILUSER/home/$JAILUSER 098 chown $JAILUSER:$JAILGROUP /export/home/jail/$JAILUSER/home/$JAILUSER 099 100 touch /export/home/jail/$JAILUSER/etc/passwd 101 touch /export/home/jail/$JAILUSER/etc/group 102 103 echo "$JAILUSER:x:`/usr/xpg4/bin/id -u $JAILUSER`:`/usr/xpg4/bin/id-g $JAILGROUP`::/home/$JAILUSER:/bin/sh" > \ 104 /export/home/jail/$JAILUSER/etc/passwd 105 106 echo "$JAILGROUP::`/usr/xpg4/bin/id -g $JAILUSER`:$JAILUSER" > \ 107 /export/home/jail/$JAILUSER/etc/group 108 109 echo "done!"
After you have adjusted the file permissions to 755
so the file can be executed, you can issue:
./jail.ksh
At this point, you should have a full chrooted environment in /export/home/jail
,
ls -al /export/home|grep jail drwxr-xr-x 3 root root 96 Sep 28 18:52 jail ls -alR /export/home/jail ...
and the permissions should be root:root,
755
all the way down the directory structure.
Next, you should verify that all the correct files were created by typing:
find /export/home/jail
Starting the sshd (SFTP) Daemon
To verify that the server is listening and to watch for any errors, start sshd
and verify that it works by starting it in debug mode with the -d
option (Listing 5).
Listing 5: Verify sshd
01 bash#> /usr/local/sbin/sshd -d 02 debug1: sshd version OpenSSH_5.6p1 03 debug1: read PEM private key done: type RSA 04 debug1: private host key: #0 type 1 RSA 05 debug1: read PEM private key done: type DSA 06 debug1: private host key: #1 type 2 DSA 07 debug1: rexec_argv[0]='/usr/local/sbin/sshd' 08 debug1: rexec_argv[1]='-d' 09 debug1: Bind to port 2202 on ::. 10 Server listening on :: port 2202. 11 debug1: Bind to port 2202 on 0.0.0.0. 12 Server listening on 0.0.0.0 port 2202.
Now the fun part begins, and you can commence testing:
bash#> sftp -P2202 sftpuser@myserver01 sftpuser@myserver01's password: Connected to myserver01. sftp> pwd Remote working directory: / sftp> ls sftp> cd / sftp> pwd Remote working directory: / sftp>quit #
If all goes well, your users now will be officially chrooted into their own directory, and they will not be able to cd
to any other directory outside of /export/home/jail/sftpuser/home/sftpuser
.