Vsftpd FTP Server With Virtual Users ( MySQL + PAM )

vsftpd (“Very Secure FTP Daemon”) is an FTP server for Linux and supports PAM (“pluggable authentication modules”). A virtual user cannot login to the Linux system and is considered more secure than using a “real” user whom can login to a Linux system. This guide offers a script that demonstrates yet another way to setup a vsftpd server using PAM to connect to the MySQL server for the database while using virtual users.

While there are many guides offering techniques to install vsftpd, not many offer guides that include the use of SELinux. While most disable SELinux or do not discuss SELinux, leaving the installation somewhat incomplete resulting in end user errors, this guide incorporates SELinux by setting SELinux boolean values. While on the subject of SELinux, a guide or two have suggested using setsebool -P allow_ftpd_full_access on, which would (the way I understand it) open up the system to ftp, however using allow_ftp_home_dir (again the way I understand it), vsftp will have access not to the entire file system, but limited to /home. So this script will use the /home directory.

This script should work as is, however, you may want to make changes to usernames and passwords to fit your needs.

This script is made possible through the efforts of many throughout the years. They are referenced at the bottom of the script. The script has successfully been tested on a CentOS 6.4 on January 4, 2014 and was developed using the following application versions.

# CentOS release 6.4 (Final)
# kernel-version 2.6.32-358.el6.x86_64
# vsftp-version 2.2.2-11.el6_4.1
# mysql-server-version 5.1.71-1.el6
# pam_mysql-version 1:0.7-0.12.rc1.el6

Here is the script.

#!/bin/bash
# install-vsftpd.sh

debugval=1	# debugval=0           A test ftp account is not created.
# debugval=1 [default] A test ftp account is created.

clear

#----------------------------------------------------------------------------------------
# USER INPUT
#----------------------------------------------------------------------------------------

echo -e "Give the root user a password for MySQL [password123]. \c"
read -s MYSQL_PASS
if $MYSQL_PASS; then
MYSQL_PASS=password123
fi &> /dev/null
echo ""

#----------------------------------------------------------------------------------------
# Install vsFTPd, MySQL, and pam_mysql
#----------------------------------------------------------------------------------------

# Install EPEL Repository for pam_mysql which authenticates users against MySQL
rpm -Uvh http://dl.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm
yum install -y vsftpd mysql-server pam_mysql

#----------------------------------------------------------------------------------------
# Configure MySQL
#----------------------------------------------------------------------------------------

# Enable autostart
chkconfig mysqld on && service mysqld start

mysql <<"EOF"
CREATE DATABASE vsftpd;
GRANT SELECT ON vsftpd.* TO 'vsftpd'@'localhost' IDENTIFIED BY 'vsftpdpassword';
FLUSH PRIVILEGES;
USE vsftpd;
CREATE TABLE `accounts` (
`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY ,
`username` VARCHAR( 30 ) NOT NULL ,
`pass` VARCHAR( 50 ) NOT NULL ,
UNIQUE ( `username` )
) ENGINE = MYISAM ;
EOF

if [[ $debugval == 1 ]]; then

# Create a test account
mysql <<"EOF"
USE vsftpd;
INSERT INTO accounts (username, pass) VALUES('user1', md5('secret'));
EOF

fi

# Provide a root password for MySQL
mysqladmin -u root password $MYSQL_PASS

#----------------------------------------------------------------------------------------
# Configure vsFTPd
#----------------------------------------------------------------------------------------

# Enable autostart
chkconfig vsftpd on && service vsftpd start

[ -d /home/users ] || mkdir -p /home/users
ln -s /home/users /mnt/ftpusers

# Create a non-priviledged user called 'vsftpd' with access to /mnt/ftpusers
useradd -G users -s /sbin/nologin -d /mnt/ftpusers vsftpd
usermod -G users -s /sbin/nologin -d /mnt/ftpusers vsftpd

# Backup the vsftpd.conf file
cp /etc/vsftpd/vsftpd.conf /etc/vsftpd/vsftpd.conf-orig

# Customize the vsftpd.conf file
sed -i "s/^.*anonymous_enable.*/anonymous_enable=NO/g" /etc/vsftpd/vsftpd.conf
sed -i "/^xferlog_std_format*a*/ s/^/#/" /etc/vsftpd/vsftpd.conf
sed -i "s/#idle_session_timeout=600/idle_session_timeout=900/" /etc/vsftpd/vsftpd.conf
sed -i "s/#nopriv_user=ftpsecure/nopriv_user=vsftpd/" /etc/vsftpd/vsftpd.conf
sed -i "/#chroot_list_enable=YES/i\chroot_local_user=YES" /etc/vsftpd/vsftpd.conf
echo "

# Here the vsftpd will allow the 'vsftpd' user to login into '/mnt/ftpusers/$USER directory
guest_enable=YES
guest_username=vsftpd
local_root=/mnt/ftpusers/$USER
user_sub_token=$USER
virtual_use_local_privs=YES
user_config_dir=/etc/vsftpd/vsftpd_user_conf" >> /etc/vsftpd/vsftpd.conf

# Create the vsftpd_user_conf directory if it does not exist.
[ -d /etc/vsftpd/vsftpd_user_conf ] || mkdir -p /etc/vsftpd/vsftpd_user_conf

#----------------------------------------------------------------------------------------
# Configure PAM
#----------------------------------------------------------------------------------------

mv /etc/pam.d/vsftpd /etc/pam.d/vsftpd-orig

echo "#%PAM-1.0
session    optional     pam_keyinit.so   force revoke
auth       required     pam_mysql.so     user=vsftpd passwd=vsftpdpassword host=localhost db=vsftpd table=accounts usercolumn=username passwdcolumn=pass crypt=3
account    required     pam_mysql.so     user=vsftpd passwd=vsftpdpassword host=localhost db=vsftpd table=accounts usercolumn=username passwdcolumn=pass crypt=3" > /etc/pam.d/vsftpd

#----------------------------------------------------------------------------------------
# Firewall Exceptions and SeLinux Modifications
#----------------------------------------------------------------------------------------

#Add vsFTPd exception to the firewall
sed -i "/-A INPUT -j REJECT --reject-with icmp-host-prohibited/i -A INPUT -m state --state NEW -m tcp -p tcp --dport 21 -j ACCEPT" /etc/sysconfig/iptables

#Append ip_conntrack_ftp to the IPTABLES_MODULE line in /etc/sysconfig/iptables-config, separated by a space. If not exists.
grep -q ip_conntrack_ftp /etc/sysconfig/iptables-config 2> /dev/null || sed -i "s/IPTABLES_MODULES=\"/IPTABLES_MODULES=\"ip_conntrack_ftp /" /etc/sysconfig/iptables-config

service iptables restart

echo "Setting the SELinux boolean values, may take a minute."
setsebool -P ftpd_connect_db on
setsebool -P ftp_home_dir on
restorecon -R -v /home/users

service vsftpd restart

#----------------------------------------------------------------------------------------
# Post-Installation Comments
#----------------------------------------------------------------------------------------
echo "The installation is complete."

chown -R vsftpd:users /mnt/ftpusers/
chmod -R 700 /mnt/ftpusers/

#----------------------------------------------------------------------------------------
# DEBUG: Create a test account
#----------------------------------------------------------------------------------------

if [[ $debugval == 1 ]]; then
echo "# dirlist_enable=YES
download_enable=YES
# full path to the directory where 'user1' will have access, change to your needs
local_root=/mnt/ftpusers/user1
write_enable=YES" > /etc/vsftpd/vsftpd_user_conf/user1

mkdir -p /mnt/ftpusers/user1
chmod 700 /mnt/ftpusers/user1
chown -R vsftpd:users /mnt/ftpusers
#chown vsftpd:users /mnt/ftpusers/user1
echo ""
LOCAL_IP_VALUE=`ifconfig eth0|awk '/inet addr/ {split ($2,A,":"); print A[2]}'`

echo "-------------------------------------"
echo "FTP Address: " $LOCAL_IP_VALUE
echo "Username: user1"
echo "Password: secret"
echo "-------------------------------------"
fi

# Source(s)
# http://www.unix.com/shell-programming-scripting/152714-mysql-bash.html
# http://stackoverflow.com/questions/5285485/bash-user-input-mysql-login
# http://nixcraft.com/shell-scripting/13225-comment-out-lines-htaccess-using-sed-command.html
# http://www.linuxquestions.org/questions/linux-newbie-8/sed-append-text-to-end-of-line-if-line-contains-specific-text-how-can-this-be-done-684650/
# http://beginlinux.com/blog/2008/11/vsftpd-and-selinux-on-centos/
# https://bugzilla.redhat.com/show_bug.cgi?id=592805
# http://gordonazmo.wordpress.com/2011/05/11/selinux-vsftpd-and-you/
# https://www.centos.org/forums/viewtopic.php?t=5012