CentOS 7 SFTP Setup
I wanted to setup SFTP + non-standard directories + fail2ban and figured an end-to-end solution had to be documented at this point by someone…somewhere. Nope – I had to stitch together several pieces from a handful of stackoverflow posts to finally get a consolidated solution. I made this a bit more complicated than I expected due to changing default location for the public keys, which then caused SELinux to block key exchange at first. That issue has been fixed in the script below and will survive reboots.
Configure CentOS
- Log into new CentOS server with admin rights
sudo yum update -y sudo yum install policycoreutils-python -y sudo mkdir /sftp sudo mkdir /sftpkeys sudo groupadd sftp-external
- Update /etc/ssh/sshd_config
sudo vi /etc/ssh/sshd_config #as far as I can tell this step isn't necessary if you force it again for members of sftp-external match group below #Subsystem sftp /usr/libexec/openssh/sftp-server Subsystem sftp internal-sftp -l VERBOSE #disconnect idle clients after 10 minutes ClientAliveInterval 600 ClientAliveCountMax 0 #Go the end of file and add the following block Match Group sftp-external X11Forwarding no AllowTcpForwarding no AllowAgentForwarding no PermitOpen none PermitTTY no PermitTunnel no ForceCommand internal-sftp -l VERBOSE ChrootDirectory /sftp/%u AuthorizedKeysFile /sftpkeys/%u/.ssh/authorized_keys
- Restart sshd
sudo systemctl restart sshd; systemctl status sshd
Create script to automate account creation, account folders, and account autorized_keys files
- Save the following on your SFTP server as sftpAccountSetup.sh and chmod 755 when complete
#!/bin/bash # Copyright (c) 2017 infiniteloop.io # http://infiniteloop.io # Version 1.0 - April 7, 2017 # # Redistribution and use in source and binary forms, with or without modification, are permitted # provided that the following conditions are met: # 1) Redistributions of source code must retain the above copyright notice, this list of conditions # and the following disclaimer. # 2) Redistributions in binary form must reproduce the above copyright notice, this list of conditions # and the following disclaimer in the documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, # INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE # DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER # IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. function verify-LocalUserAccount () { for arg in $@; do if getent passwd $arg > /dev/null; then printf "[ERROR] $arg local user already exists, exiting...\n" exit else printf "[SUCCESS] $arg local user does not exist\n" fi done } verify-LocalUserAccount $@ #these variables can be changed as needed but be sure to match values from step 1 sftpGroup="sftp-external" sftproot="/sftp" sftpkeys="/sftpkeys" for acct in $@; do sshpath="$sftpkeys/$acct/.ssh" in="$sftproot/$acct/incoming" out="$sftproot/$acct/outgoing" #user account creation printf "\nStart user creation for $acct\n" userdir="/" #change to /$in if you want the account to default to the incoming directory usershell="/sbin/nologin" useradd -M $acct -d $userdir -s $usershell -G $sftpGroup passwd -l $acct #create incoming and outgoing folders mkdir $in -p mkdir $out -p chown $acct:$acct $in chown $acct:$acct $out #create placeholder file for account's public key printf "\nConfigure $sshpath and update folder context\n" mkdir $sshpath -p semanage fcontext -a -t ssh_home_t $sshpath restorecon -v $sshpath touch $sshpath/authorized_keys chown $acct:$acct -R $sshpath chmod 700 $sshpath chmod 600 $sshpath/authorized_keys done
- Call sudo bash sftpAccountSetup account1 account2 account3
- The script will check for existing username and exit if found. Be sure the accounts you attempt to create do not exist already
- Get the public key for the account and add content to /sftpkeys/accountX/.ssh/authorized_keys
- If you or your clients are primarily Windows shops just use puttygen. Be sure to copy text from the puttygen GUI – it should not contain any line breaks
- Attempt connecting from your remote client, remembering to specify the private key
- tail -f /var/log/secure to view log activity
- edit /etc/ssh/sshd_config, uncomment LogLevel, set to DEBUG3 and restart sshd if you’re still having issues with key authentication
Install fail2ban
- Install and setup new config file for fail2ban
sudo yum install epel-release –y sudo yum install fail2ban –y sudo systemctl enable fail2ban sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
- Edit new jail.local in the [sshd] section, the following will block your source IP after 2 bad attempts for 30 seconds. It’s not a bad way to see the fail2ban in action but obviously increase the bantime if using in a production environment.
[sshd] enabled = true port = ssh logpath = %(sshd_log)s backend = %(sshd_backend)s maxretry = 2 bantime = 30
- sudo systemctl restart fail2ban; systemctl status fail2ban
- Use fail2ban-client status sshd to view sshd lockouts
- iptables -L can also display details about the ban
- tail -f /var/log/fail2ban.log to view log activity
- Use fail2ban-client set sshd unbanip x.x.x.x to remove the ban