UP | HOME

Restricted SFTP Under Red Hat 5

Table of Contents

1 Setting Up a Restricted SFTP Server on Red Hat 5

This is one way to set up a restricted SFTP server on Red Hat 5. It will allow a user to connect to the server via SFTP, but not allow them to log in via SSH. It will also restrict them to a specific download directory and prevent them from snooping around where they don't belong.

This setup has the advantage of not requiring any software beyond that which comes with Red Hat 5. The down side is that you have to connect to a non-standard port (port 2223 in this example) to make a connection.

This method will also work on Red Hat 6, but it shouldn't be used. The SSH server on Red Hat 6 is newer, and has better ways to achieve the same ends. Google is your friend.

Don't be intimidate by length of these instructions. This is of the "do one step, verify one step" school of instruction. It takes more time, but it's more likely to end with a working solution.

These instructions are inspired by instructions found on Yury V. Zaytsev's web site at http://yury.zaytsev.net. For compatibility with those instructions, the same arbitrary defaults used there will be used here (when convenient).

1.1 Some Arbitrary Defaults

The second SSH server will run on port 2223. Only users in group sftponly will be allowed access. All access will be restricted to directory /srv/sftp-only. The example sftponly user is named badguy. The user you is any user who has normal (non-~sftponly~) access to the server. The SSH server is a Red Hat 5 server named box. The connecting client is named foo.

There is nothing special about any of these values.

2 Overview

The technique used is to run a second SSH server on a different port than the default. The second service will be called sshd-sftponly. It will restrict access to users in group sftponly.

Each user will have their own home directory, but will not be allowed direct access to it. A second, per user, directory is used for file transfer.

The user won't be able to change their own password, that will require a system administrator. If a user wishes to do away with passwords all together, and use SSH public keys, it will require an administrator to install the user's public key in their home directory.

On with the show!

3 Test the Connection

Connect to the normal SFTP server a normal user. This is to make sure that everything is working properly before any changes are made.

you@foo$ sftp you@box

4 Test the Connection on the Second Port

Start the SSH daemon on the second port and connect to it. This is to see if the second port is accessible.

A useful technique is to run the daemon in foreground mode. It will sit waiting for a connection, and will exit after one attempt.

root@box# /usr/sbin/sshd -p 2223 -d
you@foo$ sftp -P 2223 you@box

Note: The sshd command uses a lowercase "p". The sftp command uses an uppercase "P".

5 Create the sftponly Group

groupadd sftponly

6 Create the Download Directories

The download directory is set up as a "chroot" jail. That is to say, the user will not be able to access any files above /srv/sftp-only. To enforce this, all the directories leading up to, and including the jail directory have to be owned by root.

root@box# ls -ld /
  drwxr-xr-x ... root root ... /
root@box# ls -ld /srv
  drwxr-xr-x ... root root ... /srv

Create the chroot directories.

root@box# mkdir -p /srv/sftp-only/{home,etc}

Make group sftponly the group owner of the files.

root@box# chgrp sftponly /srv/sftp-only
root@box# chgrp sftponly /srv/sftp-only/{home,etc}

Allow root to have full access, and restrict group sftponly to "execute" only access. Execute access allows access to known file names, but prevents listing of files within the directory.

root@box# chmod 710 /srv/sftp-only
root@box# chmod 710 /srv/sftp-only/{home,etc}

Create empty password and group files. Note: These files are only used to map numeric user/group IDs to names when doing a long listing in sftp. They're not used for logging in or mapping the user's UID or GID.

root@box# touch /srv/sftp-only/etc/{group,passwd}
root@box# chmod 640 /srv/sftp-only/etc/{group,passwd}
root@box# chgrp sftponly /srv/sftp-only/etc/{group,passwd}

Verify the permissions.

root@box# find /srv/sftp-only -printf '%M %u %g %p\n' | sort

  drwx--x--- root sftponly /srv/sftp-only
  drwx--x--- root sftponly /srv/sftp-only/etc
  drwx--x--- root sftponly /srv/sftp-only/home
  -rw-r----- root sftponly /srv/sftp-only/etc/group
  -rw-r----- root sftponly /srv/sftp-only/etc/passwd

If user access to other user's download directories is required, change home's permissions to allow group read.

root@box# chmod 750 /srv/sftp-only/home

7 Create a Test User

Go to the Adding a New User section and create a test user. Then return here.

For the rest of the install we'll call this new user badguy.

8 Create the sshd-sftponly Configuration File

Copy the existing sshd configuration file to sshd-sftponly.

root@box# cp -i /etc/ssh/sshd{,-sftponly}_config

Verify the file permissions.

root@box# ls -l /etc/ssh/sshd{,-sftponly}_config
  -rw------- ... root root ... /etc/ssh/sshd_config
  -rw------- ... root root ... /etc/ssh/sshd-sftponly_config

Note: The permissions may not be exactly the same as shown above. The important thing is that the permissions and owners match each other.

9 Edit the sshd-sftponly Configuration File

Edit /etc/ssh/sshd-sftponly_config and replace/add the configuration commands below.

This can be the tricky part of the install. There is no way to determine what special configurations are already in the existing SSHD configuration. This may require some trial an error to get right.

Port 2223
PidFile /var/run/sshd-sftponly.pid
Subsystem       sftp    internal-sftp
AllowGroups sftponly
AllowTcpForwarding no
X11Forwarding no
ChrootDirectory /srv/sftp-only
ForceCommand internal-sftp

9.1 Port 2223

Change the service port to 2223.

9.2 PidFile /var/run/sshd-sftponly.pid

Change the process ID file. This will come into play when the new server is turned into a service later in the instructions.

9.3 Subsystem sftp internal-sftp

When running SFTP, use the internal version of the SFTP server instead of the standalone executable. The internal SFTP server is compatible with the standalone version, and doesn't require any additional configuration to run in a chroot jail.

9.4 AllowGroups sftponly

Only allow users in group sftponly to access this server.

9.5 AllowTcpForwarding no

Don't allow users to forward TCP packets through this server. This is to prevent users from proxy connecting to other servers in your domain.

9.6 X11Forwarding no

Don't allow users to forward X11 packets through this server. This is to prevent users from proxy connecting to other servers in your domain.

9.7 ChrootDirectory /srv/sftp-only

Once a user is authorized, restrict them to this directory and below. Set their initial directory to /srv/sftp-only/home/badguy.

9.8 ForceCommand internal-sftp

Force the user to use the internal-sftp server once they've connected. This prevents them from running the ssh commands after log in.

If they try get a log in shell, they'll get

/sbin/nologin: No such file or directory

10 Test the Configuration

At this point it should be possible to log in as user badguy.

root@box# /usr/sbin/sshd -d -f /etc/ssh/sshd-sftponly_config
you@foo$ sftp -P 2223 badguy@box

Try uploading a file.

11 Make a New Daemon

Use a symbolic link to the SSHD executable to create a new name for the daemon.

root@box# ln -s /usr/sbin/sshd /usr/sbin/sshd-sftponly

If SFTP uses PAM (/etc/ssh/sshd-sftponly_config has UsePAM yes), then link /etc/pam.d/sshd-sftponly to /etc/pam.d/sshd. It's IMPORTANT that care is used when working with /etc/pam.d/sshd. Do not log out of the server until you're certain that sshd is still working properly.

root@box# ln -s /etc/pam.d/sshd /etc/pam.d/sshd-sftponly

Test the new command.

root@box# /usr/sbin/sshd-sftponly -d -f /etc/ssh/sshd-sftponly_config
you@foo$ sftp -P 2223 badguy@box

12 Make an sshd-sftponly Service File

A Red Hat service is simply a daemon that is controlled via a standard format init file. The easiest way to create a new service is to copy an existing service file.

Copy the existing /etc/init.d/sshd file.

root@box# cp -ip /etc/init.d/sshd /etc/init.d/sshd-sftponly

Edit /etc/init.d/sshd-sftponly and change any references from sshd to sshd-sftponly. Below is an example /etc/init.d/sshd-sftponly file based on a standard Red Hat 5 install. It can serve as a reference.

Test the new service file.

root@box# /etc/init.d/sshd-sftponly status
root@box# /etc/init.d/sshd-sftponly start
root@box# /etc/init.d/sshd-sftponly status
root@box# /etc/init.d/sshd-sftponly stop
root@box# /etc/init.d/sshd-sftponly status

Connect to the daemon twice to verify that it's running, and doesn't exit after one connection.

root@box# /etc/init.d/sshd-sftponly start
you@foo$ sftp -P 2223 badguy@box
you@foo$ sftp -P 2223 badguy@box
root@box# /etc/init.d/sshd-sftponly stop

12.1 The Example Service File

This script is derived from the original /etc/init.d/sshd script. If the existing sshd script has the same checksum, then this might be a valid drop in replacement.

root@box# sha1sum /etc/init.d/sshd-sftponly
  81952ac410cbca17284d892eb4dbda75fdada8b6  sshd-sftponly

In this service file, the directory /var/empty/sshd-sftponly/etc needs to be created before running the script for the first time.

root@box# mkdir -p /var/empty/sshd-sftponly/etc

This is the changed file. All the changes are marked with "DLW".

#!/bin/bash
#
# DLW 170113_124907 modified to run sshd in sftp only mode.
# Init file for OpenSSH server daemon
#
# chkconfig: 2345 55 25
# description: OpenSSH server daemon \
# modified to run in sftp only mode.
#
# DLW 170113_124907 Note: We use the same keys that normal sshd uses.
# processname: sshd-sftponly
# config: /etc/ssh/ssh_host_key
# config: /etc/ssh/ssh_host_key.pub
# config: /etc/ssh/ssh_random_seed
# config: /etc/ssh/sshd_config-sftponly
# pidfile: /var/run/sshd-sftponly.pid

# source function library
. /etc/rc.d/init.d/functions

# pull in sysconfig settings
# DLW 170113_124907
[ -f /etc/sysconfig/sshd-sftponly ] && . /etc/sysconfig/sshd-sftponly

RETVAL=0
prog="sshd-sftponly"

# Some functions to make the below more readable
KEYGEN=/usr/bin/ssh-keygen
SSHD=/usr/sbin/sshd-sftponly
RSA1_KEY=/etc/ssh/ssh_host_key
RSA_KEY=/etc/ssh/ssh_host_rsa_key
DSA_KEY=/etc/ssh/ssh_host_dsa_key
PID_FILE=/var/run/sshd-sftponly.pid
# DLW 170113_124907 I set OPTIONS here.
#     Should it be moved to /etc/sysconfig/sshd-sftponly?
OPTIONS=" -f /etc/ssh/sshd-sftponly_config"

runlevel=$(set -- $(runlevel); eval "echo \$$#" )

do_rsa1_keygen() {
        if [ ! -s $RSA1_KEY ]; then
                echo -n $"Generating SSH1 RSA host key: "
                rm -f $RSA1_KEY
                if $KEYGEN -q -t rsa1 -f $RSA1_KEY -C '' -N '' >&/dev/null; then
                        chmod 600 $RSA1_KEY
                        chmod 644 $RSA1_KEY.pub
                        if [ -x /sbin/restorecon ]; then
                            /sbin/restorecon $RSA1_KEY.pub
                        fi
                        success $"RSA1 key generation"
                        echo
                else
                        failure $"RSA1 key generation"
                        echo
                        exit 1
                fi
        fi
}

do_rsa_keygen() {
        if [ ! -s $RSA_KEY ]; then
                echo -n $"Generating SSH2 RSA host key: "
                rm -f $RSA_KEY
                if $KEYGEN -q -t rsa -f $RSA_KEY -C '' -N '' >&/dev/null; then
                        chmod 600 $RSA_KEY
                        chmod 644 $RSA_KEY.pub
                        if [ -x /sbin/restorecon ]; then
                            /sbin/restorecon $RSA_KEY.pub
                        fi
                        success $"RSA key generation"
                        echo
                else
                        failure $"RSA key generation"
                        echo
                        exit 1
                fi
        fi
}

do_dsa_keygen() {
        if [ ! -s $DSA_KEY ]; then
                echo -n $"Generating SSH2 DSA host key: "
                rm -f $DSA_KEY
                if $KEYGEN -q -t dsa -f $DSA_KEY -C '' -N '' >&/dev/null; then
                        chmod 600 $DSA_KEY
                        chmod 644 $DSA_KEY.pub
                        if [ -x /sbin/restorecon ]; then
                            /sbin/restorecon $DSA_KEY.pub
                        fi
                        success $"DSA key generation"
                        echo
                else
                        failure $"DSA key generation"
                        echo
                        exit 1
                fi
        fi
}

do_restart_sanity_check()
{
        # DLW 170113_124907 Added the $OPTIONS variable.
        $SSHD -t $OPTIONS
        RETVAL=$?
        if [ ! "$RETVAL" = 0 ]; then
                failure $"Configuration file or keys are invalid"
                echo
        fi
}

start()
{
        # Create keys if necessary
        do_rsa1_keygen
        do_rsa_keygen
        do_dsa_keygen

        # DLW 170113_124907
        cp -af /etc/localtime /var/empty/sshd-sftponly/etc

        echo -n $"Starting $prog: "
        $SSHD $OPTIONS && success || failure
        RETVAL=$?
        # DLW 170113_124907
        [ "$RETVAL" = 0 ] && touch /var/lock/subsys/sshd-sftponly
        echo
}

stop()
{
        echo -n $"Stopping $prog: "
        if [ -n "`pidfileofproc $SSHD`" ] ; then
            killproc $SSHD
        else
            failure $"Stopping $prog"
        fi
        RETVAL=$?
        # if we are in halt or reboot runlevel kill all running sessions
        # so the TCP connections are closed cleanly
        if [ "x$runlevel" = x0 -o "x$runlevel" = x6 ] ; then
            killall $prog 2>/dev/null
        fi
        # DLW 170113_124907
        [ "$RETVAL" = 0 ] && rm -f /var/lock/subsys/sshd-sftponly
        echo
}

reload()
{
        echo -n $"Reloading $prog: "
        if [ -n "`pidfileofproc $SSHD`" ] ; then
            killproc $SSHD -HUP
        else
            failure $"Reloading $prog"
        fi
        RETVAL=$?
        echo
}

case "$1" in
        start)
                start
                ;;
        stop)
                stop
                ;;
        restart)
                stop
                start
                ;;
        reload)
                reload
                ;;
        condrestart)
                # DLW 170113_124907
                if [ -f /var/lock/subsys/sshd-sftponly ] ; then
                        do_restart_sanity_check
                        if [ "$RETVAL" = 0 ] ; then
                                stop
                                # avoid race
                                sleep 3
                                start
                        fi
                fi
                ;;
        status)
                # DLW 170113_124907
                status -p $PID_FILE openssh-daemon-sftponly
                RETVAL=$?
                ;;
        *)
                echo $"Usage: $0 {start|stop|restart|reload|condrestart|status}"
                RETVAL=1
esac
exit $RETVAL

13 Turn sshd-sftponly into a Full Service

Tell the server that a new service is available. This will also tell the server that service sshd-sftponly should be started on reboot.

chkconfig --add sshd-sftponly

root@box# service sshd-sftponly start
root@box# service sshd-sftponly status
root@box# service sshd-sftponly restart

Test it one last time.

you@foo$ sftp -P 2223 badguy@box

14 Block sftponly from Normal SFTP

Edit /etc/ssh/sshd to prevent its use by users in group sftponly.

DenyGroups sftponly

Restart the normal server.

root@box# service sshd condrestart

NOTE: Make sure that normal sshd is still running properly before logging out.

you@foo$ ssh box

At this point the install is complete.

15 Adding a New User

15.1 There's 2 Places Like Home

The user will have 2 home directories.

The user's normal home directory holds their configuration files and is locked down to prevent log in.

The user's chrooted login home directory is used by SFTP to transfer files.

15.2 Create the New User

Create a user with normal home directory, but without the ability to log in.

root@box# useradd badguy --comment "Badly Guyson's SFTP only account" \
            --create-home --shell /sbin/nologin

If the user will log in with a password, the set their password. The user won't have the ability to change their own password.

root@box# passwd badguy

If the user will log in using an SSH public key, create a random password just to put something in. Then lock the account.

root@box# passwd badguy
root@box# passwd -l badguy

If the password should never expire, make this change.

root@box# chage -M 99999 badguy
root@box# chage -l badguy

  ...
  Password expires                                  : never
  Password inactive                                 : never
  Account expires                                   : never
  ...
  Maximum number of days between password change    : 99999
  ...

15.2.1 Setting up SSH Public Key Access

Setting up SSH public key access is done as it is for any normal account. The users public key is stored in their normal home directory.

root@box# mkdir ~badguy/.ssh
root@box# chmod 700 ~badguy/.ssh
root@box# chown badguy:badguy ~/.ssh
root@box# touch ~badguy/.ssh/authorized_keys
root@box# chmod 400 ~badguy/.ssh/authorized_keys
root@box# chown badguy:badguy ~badguy/.ssh/authorized_keys

Edit ~badguy/.ssh/authorized_keys and install the key.

15.3 Configure the user for sftponly

Add the user to group sftponly.

root@box# usermod --append --groups sftponly badguy
root@box# groups badguy
  badguy : badguy sftponly

Create the chroot home.

root@box# mkdir /srv/sftp-only/home/badguy
root@box# chown badguy:sftponly /srv/sftp-only/home/badguy
root@box# chmod 700 /srv/sftp-only/home/badguy

Add the user to the local passwd and group file.

root@box# grep '^badguy' /etc/passwd >> /srv/sftp-only/etc/passwd
root@box# grep '^badguy' /etc/group >> /srv/sftp-only/etc/group

Date: 2017-01-25T12:38-0500

Author: Dale Wiles

Org version 7.9.3f with Emacs version 24

Validate XHTML 1.0