So you need to setup an FTP server? Are you sure? The reason I ask is that FTP isn’t really a great option in many cases due to its inherent lack of security “consciousness” as I like to call it. This can best be summed up from the File Transfer Protocol’s wikipedia page, excerpt here:

FTP was not designed to be a secure protocol—especially by today’s standards—and has many security weaknesses. In May 1999, the authors of RFC 2577 enumerated the following flaws:
Bounce attacks
Spoof attacks
Brute force attacks
Packet capture (sniffing)
Username protection
Port stealing

FTP was not designed to encrypt its traffic; all transmissions are in clear text, and user names, passwords, commands and data can be easily read by anyone able to perform packet capture (sniffing) on the network. This problem is common to many Internet Protocol specifications (such as SMTP, Telnet, POP and IMAP) designed prior to the creation of encryption mechanisms such as TLS or SSL. A common solution to this problem is use of the “secure”, TLS-protected versions of the insecure protocols (e.g. FTPS for FTP, TelnetS for Telnet, etc.) or selection of a different, more secure protocol that can handle the job, such as the SFTP/SCP tools included with most implementations of the Secure Shell protocol.

With that being said, there are still plenty of valid reasons for wanting/needing an FTP server. I won’t go over an FTPS configuration but you’d literally be an SSL cert and a couple config lines away from it by the end of this post. If you simply need a good way to transfer files to/from your server and don’t need other users involved then that is a clear case for taking a strong look at SFTP/SCP transfers instead. If you are still reading this then you want an FTP server don’t you? Here’s what we are setting up; an FTP server using VSFTPD which is the most secure FTP daemon one could probably use. Tried and tested for years on very large sites, it is rarely, if ever, found to have an exploit of any kind. Added to that, we will be configuring it with virtual users. These are users solely for the FTP server and do NOT have a local account on your server. So you know how FTP sends the user/pass in clear text? Well no one will be able to ssh into your server because of that. Worst case, they will break into a chrooted environment where they will only have access to the FTP files you have given to that user anyway. If you’ve kept in mind FTP is clear text transfers then you don’t have confidential files there to be compromised any further. Regardless, even that has never happened to me and I’ve been running VSFTPD for the past 10 years. If you supplement it with a nice fail2ban configuration and further secure it with apparmor you’ll be in incredible shape.

Lets start building this great configuration. First aptitude install vsftpd. This installs the core of vsftpd. Next we’ll want to aptitude install db4.6-util. This installs the utility we will use to store our virtual users’ user-names and passwords. Now find vsftpd.conf file, there is a good chance its right at /etc/vsftpd.conf. Depending on the distro and exact version you are using some of these paths might be off for you a tad but you should be able to figure that part out, just follow the framework here. Go ahead and backup the original vsftpd.conf file and then edit to have just the following:
# vsftpd.conf
listen=YES
anonymous_enable=NO
local_enable=YES
write_enable=YES
local_umask=022
dirmessage_enable=YES
xferlog_enable=YES
connect_from_port_20=YES
chroot_local_user=YES
secure_chroot_dir=/var/run/vsftpd
pam_service_name=vsftpd-virtual
rsa_cert_file=/etc/ssl/certs/ssl-cert-snakeoil.pem
rsa_private_key_file=/etc/ssl/private/ssl-cert-snakeoil.key
hide_ids=YES
user_config_dir=/etc/vsftpd/vsftpd_user_conf
dual_log_enable=YES
guest_enable=YES
guest_username=ftp
virtual_use_local_privs=YES
local_root=/var/vsftp

A few things to point out in that configuration so you know what’s going on. We disable anon logins, that’s important. Take note of the pam_service_name of “vsftpd-virtual” we’ll be using that in a second to setup our authentication. Also look at the user_config_dir, that will give us a lot of the options people look for and don’t know how to configure. And of course the chroot_local_user is YES.
Now, mkdir /etc/vsftp and mkdir /etc/vsftpd/vsftpd_user_conf.
change directory to /etc/vsftp and vi virtual-users.txt (or whatever editor you use, I’ll recommend vi). This file lists our virtual users with username, password each on their own new line. So if you wanted two virtual-users and maybe one “admin” account for you, you could have the file be:
admin
adminpass1sh3r3
ftpuser1
us3r1p@ss
ftpuser2
correcthorsebatterystaple

Each username is followed by that user’s password on the next line. Since this is a very sensitive file, as root you should chmod 0600 virtual-users.txt. Now lets create the encrypted db that vsftpd will use to authenticate these users with: db4.6_load -T -t hash -f virtual-users.txt virtual-users.db. This converts that .txt file into a Berkeley v4.6 database format. The benefit of keeping the original .txt file around is that you can easily add additional users or tell them their password when they inevitably forget it.
Now lets finish up the authentication with vi /etc/pam.d/vsftpd-virtual. This is the pam service name in our vsftpd.conf file. Make this file look like:
# Standard behaviour for ftpd(8).
auth required pam_listfile.so item=user sense=deny file=/etc/ftpusers onerr=succeed
# Note: vsftpd handles anonymous logins on its own. Do not enable
# pam_ftp.so.
# Standard blurb.
# @include common-account
# @include common-session
# @include common-auth
auth required pam_listfile.so item=user sense=deny file=/etc/ftpusers onerr=succeed
auth required pam_userdb.so db=/etc/vsftpd/virtual-users
account required pam_userdb.so db=/etc/vsftpd/virtual-users

That’s it for the authentication. Now let’s lock down our users’ chroot location and give them access only to certain commands. Go to your /etc/vsftp/vsftpd_user_conf directory or whatever you’ve made it. In there create a file with each of the user’s user-names. So using the example virtual-users.txt file I’d create an admin file, a ftpuser1 and ftpuser2 file. Simply vi admin
and in there put:
# user_sub_token=$USER
local_root=/var/ftp

Note we’ve commented out the user_sub_token but for ftpuser1 we could use it like this:
user_sub_token=$USER
local_root=/var/ftp/$USER

Then go back into the user file, for example ftpuser1 and add this:
cmds_allowed=ABOR,ACCT,ALLO,APPE,CCC,CDUP,CWD,EPSV,LIST,MDTM,MLST,MODE,NLST,NOOP,OPTS,PASS,PASV,PBSZ,PORT,PWD,QUIT,REIN,REST,RETR,RNFR,RNTO,MKD,SITE,SIZE,STAT,STOR,STRU,SYST,TYPE,USER

This allows full download and upload of files, leaving off some potentially dangerous commands. Also this user can NOT delete or create directories. And if you want the user to only be able to download or upload then leave off STOR or RETR respectively.

This layout is great because if your users need files you don’t need to login as them to put them there. You can go in with your “admin” account, which still has no real access to your actual server filesystem, and put things into your user’s chrooted directories. You can lock down your users not only to certain directories but build a hierarchy such that some users have a chrooted environment that is contained by another user’s. Finally, you can dictate which commands each user is allowed to run giving you beautiful granular management of your entire FTP server. For the past decade this configuration has yet to fail me on its adaptability and security with FTP server’s having a multitude of specific uses.