PDA

View Full Version : vsftpd how-to with mangement script



donh3
Jan 20th 2009, 02:19 AM
Hi,

I setup vsftpd because I needed a really secure FTP setup. Here is the how-to that came out of it. I also needed a way to mange it via ssh and I couldn't find anything on the net so I wrote this script. Hope someone else finds it useful.

Don H.



#!/bin/bash
# writen for vsftpd 2.06, MySQL 5,
# Version 1.01 2008-10-30 by Don Hess
# This script will provide a menu to manipulate the mysql database
# and manage the files for all type of user administration. It is
# intended that the server only need ftp and ssh open and you can secure
# ssh to lock out IP address apon a certain number of failures. The mysql
# should be setup similar to this how-to http://www.howtoforge.com/vsftpd_mysql_debian_etch
# with the addition of a description field in the db.


chrootftploc=/storage/sharedfiles/ftp # ftp root directory
vsftpduserconfloc=/etc/vsftpd_user_conf # location of user specific config override files
vsftpduserconflocdef=$vsftpduserconfloc/default # location of default template for above
vsftpsysuser=vsftpdvirtual # the system user that vsftpd runs under
myuserlogin="" # mysql login this script will use
myuserpass="" # "" "" ""
db="vsftpd_db" # database used to store our information
mysqlhost="localhost" # host computer for mysql server
mysqlloginstr="" # used in sql queries instead of long string, set in getmysqllogin function
mysqltables="username, LEFT(pass, 6) AS pass, description" #used inplace of * for SELECT statements so password display is shorter.
# mysql syntax reference http://dev.mysql.com/doc/refman/5.0/en/sql-syntax-data-manipulation.html


mainprompt()
{
USERINPUT=1
echo "-------------------------------"
echo "vsftp mysql database utility"
echo ""
echo "1 Query user(s)"
echo "2 Add new user"
echo "3 Update user"
echo "4 Delete user"
echo "5 Change this scripts MySQL login"
echo "6 Quit"
echo ""
echo -n "--> "
read USERINPUT
}
hideechoing()
{
local stty_orig=`stty -g` # set the original stty state to variable
stty -echo # turn off terminal echoing so we don't see password
read hidinput; # read user input
sanitizeinput "$hidinput" password; # sanitize input
stty $stty_orig # put terminal back the way it was
}
sanitizeinput()
{
# $1 is input to be sanitized
# $2 tells us which options to use to sanitize
parm1=`echo "$1" | tr -d '[\000-\037][\041-\054]\057[\072-\077]\100[\133-\136]\140[\173-\176]' | tr -d '[:cntrl:]'`;
# remove non-alpha characters using octets, http://en.wikipedia.org/wiki/ASCII
# only non-alpha characters allowed are space, underscore "_", hyphen "-", and period "."
parm2="$2"
case "$parm2" in
username)
UNIN=`echo $parm1 | tr -d '[:space:]' | tr '[:upper:]' '[:lower:]' | sed 's/^\(..............................\).*$/\1/'`;; # use sed to crop to 30 chars
password)
PASSIN=`echo $parm1 | tr -d '[:space:]' | sed 's/^\(..............................\).*$/\1/'`;; # use sed to crop to 30 chars
description)
DESCIN=`echo $parm1 | sed 's/^\(........................................\).*$/\1/'`;; # use sed to crop to 40 chars
* ) echo -n "";;
esac
}
getmysqllogin()
{
echo "You must enter the connection information for MySQL before continuing."
echo -n "Enter MySQL username (case sensitive): "; read aa;
myuserlogin=`echo "$aa"`;
echo -n "Enter MySQL password (case sensitive): "; # this is a one-off hidding of password because we may need special chars
local stty_orig=`stty -g` # set the original stty state to variable
stty -echo # turn off terminal echoing so we don't see password
read bb # read user input
stty $stty_orig # put terminal back the way it was
myuserpass=`echo "$bb"`;
mysqlloginstr="--host=$mysqlhost --user="$myuserlogin" --password="$myuserpass" --database="$db"" #reset the login variable with new data
echo ""; echo "";
echo "Username and password for MySQL set."
echo Using MySQL host "'$mysqlhost'" with database "'$db'".
}
printusers()
{
# --skip-column-names removes coloumn headers
# mysql --host=host_name --user=user_name --password=your_password --database=dbname -e "statement;"
clear
echo -n "Input at least part of the username or press [Enter] key to show all: "; read UNIN; sanitizeinput "$UNIN" username;
if [ "$UNIN" = "" ]; then
echo ""
echo "First 1000 ftp users in database: "
mysql $mysqlloginstr -e "SELECT $mysqltables FROM accounts a LIMIT 0,1000;"
echo "Password field is truncated for display purposes only."
else #search for similar names.
echo "Here are the closes matches to '$UNIN' "
mysql $mysqlloginstr -e "SELECT $mysqltables FROM accounts WHERE username LIKE '%$UNIN%';"
echo "Password field is truncated for display purposes only."
fi
}
adduser()
{
echo ""
echo "Note you must have sudo privileges to add a user."
# tell sanitize function how sanitize (username, password, etc, UNIN var is globalally available so we won't pass
echo -n "Enter new ftp username (30 char max) [cancel]: "; read UNIN; sanitizeinput "$UNIN" username;
echo "The username that will be used after sanitizing is '$UNIN'. "
echo -n "Enter new ftp user's password (30 char max, only _ - . puncuation allowed): "; echo ""; hideechoing; # use hideechoing function
echo -n "Enter user description (40 char max) [NULL]: "; read DESCIN; sanitizeinput "$DESCIN" description;
if [ "$UNIN" = "" ]; then
echo "OOPS, you didn't enter anything for a username."
else
if [ "$PASSIN" = "" ]; then
echo "OOPS, you didn't enter anything for a password."
else
mysql $mysqlloginstr -e "INSERT INTO accounts (id, username, pass, description) VALUES(DEFAULT, '$UNIN', PASSWORD('$PASSIN'), '$DESCIN');"
PASSIN=""; # clear password because we are done with it.

# create user home directory?
echo ""
echo -n "Do you want to create a ftp home directory for the user? "; read YN;
case "$YN" in
[yY]) sudo mkdir "$chrootftploc/$UNIN";
sudo chown $vsftpsysuser:$vsftpsysuser "$chrootftploc/$UNIN";
sudo chmod 775 "$chrootftploc/$UNIN";;
* ) echo -n "";;
esac
# copy the default file to username so we can configure them independently
echo ""
echo "Copying default user configuration to $vsftpduserconfloc/$UNIN "
sudo cp "$vsftpduserconflocdef" "$vsftpduserconfloc/$UNIN"
echo ""
echo -n "Do you want to edit the $vsftpduserconfloc/$UNIN configuration file? [y/N]: "; read YN;
case "$YN" in
[yY]) sudo vi "$vsftpduserconfloc/$UNIN";;
* ) echo -n "";;
esac
fi
fi
}
updateuser()
{
echo -n "Enter ftp username to update: "; read UNIN; sanitizeinput "$UNIN" username;
if [ "$UNIN" = "" ]; then
echo "OOPS, you didn't enter anything."
else #check if name is in db, if it is continue. If not search for similar names.
UU=`mysql --skip-column-names $mysqlloginstr -e "SELECT username FROM accounts a WHERE username='$UNIN';"`

if [ "$UNIN" = "$UU" ]; then
echo -n "Do you want to update password for '$UNIN'? [y/N]: "; read YN;
case "$YN" in
[yY]) echo -n "Enter new password for '$UNIN' (30 char max, only _ - . puncuation allowed): "; echo ""; hideechoing; # use hideechoing function
mysql $mysqlloginstr -e "UPDATE accounts SET pass=PASSWORD('$PASSIN') WHERE username='$UNIN';"
PASSIN="";; # clear password because we are done with it.
* ) echo -n "";;
esac
echo -n "Do you want to update description for '$UNIN'? [y/N]: "; read YN;
case "$YN" in
[yY]) echo -n "Enter new description for '$UNIN' (40 char max): "; read DESCIN; sanitizeinput "$DESCIN" description;
mysql $mysqlloginstr -e "UPDATE accounts SET description='$DESCIN' WHERE username='$UNIN';" ;;
* ) echo -n "";;
esac
else echo ""
echo "FTP username '$UNIN' not in database."
echo "but here are some names that are close: "
mysql $mysqlloginstr -e "SELECT $mysqltables FROM accounts WHERE username LIKE '%$UNIN%';"
fi
fi
}
deleteuser()
{
echo ""
echo "Note you must have sudo privileges to properly delete a user."
echo -n "Input the username or part of the username to delete [cancel]: "; read UNIN; sanitizeinput "$UNIN" username;
if [ "$UNIN" = "" ]; then
echo "OOPS, you didn't enter anything."
else #check if name is in db, if it is delete. If not, search for similar names.
UU=`mysql --skip-column-names $mysqlloginstr -e "SELECT username FROM accounts WHERE username='$UNIN';"`
if [ "$UNIN" = "$UU" ]; then
echo -n "Are you sure you want to delete user '$UNIN'? [y/N]: "; read YN;
case "$YN" in
[yY]) mysql $mysqlloginstr -e "DELETE FROM accounts WHERE username='$UNIN';"
sudo rm "$vsftpduserconfloc/$UNIN"; # delete user specific config file
if [ -e "$chrootftploc/$UNIN" ]; then # check if user has a ftp root folder created for them
echo -n "Are you sure you want to delete the folder $chrootftploc/$UNIN? [y/N]: "; read YN;
case "$YN" in
[yY]) sudo rm -r "$chrootftploc/$UNIN";; # delete users ftp root folder
* ) echo "";;
esac
else
echo "Username '$UNIN' deleted"
fi
echo "Username '$UNIN' deleted";;
* ) echo ""; echo -n "User '$UNIN' kept safe";;
esac
else echo ""
echo "The FTP username '$UNIN' not in database "
echo "but here are some names that are close: "
mysql $mysqlloginstr -e "SELECT $mysqltables FROM accounts WHERE username LIKE '%$UNIN%';"
fi
fi
}

clear
getmysqllogin;
mainprompt;

while [ "$USERINPUT" != "6" ]
do

case "$USERINPUT" in
"1") printusers;;
"2") adduser;;
"3") updateuser;;
"4") deleteuser;;
"5") getmysqllogin;;
"6") echo "Good Bye";;
* ) clear
echo "Please select a number";;
esac

echo ""
echo ""
mainprompt;

done

donh3
Jan 20th 2009, 02:22 AM
Helps I attach the how-to.

newmember
Feb 19th 2009, 07:40 PM
I updated the code a bit.



#!/bin/bash
# writen for vsftpd 2.06, MySQL 5,
# Version 1.01 2008-10-30 by Don Hess
# This script will provide a menu to manipulate the mysql database
# and manage the files for all type of user administration. It is
# intended that the server only need ftp and ssh open and you can secure
# ssh to lock out IP address apon a certain number of failures. The mysql
# should be setup similar to this how-to http://www.howtoforge.com/vsftpd_mysql_debian_etch
# with the addition of a description field in the db.


chrootftploc=/home/ftp # ftp root directory
vsftpduserconfloc=/etc/vsftpd_user_conf # location of user specific config override files
vsftpduserconflocdef=$vsftpduserconfloc/default # location of default template for above
vsftpsysuser=vsftpd # the system user that vsftpd runs under
myuserlogin="vsftpd" # mysql login this script will use
myuserpass="yourppassword" # "" "" ""
db="vsftpd" # database used to store our information
mysqlhost="localhost" # host computer for mysql server
mysqlloginstr="" # used in sql queries instead of long string, set in getmysqllogin function
mysqltables="username, LEFT(pass, 6) AS pass, description" #used inplace of * for SELECT statements so password display is shorter.
# mysql syntax reference http://dev.mysql.com/doc/refman/5.0/en/sql-syntax-data-manipulation.html


mainprompt()
{
USERINPUT=1
echo "-------------------------------"
echo "vsftp mysql database utility"
echo ""
echo "1 Query user(s)"
echo "2 Add new user"
echo "3 Update user"
echo "4 Delete user"
echo "5 Change this scripts MySQL login"
echo "6 Quit"
echo ""
echo -n "--> "
read USERINPUT
}
hideechoing()
{
local stty_orig=`stty -g` # set the original stty state to variable
stty -echo # turn off terminal echoing so we don't see password
read hidinput; # read user input
sanitizeinput "$hidinput" password; # sanitize input
stty $stty_orig # put terminal back the way it was
}
sanitizeinput()
{
# $1 is input to be sanitized
# $2 tells us which options to use to sanitize
parm1=`echo "$1" | tr -d '[\000-\037][\041-\054]\057[\072-\077]\100[\133-\136]\140[\173-\176]' | tr -d '[:cntrl:]'`;
# remove non-alpha characters using octets, http://en.wikipedia.org/wiki/ASCII
# only non-alpha characters allowed are space, underscore "_", hyphen "-", and period "."
parm2="$2"
case "$parm2" in
username)
UNIN=`echo $parm1 | tr -d '[:space:]' | tr '[:upper:]' '[:lower:]' | sed 's/^\(..............................\).*$/\1/'`;; # use sed to crop to 30 chars
password)
PASSIN=`echo $parm1 | tr -d '[:space:]' | sed 's/^\(..............................\).*$/\1/'`;; # use sed to crop to 30 chars
description)
DESCIN=`echo $parm1 | sed 's/^\(........................................\).*$/\1/'`;; # use sed to crop to 40 chars
* ) echo -n "";;
esac
}
getmysqllogin()
{
echo "You must enter the connection information for MySQL before continuing."
echo -n "Enter MySQL username (case sensitive): "; read aa;
myuserlogin=`echo "$aa"`;
echo -n "Enter MySQL password (case sensitive): "; # this is a one-off hidding of password because we may need special chars
local stty_orig=`stty -g` # set the original stty state to variable
stty -echo # turn off terminal echoing so we don't see password
read bb # read user input
stty $stty_orig # put terminal back the way it was
myuserpass=`echo "$bb"`;
mysqlloginstr="--host=$mysqlhost --user="$myuserlogin" --password="$myuserpass" --database="$db"" #reset the login variable with new data
echo ""; echo "";
echo "Username and password for MySQL set."
echo Using MySQL host "'$mysqlhost'" with database "'$db'".
}
printusers()
{
# --skip-column-names removes coloumn headers
# mysql --host=host_name --user=user_name --password=your_password --database=dbname -e "statement;"
clear
echo -n "Input at least part of the username or press [Enter] key to show all: "; read UNIN; sanitizeinput "$UNIN" username;
if [ "$UNIN" = "" ]; then
echo ""
echo "First 1000 ftp users in database: "
mysql $mysqlloginstr -e "SELECT $mysqltables FROM accounts a LIMIT 0,1000;"
echo "Password field is truncated for display purposes only."
else #search for similar names.
echo "Here are the closes matches to '$UNIN' "
mysql $mysqlloginstr -e "SELECT $mysqltables FROM accounts WHERE username LIKE '%$UNIN%';"
echo "Password field is truncated for display purposes only."
fi
}
adduser()
{
echo ""
echo "Note you must have sudo privileges to add a user."
# tell sanitize function how sanitize (username, password, etc, UNIN var is globalally available so we won't pass
echo -n "Enter new ftp username (30 char max) [cancel]: "; read UNIN; sanitizeinput "$UNIN" username;
echo "The username that will be used after sanitizing is '$UNIN'. "
echo -n "Enter new ftp user's password (30 char max, only _ - . puncuation allowed): "; echo ""; hideechoing; # use hideechoing function
echo -n "Enter user description (40 char max) [NULL]: "; read DESCIN; sanitizeinput "$DESCIN" description;
if [ "$UNIN" = "" ]; then
echo "OOPS, you didn't enter anything for a username."
else
if [ "$PASSIN" = "" ]; then
echo "OOPS, you didn't enter anything for a password."
else
mysql $mysqlloginstr -e "INSERT INTO accounts (id, username, pass, description) VALUES(DEFAULT, '$UNIN', PASSWORD('$PASSIN'), '$DESCIN');"
PASSIN=""; # clear password because we are done with it.

# create user home directory?
echo ""
echo -n "Do you want to create a ftp home directory for the user? "; read YN;
case "$YN" in
[yY]) sudo mkdir "$chrootftploc/$UNIN";
sudo chown $vsftpsysuser:nogroup "$chrootftploc/$UNIN";
sudo chmod 775 "$chrootftploc/$UNIN";;
* ) echo -n "";;
esac
# copy the default file to username so we can configure them independently
echo ""
echo "Copying default user configuration to $vsftpduserconfloc/$UNIN "
sudo cp "$vsftpduserconflocdef" "$vsftpduserconfloc/$UNIN"
echo ""
echo -n "Do you want to edit the $vsftpduserconfloc/$UNIN configuration file? [y/N]: "; read YN;
case "$YN" in
[yY]) sudo vi "$vsftpduserconfloc/$UNIN";;
* ) echo -n "";;
esac
fi
fi
}
updateuser()
{
echo -n "Enter ftp username to update: "; read UNIN; sanitizeinput "$UNIN" username;
if [ "$UNIN" = "" ]; then
echo "OOPS, you didn't enter anything."
else #check if name is in db, if it is continue. If not search for similar names.
UU=`mysql --skip-column-names $mysqlloginstr -e "SELECT username FROM accounts a WHERE username='$UNIN';"`

if [ "$UNIN" = "$UU" ]; then
echo -n "Do you want to update password for '$UNIN'? [y/N]: "; read YN;
case "$YN" in
[yY]) echo -n "Enter new password for '$UNIN' (30 char max, only _ - . puncuation allowed): "; echo ""; hideechoing; # use hideechoing function
mysql $mysqlloginstr -e "UPDATE accounts SET pass=PASSWORD('$PASSIN') WHERE username='$UNIN';"
PASSIN="";; # clear password because we are done with it.
* ) echo -n "";;
esac
echo -n "Do you want to update description for '$UNIN'? [y/N]: "; read YN;
case "$YN" in
[yY]) echo -n "Enter new description for '$UNIN' (40 char max): "; read DESCIN; sanitizeinput "$DESCIN" description;
mysql $mysqlloginstr -e "UPDATE accounts SET description='$DESCIN' WHERE username='$UNIN';" ;;
* ) echo -n "";;
esac
else echo ""
echo "FTP username '$UNIN' not in database."
echo "but here are some names that are close: "
mysql $mysqlloginstr -e "SELECT $mysqltables FROM accounts WHERE username LIKE '%$UNIN%';"
fi
fi
}
deleteuser()
{
echo ""
echo "Note you must have sudo privileges to properly delete a user."
echo -n "Input the username or part of the username to delete [cancel]: "; read UNIN; sanitizeinput "$UNIN" username;
if [ "$UNIN" = "" ]; then
echo "OOPS, you didn't enter anything."
else #check if name is in db, if it is delete. If not, search for similar names.
UU=`mysql --skip-column-names $mysqlloginstr -e "SELECT username FROM accounts WHERE username='$UNIN';"`
if [ "$UNIN" = "$UU" ]; then
echo -n "Are you sure you want to delete user '$UNIN'? [y/N]: "; read YN;
case "$YN" in
[yY]) mysql $mysqlloginstr -e "DELETE FROM accounts WHERE username='$UNIN';"
sudo rm "$vsftpduserconfloc/$UNIN"; # delete user specific config file
if [ -e "$chrootftploc/$UNIN" ]; then # check if user has a ftp root folder created for them
echo -n "Are you sure you want to delete the folder $chrootftploc/$UNIN? [y/N]: "; read YN;
case "$YN" in
[yY]) sudo rm -r "$chrootftploc/$UNIN";; # delete users ftp root folder
* ) echo "";;
esac
else
echo "Username '$UNIN' deleted"
fi
echo "Username '$UNIN' deleted";;
* ) echo ""; echo -n "User '$UNIN' kept safe";;
esac
else echo ""
echo "The FTP username '$UNIN' not in database "
echo "but here are some names that are close: "
mysql $mysqlloginstr -e "SELECT $mysqltables FROM accounts WHERE username LIKE '%$UNIN%';"
fi
fi
}

clear
getmysqllogin;
mainprompt;

while [ "$USERINPUT" != "6" ]
do

case "$USERINPUT" in
"1") printusers;;
"2") adduser;;
"3") updateuser;;
"4") deleteuser;;
"5") getmysqllogin;;
"6") echo "Good Bye";;
* ) clear
echo "Please select a number";;
esac

echo ""
echo ""
mainprompt;

done



Here is my vsftpd.conf



listen=YES

log_ftp_protocol=YES

anonymous_enable=NO
local_enable=YES
write_enable=YES
local_umask=022

pasv_address=EXTERNAL IPADDRESS
dirmessage_enable=YES

vsftpd_log_file=/var/log/vsftpd.log
xferlog_enable=YES

connect_from_port_20=YES
idle_session_timeout=600
data_connection_timeout=120
##
#Welcome Banner
##
ftpd_banner=Welcome to the FTP service.

nopriv_user=vsftpd
chroot_local_user=YES

secure_chroot_dir=/var/run/vsftpd

pam_service_name=vsftpd


rsa_cert_file=/etc/ssl/certs/vsftpd.pem
guest_enable=YES
guest_username=vsftpd
local_root=/home/ftp/$USER
user_sub_token=$USER
virtual_use_local_privs=YES
user_config_dir=/etc/vsftpd_user_conf

#Enable SSL, a very good thing
##
ssl_enable=YES
allow_anon_ssl=NO
##
#Set up for SSL access conections
##
rsa_cert_file=/etc/ssl/certs/ssl-cert-snakeoil.pem
rsa_private_key_file=/etc/ssl/private/ssl-cert-snakeoil.key
##
#Have to leave these as NO so that the web-ftp in ispconfig can access ftp folders
##
force_local_data_ssl=NO
force_local_logins_ssl=NO
ssl_tlsv1=YES
ssl_sslv2=YES
ssl_sslv3=YES
# Filezilla uses port 21 if you don't set any port
# in Servertype "FTPES - FTP over explicit TLS/SSL"
# Port 990 is the default used for FTPS protocol.
# Uncomment it if you want/have to use port 990.
##
#Since I can only use either 21 or 990 I will use port 21.
#I used a port forward on my firewall to match port 990 --> port 21
##
#listen_port=990
##
#I didn't use pasv becuase I only want people to use SFTP or FTPS.
#I only need FTP for the ISPConfig web interface which is on the local server.
#I should set up a rule to only allow local listening for FTP unsecure.
##
pasv_min_port=12000
pasv_max_port=12100