Lock IT Down: Set up a secure FTP server with ProFTPD

See how to replace the popular wu-ftpd service with ProFTPD for more secure FTP services.

One of the most important services available on the Internet today—with the exception of Web serving—is the ability to exchange files via File Transfer Protocol (FTP). FTP sites are a cornerstone of the Internet, and no one can dispute this. Because of the popularity of FTP, a number of FTP server packages have been released for a variety of platforms. In the Linux world, probably the most popular of these FTP servers is the wu-ftpd server.

While wu-ftpd is a good product and provides excellent performance, it lacks a number of features found in newer products. It also has a poor security history. A number of people felt that the only way to get wu-ftpd up to spec was to completely redesign it from the ground up. It was at this time that a new FTP server, called ProFTPD, was born. In this Daily Drill Down, I’ll introduce you to ProFTPD.

Unlike other FTP servers, ProFTPD is designed to be lightweight, secure, and configurable. (If you’re familiar with Apache, you should have no trouble adapting because the configuration for ProFTPD is based on a similar syntax.) Other FTP servers may provide an extremely secure setting at the expense of configurability. ProFTPD doesn’t do this. It provides an extremely capable FTP service, with a high level of configurability and security. Currently, ProFTPD offers some attractive features that I’ll quickly outline for you.

ProFTPD features per-directory .ftpaccess configuration files similar to Apache's .htaccess files. ProFTPD makes it simple to configure multiple virtual FTP servers and anonymous FTP services. It can be run either as a standalone server or from inetd, depending on your needs. The anonymous FTP service it provides doesn’t need a specific directory structure or other system files like the anonftp package for wu-ftpd (no more /home/ftp if you don't want it!). ProFTPD allows for hidden directories and files based on user/group ownership and UNIX-style permissions. It can run as a configurable, non-privileged user in standalone mode in order to decrease the chance of attacks that might exploit its root capabilities (like Apache running as user nobody). It provides multiple forms of logging compatible with the wu-ftpd logging standard, and it provides extended paranoid logging. It also supports full shadow passwording, including support for expired accounts.

Installing ProFTPD
The first thing you must do is install ProFTPD. Because ProFTPD is a full FTP server, it conflicts with wu-ftpd and you must first remove wu-ftpd from your system. To do this on a Linux Mandrake or any other RPM-based system, use the following:
rpm -e anonftp
rpm -e wu-ftpd

The anonftp package provides anonymous FTP services for wu-ftpd and must be removed first because it depends on the wu-ftpd package. Finally, you have to remove wu-ftpd itself.

At this point, you can install ProFTPD. Some distributions provide a packaged version of ProFTPD, usually via user contributions. If your distribution does provide this, it would be easiest to install the ProFTPD RPM. If that isn't possible, you'll have to download the source code from the ProFTPD site and compile it yourself.

To download and compile ProFTPD, grab the latest tarball from the downloads page of the Web site ( Currently, the latest version is 1.2.0pre10. Move the downloaded file to the /usr/src directory and issue
tar xvzf proftpd-1.2.0pre10.tar.gz
cd proftpd-1.2.0pre10

This will place you in the top-level directory of the source distribution. To compile and install the package, issue the following:
./configure —prefix=/usr —sysconfdir=/etc —localstatedir=/var/run \
make install

The first command will set up the make environment. Here you tell it to use the /usr directory as the top-level directory for the installation, and it will place binaries into /usr/bin, manpages into /usr/man, and so forth. You also tell it to look for the proftpd.conf file in the /etc directory and to provide localstate files (PID, or Process Identification files) in the /var/run/proftpd directory. Finally, you tell it to include three modules:
  • mod_linuxprivs—Provides ProFTPD support to use Linux privileges.
  • mod_ratio—Provides user upload/download ratio support.
  • mod_readme—Provides support for displaying readme status information.

Next you make the binaries, and then you use make to install everything to its proper location.

A walk-through of the /etc/proftpd.conf configuration file
The main configuration of ProFTPD is done in the /etc/proftd.conf file. Open the proftpd.conf file in your favorite text editor, and let’s look at a few directives that will create an effective FTP server with several good security options enabled.

The first thing you want to do is define defaults for the server. The following will define some of the server information ProFTPD needs. These should be the first entries in your proftpd.conf file. Like with most configuration files, you place the hash symbol (#) at the beginning of a line to place comments in the file.
# Server info
ServerName      ?My Server?
ServerType      standalone
DefaultServer   on

The ServerNamekeyword defines the default name of the server. The ServerType keyword specifies whether ProFTPD is to be run as a standalone or inetd service. In our example, ProFTPD is running as a standalone server. If you want to run ProFTPD from inetd, change standalone to inetd and place the following in your /etc/inetd.conf file:
ftp   stream   tcp   nowait   root   /usr/sbin/tcpd   in.proftpd

The ServerAdmin keyword defines the e-mail address to the administrator of this FTP server, and the DefaultServer keyword specifies whether this is the default server in the configuration. Since ProFTPD also supports Virtual FTP servers, you should define one server as the default server. Otherwise, any connections to an IP address not defined will receive a no server available to service your request error message. For example, if you do a ftp localhost command on the system ProFTPD is running on and don’t explicitly define a server for the IP address, that error message will be returned if DefaultServer is off. With DefaultServer on, any connections to unknown IP addresses will be forwarded to the defined default server.

Next you want to define some server defaults. These values must be defined outside any <VirtualHost> or <Global> directives, so we'll put them right after the server information:
# Some basic defaults
Port                     21
TimeoutLogin            120
TimeoutIdle             600
TimeoutNoTransfer       900
TimeoutStalled         3600
MaxInstances             30

The Port keyword defines the default port ProFTPD should listen to (this has no bearing on inetd-started servers). The default FTP port is 21, so set it to that value.

The TimeoutLogin keyword defines how long the server will wait for a user to log in before timing out when a connection has been initiated. Setting the value to 120 means you give users two minutes to fully log in before disconnecting them.

The TimeoutIdle keyword defines how many seconds ProFTPD will wait to disconnect the client if no data is received in either control or data mode. A value of 0 will disable this, but it's generally a bad idea because if a TCP connection dies, the server will never terminate the disconnected session.

The TimeoutNoTransfer keyword is used to define the maximum number of seconds a client can sit idle after logging into the FTP server without initiating a file upload or download, or without retrieving a directory listing.

The TimeoutStalled keyword defines the maximum number of seconds to wait during an active data connection between the server and the client in which no data is sent (in other words, the connection has stalled). The default is 0 (indefinite).

Finally, the MaxInstances keyword defines the maximum number of child processes the standalone server can spawn at any given time (this has no bearing on inetd-started servers). Because each child process is an active connection, this also controls how many users can be logged in simultaneously. Any connections beyond the configured maximum are written to the syslog and silently refused. The default is 0 (unlimited), but this generally isn't a good idea. Reducing the number to a reasonable amount (depending on your system, you may want to configure anywhere from 10 to 100) will prevent Denial-of-Service attacks that may attempt to fork-bomb your server. This is typically accomplished by repeated connections to the FTP port, which cause ProFTPD to spawn a new child process until it’s unable to spawn more and simply gives up.

The next thing you want to define is what user the FTP server will normally run as in standalone mode. When ProFTPD starts, it initially starts as root so it can grab control of the standard FTP port (only root has the power to bind to TCP ports below port 1024). However, it will switch to a defined user as quickly as possible. To accomplish this, add the following to your proftpd.conf file:
User    ftp
Group   ftp

The User and Groupkeywords tell ProFTPD what UID/GID to switch to. These keywords can also be used within <VirtualHost> and <Anonymous> directives, allowing ProFTPD to switch to different UID/GID settings if you require them. In the above example, I set both the UID and GID to the user ftp, which is a non-privileged user. You could also use the user nobody (Apache uses it by default).

Defining permissions and logging options
Now you can start defining some permissions to the server. First, you want to allow all users to log into the server, so we have to use a <Limit> directive. Because this is outside a <Global> or <VirtualHost> directive, this setting applies to all defined servers:
<Limit LOGIN>

The <Limit> directive takes a number of arguments. Some of these arguments are:
  • LOGIN—Allow or Deny users from logging into the server
  • READ—All commands dealing with file reading (RETR, STAT, etc.)
  • WRITE—All commands dealing with file or directory writing/creating/deletion
  •  DIRS—All commands dealing with directory listings (NLST, LIST, etc.)
  • ALL—All FTP commands (identical to READ WRITE DIRS)

<Limit> will also take, as an argument, any valid FTP command, like STOR, RETR, DELE, CWD, and so forth. This way, you can either use a command group to limit (like WRITE) or limit a specific command itself.

The precedence of <Limit> directives is defined by which one comes first. The configuration file is basically read from top to bottom, so the first <Limit> directive will control all the servers unless other <Limit> directives are used within <Directory> or <Anonymous> directives that change the behavior. For example, this is the first <Limit> directive you use, so by default all users may log into the FTP server. You could place, further down in the configuration file, another <Limit LOGIN> to deny everyone except a handful of specific users for a defined <VirtualHost>.

Next, you want to place a global <Directory> directive that allows all files to be overwritten. If you want to upload a new file with the same name as an existing file, you’ll have to delete the old file first if you fail to include this particular directive. So this directive saves us a step:
<Directory /*>
 AllowOverwrite      on

Next, we want to deny writing to the base server. This will prevent users from making their own directories, uploading files, and so forth:
<Limit WRITE>

Finally, we want to define some logging options. The TransferLog keyword defines the log file to write transfers to. You can use multiple LogFormat keywords to define different logging styles for different things. The general syntax for LogFormat is nickname string. The nickname can be referenced via the TransferLog and ExtendedLog keywords. Here I’ve defined the default log format, as well as two nicknames, auth and write:
TransferLog /var/log/proftpd/xferlog.default
LogFormat default "%h %l %u %t \"%r\" %s %b"
LogFormat auth "%v [%P] %h %t \"%r\" %s"
LogFormat write "%h %l %u %t \"%r\" %s %b"
UseReverseDNS off
ScoreboardPath /var/run/proftpd

You also need to specify whether to use reverse DNS lookups on data connections. Since this can be time consuming, set UseReverseDNS to off. You must also define ScoreboardPath, which is where ProFTPD will place its PID file and scoreboard files (these files are necessary for the MaxClients keyword to work properly). In addition, this allows us to use some utility clients like ftpwho and ftpcount that come with ProFTPD.

Next, you need to define some global settings that are shared by the main server and all defined virtual hosts. These settings must be enclosed in the <Global> and </Global> tags as shown:
 Umask 022
 DisplayLogin welcome.msg
 DisplayFirstChdir readme
 DisplayReadme README
 DeferWelcome on
 IdentLookups on
 ExtendedLog /var/log/proftpd/access.log WRITE,READ write
 ExtendedLog /var/log/proftpd/auth.log AUTH auth
 ExtendedLog /var/log/proftpd/paranoid.log ALL default

The settings I’ve defined here are quite simple. The Umaskkeyword defines the permissions new files and directories will have, in octal format. The DisplayLogin keyword tells ProFTPD what file to display when users first connect to the FTP site. This file must exist in the defined root directory for the FTP site to be displayed. The DisplayFirstChdir keyword will display the contents of the defined file if it exists in the new directory a user changes to. And the DisplayReadme keyword will display a message that asks users to read the defined file (in this case README). This keyword will also display how long ago the file was modified.

The DeferWelcome keyword specifies whether the server will delay transmitting the server name and address to new connections until after the client has logged in. For the security conscious, this is a good option to enable. This way, no extra information is given out on the server for simple probes, only for valid logins.

The IdentLookups keyword specifies whether ident will be used to identify the remote host name. Because this can be time consuming, some people prefer to turn it off, but again, for the security conscious, this will provide you with more information on clients connecting to your FTP site.

Finally, the ExtendedLog keywords define what you’re logging. The first instance of the keyword tells ProFTPD to log all WRITE and READ command groups to the file /var/log/proftpd/access.logusing the write format (remember the LogFormat keywords you defined previously?). The second instance tells ProFTPD to log all AUTH commands to the file /var/log/proftpd/auth.log using the auth nickname format. Finally, the last instance is a catch-all paranoid logging, which will log everything using the default format to the file /var/log/proftpd/paranoid.log.

The next thing you’ll define is the anonymous portion of your FTP site. All valid users will be able to log into their home directories using their regular username/password, but in order to allow anonymous FTP clients, you’ll need to define a section enclosed in <Anonymous> and </Anonymous> tags, like this:
<Anonymous ~ftp>
 User ftp
 Group ftp
 UserAlias anonymous ftp
 RequireValidShell off
 AnonRequirePassword off
 MaxClients 10

This section tells ProFTPD to place all anonymous connections into the /home/ftp directory. (You can use any directory you like; you don’t need to use /home/ftp at all, unlike wu-ftpd and the anonftp package.) Here again you define the Userand Groupkeywords to tell ProFTPD to run all anonymous FTP connections with the UID/GID of the FTP user. The UserAlias keyword tells ProFTPD to map the anonymous user to the FTP user. Since you don't have an actual Linux user by the name of anonymous, in order to allow that as a login name, you need to map it to an existing login-user, such as ftp or nobody. The format of UserAlias is
<login-user> <userid>

where <login-user> is the name we want to allow (in this case, anonymous) and <userid> is the name of the real user to use for the login (in this case, ftp).

The RequireValidShell keyword specifies whether the user needs a real shell (like /bin/sh or /bin/csh) to log in. Since the user ftp should have a shell of /bin/false or /dev/null on security-conscious systems, you should turn this off; otherwise, anonymous users will never be able to log in.

The AnonRequirePassword, if set to on, will force the client to enter a valid password. In an anonymous environment, clients are expected to enter their e-mail address as the password, so you turn AnonRequirePassword off. If you were defining a guest account that you wanted to force using a valid password, you’d turn AnonRequirePassword on.

Finally, let’s add a little extra security for existing real users. Since, by default, a real user will connect to the FTP site and be placed in the /home/username directory, tell ProFTPD to chroot that home directory. This prevents users from moving outside their home directory tree. In any other FTP environment that doesn't chroot the home directory upon login, users can change to directories like /etc or /usr/bin. When you tell ProFTPD to chroot the home directory, the user will be placed in / instead of /home/username when they connect, and /home/username will be treated as an absolute root directory. If the user then tries to change to /etc, ProFTPD will attempt to change to /home/username/etc. Although this should not be the complete basis for FTP security (it can be circumvented), it is usually enough for the average FTP site. Trying to break out of a chroot environment via FTP is time consuming and difficult. To enable this feature, use:
DefaultRoot /home/bob bob
DefaultRoot /home/jim jim

This means that when user bob logs in, his root directory is /home/bob, and the same for user jim.

While securing an FTP site is not the simplest thing, ProFTPD has made it much easier and more intuitive by providing configuration directives that allow you to tighten the hatches on potential exploits of your system. Beyond the security itself, ProFTPD is an excellent replacement for the traditional wu-ftpd that ships by default with most Linux distributions. Not only is it far more secure, it’s also much more versatile and configurable. The keyword directives, if used together, will provide a fully functional FTP server with anonymous login capabilities and a fairly high level of security. To provide even more security, you can use the <Limit> directives liberally to secure various areas of your site.

For more information on the many configuration keywords and directives, I suggest visiting the ProFTPD Web site and reading the manpages that come with ProFTPD.

Vincent Danen, a native Canadian in Edmonton, Alberta, has been computing since the age of 10, and he’s been using Linux for nearly two years. Prior to that, he used OS/2 exclusively for approximately four years. Vincent is a firm believer in the philosophy behind the Linux "revolution,” and he attempts to contribute to the Linux cause in as many ways as possible—from his FreezerBurn Web site to building and submitting custom RPMs for the Linux Mandrake project. Vincent also has obtained his Linux Administrator certification from Brainbench . He hopes to tackle the RHCE once it can be taken in Canada.

The authors and editors have taken care in preparation of the content contained herein, but make no expressed or implied warranty of any kind and assume no responsibility for errors or omissions. No liability is assumed for any damages. Always have a verified backup before making any changes.


Vincent Danen works on the Red Hat Security Response Team and lives in Canada. He has been writing about and developing on Linux for over 10 years and is a veteran Mac user.

Editor's Picks