Aug 242011
Configure Secure FTP, With VSFTPD

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

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:


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 item=user sense=deny file=/etc/ftpusers onerr=succeed
# Note: vsftpd handles anonymous logins on its own. Do not enable
# Standard blurb.
# @include common-account
# @include common-session
# @include common-auth
auth required item=user sense=deny file=/etc/ftpusers onerr=succeed
auth required db=/etc/vsftpd/virtual-users
account required 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

Note we’ve commented out the user_sub_token but for ftpuser1 we could use it like this:


Now you can simply cp ftpuser1 ftpuser2 and you have two virtual users who are chrooted to /var/ftp/ftpuser1 directory and /var/ftp/ftpuser2 directory respectively. Meanwhile if you login as admin you’ll be chrooted a level up from there so you will see and have full access to those two folders.
Now how can we restrict what they can or can’t do within their chrooted environment? Well I take a text file of all FTP commands and save it on the system for reference:

# List of FTP commands
# ABOR - Abort an active file transfer.
# ACCT - Account information.
# ADAT - Authentication/Security Data (RFC 2228)
# ALLO - Allocate sufficient disk space to receive a file.
# APPE - Append.
# AUTH - Authentication/Security Mechanism (RFC 2228)
# CCC - Clear Command Channel (RFC 2228)
# CDUP - Change to Parent Directory.
# CONF - Confidentiality Protection Command (RFC 697)
# CWD - Change working directory.
# DELE - Delete file.
# ENC - Privacy Protected Channel (RFC 2228)
# EPRT - Specifies an extended address and port to which the server should connect. (RFC 2428)
# EPSV - Enter extended passive mode. (RFC 2428)
# FEAT - Get the feature list implemented by the server. (RFC 2389)
# HELP - Returns usage documentation on a command if specified, else a general help document is returned.
# LAND - Language Negotiation (RFC 2640)
# LIST - Returns information of a file or directory if specified, else information of the current working directory is returned.
# LPRT - Specifies a long address and port to which the server should connect. (RFC 1639)
# LPSV - Enter long passive mode. (RFC 1639)
# MDTM - Return the last-modified time of a specified file. (RFC 3659)
# MIC - Integrity Protected Command (RFC 2228)
# MKD - Make directory.
# MLST - Lists the contents of a directory if a directory is named. (RFC 3659)
# MODE - Sets the transfer mode (Stream, Block, or Compressed).
# NLST - Returns a list of file names in a specified directory.
# NOOP - No operation (dummy packet; used mostly on keepalives).
# OPTS - Select options for a feature. (RFC 2389)
# PASS - Authentication password.
# PASV - Enter passive mode.
# PBSZ - Protection Buffer Size (RFC 2228)
# PORT - Specifies an address and port to which the server should connect.
# PWD - Print working directory. Returns the current directory of the host.
# QUIT - Disconnect.
# REIN - Re initializes the connection.
# REST - Restart transfer from the specified point.
# RETR - Retrieve (download) a remote file.
# RMD - Remove a directory.
# RNFR - Rename from.
# RNTO - Rename to.
# SITE - Sends site specific commands to remote server.
# SIZE - Return the size of a file. (RFC 3659)
# SMNT - Mount file structure.
# STAT - Returns the current status.
# STOR - Store (upload) a file.
# STOU - Store file uniquely.
# STRU - Set file transfer structure.
# SYST - Return system type.
# TYPE - Sets the transfer mode (ASCII/Binary).
# USER - Authentication username.

Then go back into the user file, for example ftpuser1 and add this:


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.