Team LiB   Previous Section   Next Section

9.1 FTP Security

What would we do without FTP? You can use FTP to install Linux, download software from public archives, and share files with friends and colleagues. It's both venerable and ubiquitous: nearly every major site on the Internet offers some level of public FTP access.

But like many other Internet applications, FTP is showing its age. Designed for a simpler era, FTP is gradually going the way of Telnet: it's still useful for "anonymous" (public) access, but its clear-text login makes it too dangerous for use with important user accounts.

A Brief Word About FTP Server Packages

WU-FTPD is currently the most popular FTP server for Unix and Unix-like platforms. This is probably because, compared to the traditional BSD ftpd from which it evolved, WU-FTPD is very rich in features, very stable, and, theoretically, more securable. I say "theoretically" with a bit of irony because in recent years, WU-FTPD itself has been vulnerable to a series of buffer overflows that, since WU-FTPD runs as root, have led to many servers being compromised. While its developers have been quick to provide patches, I personally avoid WU-FTPD since these bugs crop up with more regularity than I'm comfortable with.

ProFTPD, a "written-from-scratch" package with Apache-like configuration syntax and modularity, claims security as one of its fundamental design goals. Although it too has had some serious vulnerabilities, I use it for most of my own FTP server needs (albeit behind a proxy). This is out of a perhaps naïve belief that these vulnerabilities are mainly due to growing pains. My willingness to take this chance is partly due to ProFTPD's features (e.g., its support for "virtual servers"), in which multiple FTP sites hosted on the same system appear to be on separate systems.

D. J. Bernstein's package publicfile is designed to be a bare-bones, ultra-secure daemon for serving up public datafiles and simple web pages to anonymous users. (By not even supporting logins to local user accounts, says Bernstein, it's easier to prevent those accounts from being compromised). It's undoubtedly more secure than WU-FTPD or ProFTPD, but also has far fewer features. Also, publicfile requires you to install and run Bernstein's daemon tools and ucspi-tcp packages, which can take some getting used to (though to me, this is merely an annoyance and not a reason not to run publicfile — see Section 6.5.4).

I'm covering ProFTPD in this chapter because that's what I'm most familiar with and because I like some of its features (especially its security features). But if your FTP-server needs (or, for that matter, web-server needs) are very basic and limited to anonymous access, you really should consider publicfile. D. J. Bernstein's publicfile web site is http://cr.yp.to/publicfile.html.

Anonymous FTP, though, will probably remain with us for some time, so let's discuss FTP security, both in general and with specific regard to my preferred FTP server, ProFTPD.

9.1.1 Principles of FTP Security

With FTP, we have several major threat models. The first concerns anonymous access: anonymous users shouldn't be able to do anything but list and download public files and maybe upload files to a single "incoming" directory. Needless to say, we don't want them to "escalate" their privileges to those of a more trusted user.

Another important FTP threat model involves local user accounts. If a local user logs in via FTP to upload or download something to or from his home directory, we don't want that session hijacked or eavesdropped on by anybody else, or the user's credentials may be stolen and used with other services such as telnet, SSH, etc.

The third threat model worth considering involves confidentiality. At the very least, login credentials must be protected from disclosure, as should any other sensitive data that is transmitted.

Unfortunately, by its very design FTP fails miserably in addressing any but the first of these threat models: a good FTP server package that is carefully configured can protect against privilege escalation, but like telnet, the FTP protocol as described in RFC 959 (ftp://ftp.isi.edu/in-notes/rfc959.txt) is designed to transmit both authentication credentials and session data in clear text.

Accordingly, FTP is the wrong tool for almost anything but the anonymous exchange of public files. Using real user accounts for FTP exposes those users' credentials to eavesdropping attacks; all subsequent session data is similarly exposed. For this reason most people's FTP security efforts tend to focus on properly configuring anonymous FTP services and on keeping their FTP server software up to date. Protecting FTP transactions themselves is all but futile.

If your users need to move data onto or off of the system, require them to use scp, sftp, or rsync in combination with stunnel. I describe all of these later in the chapter.

9.1.1.1 Active mode versus passive mode FTP

To make matters worse, FTP's use of TCP ports is, to put it charitably, inopportune. You may have already learned that FTP servers listen on TCP port 21. However, when an FTP client connects to an FTP server on TCP port 21, only part of the transaction uses this initial "control" connection.

By default, whenever an FTP client wishes to download a file or directory listing, the FTP server initiates a new connection back to the client using an arbitrary high TCP port. This new connection is used for transmitting data, as opposed to the FTP commands and messages carried over the control connection. FTP with server-initiated data channels is called "active mode" FTP.

If you think allowing externally initiated (i.e., inbound) data connections in through your firewall is a really bad idea, you're right. Networks protected by simple packet filters (such as router ACLs) are often vulnerable to PORT theft attacks. Herein an attacker opens a data channel (requested by a legitimate user's PORT command) to the user's system before the intended server responds.

PORT commands can also be used in FTP Bounce attacks, in which an attacking FTP client sends a PORT command requesting that the server open a data port to a different host than that from which the command originated. FTP Bounce attacks are used to scan networks for active hosts, to subvert firewalls, and to mask the true origin of FTP client requests (e.g., to skirt export restrictions).

The only widely supported (RFC-compliant) alternative to active mode FTP is passive mode FTP, in which the client rather than the server opens data connections. That mitigates the "new inbound connection" problem, but passive FTP still uses a separate connection to a random high port, making passive FTP only slightly easier to deal with from a firewall engineering perspective. (Many firewalls, including Linux iptables, now support FTP connection tracking of passive mode FTP; a few can track active mode as well.)

There are two main lessons to take from this discussion of active versus passive FTP. First, of the two, passive is preferable since all connections are initiated by the client, making it somewhat easier to regulate and harder to subvert than active mode FTP. Second, FTP is an excellent candidate for proxying at the firewall, even if your firewall is otherwise set up as a packet filter.

SuSE's Proxy Suite, which can be run on any Linux distribution (not just SuSE), contains an FTP proxy that interoperates well with iptables and ipchains. This proxy, ftp-proxy, can broker all FTP transactions passing through your firewall in either direction (in or out). In this way, you can control at the firewall which commands may be used in FTP sessions. You can also prevent buffer overrun attempts and other anomalies from reaching either your FTP servers or clients.[1]

[1] The HTTP proxy, Squid, can also proxy FTP connections but is a general purpose caching proxy, whereas ftp-proxy is specifically designed as a security proxy.

Using an FTP proxy will require your users to configure their FTP software accordingly, unless you've configured your firewall to act as a transparent proxy — i.e., to redirect automatically all outbound and/or inbound FTP connections to its local proxy. (To use a Linux 2.4 iptables firewall for transparent proxying, you'll first need to load the module ipt_REDIRECT.) See Chapter 2 for a detailed explanation of proxies and application gateways and what they do.

Additionally, iptables includes the kernel module ip_conntrack_ftp for tracking FTP connections. While this module doesn't give as granular control as ftp-proxy, it effectively tracks PORT requests (active FTP transactions), passive FTP data requests, and their respective new data channels, and it is intelligent enough to deny spoofed data connections. ip_conntrack_ftp can be used with or without an FTP proxy such as ftp-proxy.

9.1.1.2 The case against nonanonymous FTP

As I mentioned earlier, the FTP protocol transmits logon credentials in clear text over the network, making it unsuitable for Internet use by accounts whose integrity you wish to protect. Why, you may wonder, is that so?

Admittedly, it's unlikely that a given Internet FTP session will be eavesdropped by, say, an evil system administrator at an ISP somewhere on that data's path. The problem is that it's trivially easy for such a person to eavesdrop if she's so inclined. It's equally unlikely that a burglar will rattle the doorknob on your front door at any given moment, but equally easy for one to try. This is reason enough to keep your door locked, and the simplicity of eavesdropping attacks is reason enough to protect one's logon credentials from them.

Furthermore, you may trust your own ISP, but what about the various other unknown networks between you and the other hosts with which you interact across the Internet? What if, at some point, the data passes over a shared medium such as a cable-modem network? Remember, it's very difficult to predict (let alone control) which parts of the Internet your packets will traverse once you send them off. So again, since it's possible that your packets will encounter eavesdroppers, it must be planned against.

For the most part, this means that FTP constitutes an unacceptable risk except when you don't care whether the logon session is eavesdropped (as in anonymous FTP) and whether the subsequent data transfers are eavesdropped.

This doesn't quite make FTP obsolete: as anyone who's ever installed Linux over FTP can attest, there's plenty of value in anonymous FTP. A great deal of the data shared over the Internet is public data.

I'm not going to elaborate here on how to tighten nonanonymous FTP security: I feel strongly that this is a losing proposition and that the only good FTP is anonymous FTP. If remote users need to read or write data to nonpublic areas, use one of the tools described later in this chapter (i.e., rsync, scp, and sftp).

9.1.1.3 Tips for securing anonymous FTP

I do have some guidelines to offer on securing anonymous FTP. They can be summarized as follows:

  • Run your FTP daemon as an unprivileged user/group if possible

  • Make sure your anonymous FTP account uses a bogus shell

  • Create a restricted chroot jail, owned by root, in which anonymous users may operate

  • Don't allow anonymous users to upload files unless you have very good reasons, plus the time and motivation to watch publicly writable directories very closely

Let's examine these tips in depth and then look at how to implement them using my FTP server of choice, ProFTPD.

First, run the FTP daemon as an unprivileged user and group: this sounds like and is common sense, but it may or may not be possible with your chosen FTP server package. The problem is that FTP servers are expected to listen for incoming connections on TCP port 21 and, in some circumstances, to send data from TCP port 20. These are both privileged ports, and any process that needs to bind to them must run as root (at least initially).

ProFTPD by default starts as root, binds to TCP 21, and promptly demotes itself to the user "nobody" and the group "nogroup." (This behavior is customizable if you have a different user or group you'd like ProFTPD to run as.) D. J. Bernstein's minimalist FTP/www server, publicfile, also starts as root and immediately demotes itself. WU-FTPD, however, does not appear to support this feature; as best as I can determine, it runs as root at all times.

My second tip, to make sure that your anonymous FTP account (usually "ftp") specifies a bogus shell, should also be obvious, but is extremely important. /bin/false and /bin/true are both popular choices for this purpose. You don't want an anonymous FTP user to somehow execute and use a normal shell such as /bin/sh, nor do you want anyone to trick some other process into letting them run a shell as the user "ftp." Note that by "bogus," I do not mean "invalid": any shell specified in any line of /etc/passwd should be listed in /etc/shells, regardless of whether it's a real shell, though some FTP server applications are more forgiving of this than others.

A related tip is to make sure in both /etc/passwd and /etc/shadow (if your system uses shadowed passwords) that the password-hash for your anonymous user account is set to *. This prevents the account from being usable for login via any service other than FTP.

Next, build an appropriate chroot jail for anonymous FTP users. Obviously, this directory hierarchy must contain all the things you want those users to be able to download. Be careful not to create any links from within the jail to files outside of it: symbolic links that point outside of the jail will simply not work, but hard links will, and thus they will present attackers with a way out of the chroot jail.

Historically, this chroot jail has needed to contain not only the actual download directory, pub/, but also a bin/ directory with its own copy of ls, an etc/ directory containing passwd, group, and localtime, and sometimes copies of other system directories and files. WU-FTPD requires some of these, but ProFTPD and publicfile do not: the latter two use their own internal versions of ls rather than the system's and function without their own versions of /etc/passwd, etc.

The chroot directory itself and every directory within it should be owned by root, not by your anonymous FTP account (e.g., ftp) or the daemon's "run-as" account (e.g., nobody). A common configuration error on anonymous-FTP servers is for the FTP root to be owned by the FTP account, which constitutes a major exposure, since an anonymous FTP user could write a .rhosts or .forward file to it that extends the user's access to the system.

Proper FTP root (chroot jail) ownerships and permissions are illustrated in Example 9-1, which shows a recursive listing of a sample FTP chroot jail in /var/ftp/.

Example 9-1. ls -lR of an FTP chroot jail
/var/ftp:
total 12
d--x--x--x    2 root     root         4096 Apr 16 00:19 bin
dr--r--r--    2 root     root         4096 Apr 16 00:27 etc
drwxr-xr-x    2 root     wheel        4096 Apr 16 06:56 pub

/var/ftp/bin:
total 44
---x--x--x    1 root     root        43740 Apr 16 00:19 ls

/var/ftp/etc:
total 12
-r--r--r--    1 root     root           63 Apr 16 00:26 group
-r--r--r--    1 root     root         1262 Apr 16 00:19 localtime
-r--r--r--    1 root     root          106 Apr 16 00:27 passwd

/var/ftp/pub:
total 1216
-rw-r--r--    1 root     root       713756 Apr 16 06:56 hijinks.tar.gz
-rw-r--r--    1 root     root       512540 Apr 16 06:56 hoohaw.tar.gz
-rw-r--r--    1 root     root          568 Apr 16 06:43 welcome.msg

The directory /var/ftp itself is set up like this:

drwxr-xr-x    2 root     root        4096 Apr 16 00:06 ftp

If your FTP server is to be maintained by a nonroot user, or if you wish to add files to the pub/ directory without being root, it's okay to make pub/ group writable and owned by a group to which your nonroot account belongs. Since the group wheel is used on many systems to define which user accounts may perform su root, and it's a group that to which you or your subadministrators probably already belong, it's a logical choice for this purpose.

If you make pub/ or any of its subdirectories group writable, however, in no circumstances should their group ID be equal to that of the anonymous user account!

My final general guideline for anonymous FTP is not to allow anonymous uploads unless you know exactly what you're doing, and if you do, to configure and monitor such directories very carefully. According to CERT, publicly writable FTP directories are a common avenue of abuse (e.g., for sharing pornography and pirated software) and even for Denial of Service attacks (e.g., by filling up disk volumes).

If you decide to create such an FTP drop-off directory (conventionally named incoming/), there are a number of things you can do to make it harder to abuse:

  • As with the FTP chroot jail itself, make sure the writable directory isn't owned by the anonymous user account.

  • Enable public write access (i.e., the FTP command STOR), but disable public read access (i.e., the FTP command RETR) to the writable directory. This prevents uploaded files from being downloaded by other anonymous users. Public execute access, which allows users to change their working directory to imcoming/, is okay.

  • To prevent Denial of Service attacks that attempt to stop the FTP server by filling its filesystems, consider limiting the maximum uploadable file size, setting the anonymous FTP user account's disk quota, or mounting the writable directory to its own disk volume.

  • Don't allow uploaded files to remain in the writable directory indefinitely: write a script to run as a cron job that emails you when files have been uploaded or automatically moves uploaded files to a nonpublic part of the filesystem.

  • In general, monitor this directory carefully. If your FTP server can be configured to log all file uploads, do so, and keep an eye on these log entries (Swatch, covered in Chapter 10, is useful for this).

9.1.2 Using ProFTPD for Anonymous FTP

That's how you secure anonymous FTP in a general sense. But what about actual configuration settings on an actual FTP server? Let's examine some, using the powerful ProFTPD package as our example.

9.1.2.1 Getting ProFTPD

ProFTPD is now included in binary form in most Linux distributions, including Red Hat, SuSE, and Debian. Make sure, however, that your distribution's version is no older than 1.2.0rc3, due to known vulnerabilities in prior versions. As of this writing, the most current stable version of ProFTPD is 1.2.4.

If your distribution of choice provides a ProFTPD package older than 1.2.0rc3 and doesn't have a newer one on its "updates" or "errata" web site (see Chapter 3), you can get ProFTPD from the official ProFTPD download site, ftp://ftp.proftpd.org. Source code is located at this site (and its mirrors) in the /distrib/source/ directory; RPM and SRPM packages are located in /distrib/packages/.

Note that if you use the official ProFTPD RPMs, you'll need to download two packages: the base package proftpd plus one of proftpd-inetd, proftpd-standalone, proftpd-inetd-mysql, or proftpd-standalone-mysql — depending on whether you intend to run ProFTPD from inetd/xinetd or as a standalone daemon and whether you need a ProFTPD binary that was compiled with MySQL database support. (ProFTPD can be compiled to support the use of a MySQL database for authenticating FTP users.)

9.1.2.1.1 Inetd/Xinetd Versus standalone mode

On a lightweight, multipurpose system on which you don't anticipate large numbers of concurrent FTP users, you may want to run ProFTPD from inetd or xinetd : in this way, the FTP daemon will be started only when an FTP user tries to connect. This means that ProFTPD won't consume system resources except when being used.

Also, whenever you edit /etc/proftpd.conf, the changes will be applied the next time a user connects without further administrative intervention, since the daemon reads its configuration file each time it's invoked by inetd or xinetd. The other advantage of this startup method is that you can use Tcpwrappers with ProFTPD, leveraging the enhanced logging and access controls Tcpwrappers provides.

The disadvantages of starting ProFTPD from an Internet superserver such as inetd or xinetd are twofold. The first is performance: ProFTPD's full startup each time it's invoked this way, in which it reads and processes its entire configuration file, is inefficient if the daemon is started repeatedly in a short period of time, and users will notice a delay when trying to connect. The second disadvantage is that some of ProFTPD's best features, such as virtual servers, are available only in standalone mode.

On a dedicated FTP system, therefore, or any other on which you expect frequent or numerous FTP connections, standalone mode is better. When run as a persistent daemon, ProFTPD reads its configuration only once (you can force ProFTPD to reread it later by issuing a kill -HUP command to its lowest-numbered process), which means that whenever a new child process is spawned by ProFTPD to accept a new connection, the new process will get to work more quickly than an inetd-triggered process.

9.1.2.2 ProFTPD modules

Like Apache, ProFTPD supports many of its features via source-code modules. If you install ProFTPD from binary packages, the choice of which modules to compile in ProFTPD has already been made for you (which is why you have multiple RPMs from which to choose from downloading Red Hat ProFTPD packages).

Some modules are included automatically in all ProFTPD builds (and thus all binary packages): mod_auth, mod_core, mod_log, mod_ls, mod_site, mod_unixpw, mod_xfer, and, if applicable to your platform, mod_pam. These modules provide ProFTPD's core functionality, including such essentials as authentication, syslog logging, and FTP command parsers.

Optional and contributed modules, which you generally must compile into ProFTPD yourself, include mod_quota, which provides support for putting capacity limits on directory trees, and mod_wrap, which provides support for TCPwrappers-style access control (i.e., via /etc/hosts.allow and /etc/hosts.deny). There are many other ProFTPD modules: see the file README.modules in the ProFTPD source code for a complete list.

Compiling ProFTPD is simple using the conventional ./configure && make && make install method. You can tell the configure script which optional/contributed modules to include via the — with-modules flag, e.g.:

[root@myron proftpd-1.2.4]# ./configure --with-modules=mod_readme:mod_quota

It isn't necessary to specify the automatically included modules mod_auth, mod_core, etc.

9.1.2.3 Setting up the anonymous FTP account and its chroot jail

Once ProFTPD is in place, it's time to set it up. You should begin by creating or configuring the anonymous FTP user account, which is usually called "ftp." Check your system's /etc/passwd file to see whether your system already has this account defined. If it's there already, make sure its entry in /etc/passwd looks like the one in Example 9-2.

Example 9-2. An /etc/passwd entry for the user ftp
ftp:x:14:50:FTP User:/home/ftp:/bin/true

Make sure of the following:

  • The group ID is set to an unprivileged group such as "ftp" (in the case of Example 9-2, you'll need to look up GID 50 in /etc/group to determine this).

  • The home directory is set to the directory you wish to use as an anonymous FTP chroot jail.

  • The shell is set to a bogus, noninteractive shell such as /bin/true or /bin/false.

If you don't already have the account "ftp," first create a group for it by adding a line like this to /etc/group:

ftp:x:50:

(Alternatively, you can use an existing unprivileged group such as "nobody" or "nogroup.") Then, add the user "ftp" using the useradd command:

[root@myron etc]# useradd -g ftp -s /bin/true ftp

Red Hat Linux's useradd behaves differently from SuSE's, Debian's, and probably that of most other (non-Red Hat-derived) distributions: on a Red Hat system, useradd automatically creates the user's home directory under /home and copies the contents of /etc/skel into it, using the specified username as the directory's name (e.g., /home/ftp). Clearly, you don't want the FTP user account to be loaded down with all this garbage.

Be sure, therefore, to specify the home directory with the -d directive, which will cause Red Hat's useradd to behave "normally." That is, it will list the specified directory in the new user's /etc/passwd entry, but will not create or populate the home directory (unless the -m flag is also present).

On other distributions, useradd doesn't create the new user's home directory unless the -m flag is given.

To specify a different directory on Red Hat Linux or to tell your non-Red Hat useradd to create the user directory, use the -d directive, e.g.:

[root@myron etc]# useradd -g ftp -s /bin/true -d /var/ftp ftp

If useradd didn't create your ftp user's home directory (i.e., the chroot jail), do so manually. In either case, make sure this directory's user ID is root and its group ID is either root or some other privileged group to which your anonymous FTP account does not belong.

If useradd did create your ftp user's home directory, either because you passed useradd the -m flag or because you run Red Hat, remove the dot (".") files and anything else in this directory copied over from /etc/skel. ProFTPD won't let anonymous users see such "invisible" files, but the fact that they aren't needed is reason enough to delete them if present.

With ProFTPD it's also unnecessary for this directory to contain any copies of system files or directories. (ProFTPD doesn't rely on external binaries such as ls.). Thus, all you need to do is create the jail directory itself, populate it with the things you intend to make available to the world, and set appropriate ownerships and permissions on the jail and its contents, as described earlier in Section 9.1.1.3 and illustrated in Example 9-1.

Continuing our sample ProFTPD setup, suppose you want the jail to be group writable for your system administrators, who all belong to the group wheel. Suppose further that you need to accept files from anonymous users and will therefore allow write access to the directory incoming/. Example 9-3 shows a recursive listing on our example anonymous FTP chroot jail, /home/ftp.

Example 9-3. Example ProFTPD chroot jail
/home:
drwxrwxr-x   2 root    wheel     4096 Apr 21 16:56 ftp
   
/home/ftp:
total 12
-rwxrwx-wx   1 root    wheel      145 Apr 21 16:48 incoming
-rwxrwxr-x   1 root    wheel      145 Apr 21 16:48 pub
-rw-rw-r--   1 root    wheel      145 Apr 21 16:48 welcome.msg
   
/home/ftp/incoming:
total 0
   
/home/ftp/pub:
total 8
-rw-rw-r--   1 root    wheel      145 Apr 21 16:48 hotdish_recipe_no6132.txt
-rw-rw-r--   1 root    wheel     1235 Apr 21 16:48 pretty_good_stuff.tgz

As you can see, most of Example 9-3 is consistent with Example 9-1. Notable differences include the absence of etc/ and bin/ and the fact that everything is writable by its group-owner, wheel.

Also, in Example 9-3 there's a world-writable but non-world-readable incoming/ directory, to which all the warnings offered earlier under Section 9.1.1.3 are emphatically applicable. (Make sure this directory has a quota set or is mounted as a discrete filesystem, and move anything uploaded there into a privileged directory as soon as possible).

9.1.2.4 General ProFTPD configuration

Now that we've built the restaurant, it's time to train the staff. In the case of ProFTPD, the staff is pretty bright and acclimates quickly. All we need to do is set some rules in /etc/proftpd.conf.

As I stated earlier, ProFTPD has an intentionally Apache-like configuration syntax. Personally, I consider this to be not only a convenience but also, in a modest way, a security feature. Confusion leads to oversights, which nearly always result in bad security; ergo, when applications use consistent interfaces, allowing their administrators to transfer knowledge between them, this ultimately enhances security. (This, and not mental laziness, is the main reason I hate sendmail.cf's needlessly arcane syntax — see Chapter 7.)

The /etc/proftpd.conf file installed by default requires only a little customization to provide reasonably secure anonymous FTP services. However, for our purposes here, I think it's more useful to start fresh. You'll understand ProFTPD configuration better this way than if I were to explain the five or six lines in the default configuration that may be the only ones you need to alter.

Conversely, if your needs are more sophisticated than those addressed by the following examples, view the documentation of the ProFTPD binary packages generally put under /usr/share/doc/proftpd/ or /usr/share/doc/packages/proftpd/. Particularly useful are the "ProFTPD Configuration Directives" page (Configuration.html) and the sample proftpd.conf files (in the subdirectory named either examples/ or sample-configurations/, depending on your version of ProFTPD).

Before we dive into proftpd.conf, a word or two about ProFTPD architecture are in order. Like Apache, ProFTPD supports "virtual servers," parallel FTP environments physically located on the same system but answering to different IP addresses or ports. Unlike Apache, however, ProFTPD does not support multiple virtual servers listening on the same combination of IP address and port.

This is due to limitations of the FTP protocol. Whereas HTTP 1.1 requests contain the hostname of the server being queried (i.e., the actual URL entered by the user), FTP requests do not. For this reason, you must differentiate your ProFTPD virtual servers by IP address (by assigning IP aliases if your system has fewer ethernet interfaces than virtual hosts) or by listening port. The latter approach is seldom feasible for anonymous FTP, since users generally expect FTP servers to be listening on TCP 21. (But this is no big deal: under Linux, it's very easy to assign multiple IP addresses to a single interface.)

9.1.2.5 Base-server and global settings

On to some actual configuring. The logical things to start with are base-server settings and global settings. These are not synonymous: base-server (or "primary-server") settings apply to FTP connections to your server's primary IP address, whereas global settings apply both to the base server and to all its virtual servers.

You might be tempted in some cases to assume that base-server settings are inherited by virtual servers, but resist this temptation, as they usually aren't. With regard to directives that may be specified in both base-server and virtual-host configurations, the base server is a peer to your virtual servers, not some sort of master. Thus, you need both base-server and global settings (unless you have no virtual servers — in which case you can put everything with your base-server settings).

There are some base-server settings that are inherited by virtual hosts: most of these settings may only be set in the base-server section. They include ServerType, MaxInstances, the Timeout... directives, and the SQL... directives. See ProFTPD's Configuration.html file for a complete reference, which includes each directive's permitted contexts.

Example 9-4 contains settings that apply only to the base server, plus some that apply globally because of their very nature.

Example 9-4. Base-server settings in /etc/proftpd.conf
# Base Settings:
   
ServerType                 standalone
MaxInstances               30
TimeoutIdle                300
TimeoutNoTransfer          300
TimeoutStalled             300
UseReverseDNS              no
LogFormat                  uploadz "%t %u\@*l \"%r\" %s %b bytes"
SyslogFacility             LOCAL5
   
# Base-server settings (which can also be defined in <VirtualHost> blocks):
ServerName                 "FTP at Polkatistas.org"
Port                       21
MasqueradeAddress          firewall.polkatistas.org
<Limit LOGIN>
  DenyAll
</Limit>

Let's step through the settings of Example 9-4 one by one, beginning with what I think of as "base-server but actually global" settings (settings that may only be specified in the base-server section and that actually apply globally). Paradoxically, none of these may be set in a <Global> configuration block.

ServerType standalone

Lets you tell ProFTPD whether it's being invoked by inetd (or xinetd, but either way, the value of this directive would be inetd) or as a standalone daemon.

MaxInstances 30

Limits the number of child processes the proftpd daemon may spawn when running in standalone mode and is therefore an upper limit on the number of concurrent connections. Unlike MaxClients, attempted connections past this number are dropped silently — i.e., without any error message being returned to the prospective client.

Setting this directive has ramifications not only for performance and availability, but also for security because it's the most efficient means of handling the large number of simultaneous connection attempts that are the hallmark of FTP Denial of Service attacks.

TimeoutIdle 300

Specifies the number of seconds of idle time (during which no commands are issued by the client) before the server closes the connection. Set a value here, even a high one, to mitigate exposure to Denial of Service attacks.

TimeoutNoTransfer 300

Specifies the maximum number of seconds the server will leave the connection open without any requests from the user to upload or download files or request directory listings. Setting this is another means of limiting DoS opportunities.

TimeoutStalled 300

Specifies the number of seconds after which the server will close a stalled data connection. Useful in mitigating certain PASV-based DoS attacks.

UseReverseDNS no

Normally, ProFTPD attempts to resolve all client IP addresses before writing log entries. This can impair performance under a heavy load, however, and you can always perform reverse-DNS resolution later when you analyze the logs. I therefore recommend setting this to no.

LogFormat uploadz "%t %u\@*l \"%r\" %s %b bytes"

Lets you specify a custom log-message format that can be referenced later in ExtendedLog directives (see Example 9-6). Custom formats make such messages more easy to monitor or process by tools such as Swatch (covered in Chapter 10).

SyslogFacility LOCAL5

Specifies a Syslog facility other than the default combination of AUTH and DAEMON to which ProFTPD's messages can be written: in Example 9-4, all ProFTPD's Syslog messages will go to LOCAL5. See Chapter 10 for a description of these facilities.

And this brings us to Example 9-4's "plain vanilla" base-server settings. These directives may be declared in either base-server or virtual-server sections. None of these, however, may be declared in a <Global> block (which, in this case, makes sense).

ServerName "FTP at Polkatistas.org"

Naturally, each base/virtual server will print a brief greeting to users. Set it here. Note that this "name" bears no relation to DNS whatsoever — i.e., it needn't contain the name registered to the server's IP address in DNS. (In that sense, the directive might have been more accurately named ServerBanner.) Note also that this string will not be displayed prior to login if ServerIdent is set to off (see Example 9-5).

Port 21

The TCP port on which this server will listen for FTP control connections. Different base/virtual servers listening on the same IP address must listen on different ports, so if you're stingy with IP aliases (e.g., you want to host multiple virtual servers but don't have more than one routable IP to assign to your Ethernet interface), you'll need to use this directive. The expected and therefore default TCP port is, of course, 21.

MasqueradeAddress firewall.polkatistas.org

This is the IP address or FQDN that your server will display in application-layer messages to clients. Your server knows its real name and IP address, of course, but this directive substitutes it with the IP address or hostname of a proxy or firewall from whom the server's packets will appear (to external hosts) to originate. The masquerade address/name will be displayed prior to login unless ServerIdent is set to off (see Example 9-5).

For a Network-Address-Translated (NAT-ed) server to be reachable via its own DNS-registered name, your firewall or proxy may need to have a static mapping from a virtual IP (IP alias) on the outside interface of the firewall to the server's actual IP address. If you have multiple Internet-routable IP addresses at your disposal, this is the best way to handle more than one or two different servers and/or services: having one-to-one mappings of virtual (firewall) IP addresses to publicly accessible servers minimizes confusion at all levels.

If, however, the server's listening port isn't already in use by the firewall (i.e., you don't need more than one protected server reachable via that port number), then you can simply register a DNS CNAME record that resolves ftp.yourdomain.com (or whatever you want your server to be known as) to the name and thus the primary IP address of the firewall. Then you can configure your firewall to forward all incoming connections to that port to your server.

ProFTPD's MasqueradeAddress directive is useful in either case.

<Limit LOGIN>

DenyAll

</Limit>

This configuration block is used to specify access controls on a command or set of commands. In Example 9-5, ProFTPD is configured to deny all attempts by all users (i.e., DenyAll) to execute the command LOGIN (i.e., to log on). This may seem rather extreme: surely you want to let somebody log on. Indeed you do, and we'll therefore specify an exception to this shortly. proftpd.conf directives are hierarchical, with specific directives overriding more general ones. Skip ahead to Example 9-6 if you're curious to see how.

You can use <Limit> configuration blocks in <Global> blocks, but other limits set in the base-server and virtual-server settings may or may not take precedence. Therefore, I don't recommend using <Limit> in <Global> blocks except for commands that aren't limited elsewhere (i.e., except when there are no exceptions to the defined limit).

After base-system settings, you should define global settings. This is done via one or more <Global> configuration blocks (multiple blocks will be combined into one by proftpd's configuration parser).

Example 9-5 lists our sample FTP server's global settings. (That is, our technically global settings, not our "base-server-but-actually-global" settings.)

Example 9-5. Global settings in /etc/proftpd.conf
# Global Settings: shared by base server AND virtual servers
   
<Global>
  ServerIdent              off
  AllowRetrieveRestart     on
  MaxClients               20 "Sorry, all lines are busy (%m users max)."
  MaxClientsPerHost        1  "Sorry, your system is already connected."
  Umask                    022
  User                     nobody
  Group                    nogroup
</Global>

Again, let's examine these directives:

ServerIdent off

If set to on (the default if empty or left out altogether), this displays the server's software name and version prior to prompting users for login. In the interests of disclosing configuration details only when necessary, I recommend you set this to off. If some user's FTP client software expects or requires server identification, you can always set it back to on.

AllowRetrieveRestart on

Actually, I don't believe this directive has any impact on security, but it's worth mentioning that many Linux users use the wget command to download files, and one of wget's best features is the ability to resume interrupted file transfers. Given the importance and popularity of this feature, I recommend you set AllowRetrieveRestart to on so that your FTP server honors requests for "download resumption."

You can also enable upload resumption (e.g., file writes to incoming/) by enabling the AllowStoreRestart directive. But since uploading is inherently more prone to abuse than downloading, I do not recommend this even within a controlled incoming/ directory — unless you have a compelling need for large file uploads to succeed at all costs, or if the uploads in question are performed by authenticated users. (But remember, I don't believe in using FTP for anything that is that important to begin with — use sftp or scp instead!)

MaxClients 20

The MaxClients directive specifies the maximum number of concurrent logins to a given base/virtual server, irrespective of the number of active processes, i.e., regardless of whether ProFTPD is being run in standalone mode or from inetd/xinetd. You may specify an error message to return to attempted clients who exceed this number, in which you may reference the "magic string" %m (which is expanded to the value of MaxClients).

MaxClientsPerHost 1

Use MaxClientsPerHost to limit the number of concurrent connections originating from the same host (based on IP address). On the face of it, this seems a good way to mitigate DoS attacks and other abuses, except for two problems.

First, multiple users' connections originating from behind the same firewall or proxy server will typically appear to come from a single host (i.e., from the proxy or firewall). Second, users connected to the same client system (such as an ISP's "shell-account" server) will likewise share a single IP.

In short, the MaxClientsPerHost directive assumes that legitimate users will tend to have unique IP addresses. If you anticipate this not being the case, set this directive to a relatively high number (say, 50) or leave it unset for no limit at all.

Umask 022

The umask you set with this directive applies to any file or directory created by a logged-in FTP user. You probably don't need to set this if you don't have any writable FTP directories, but then again, it can't hurt (assuming, of course, you set a restrictive umask such as 022).

User, Group...

When specified in a server section (either base server or a <Virtual> block), these directives set the username and group name, respectively, under which the daemon should run except when performing privileged functions such as binding to TCP Port 21 at startup (when ProFTPD must be root, it will temporarily become root). If you declare no User or Group directives, by default ProFTPD will always run as root, which is dangerous. In most cases, it makes sense to declare them in a <Global> block and additionally in <Anonymous> configuration blocks (see Example 9-6).

9.1.2.6 Anonymous FTP setup

Now that your base-server and global-server options are defined, it's time to tell your base server whether and how to handle anonymous FTP connections. Directives in an <Anonymous> configuration block override any also set in its "parent" configuration (the base-, global-, or virtual-server section within which the Anonymous block is nested). Since in Example 9-5 you disabled ordinary user logins (actually all logins) in the base-server configuration, you'll need to enable it here, and indeed you shall (Example 9-6).

Example 9-6. Anonymous FTP settings in /etc/proftpd.conf
# Anonymous configuration, uploads permitted to "incoming"
<Anonymous ~ftp>
  User                          ftp
  Group                         ftp
  UserAlias                     anonymous ftp
  MaxClients                    30
  DisplayLogin                  welcome.msg
  ExtendedLog                   /var/log/ftp_uploads WRITE uploadz
  AllowFilter                   "^[a-zA-Z0-9 ,.+/_\-]*$"
   
  <Limit LOGIN>
    AllowAll
  </Limit>
   
  <Limit WRITE>
    DenyAll
  </Limit>
   
  <Directory incoming/*>
    <Limit READ DIRS CWD>
      DenyAll
    </Limit>
   
    <Limit STOR>
      AllowAll
    </Limit>
  </Directory>
   
</Anonymous>

And here's the blow-by-blow explanation of Example 9-6:

<Anonymous ~ftp>...

In the <Anonymous> tag itself, we must specify the home directory to be used and chrooted to by these anonymous users. You can use a tilde (~) as shorthand for "the home directory of the following user account." In this example, ~ftp translates to /home/ftp.

User, Group...

In the context of server configurations, recall that these directives apply to the daemon itself. In the context of <Anonymous> blocks, however, they apply to the anonymous user in question, i.e., to the specific proftpd child-process handling the user's connection. In this context, I recommend setting these to a different username and group than those used by the server's daemon to more easily differentiate the restricted environment in which you wish to contain anonymous users.

UserAlias anonymous ftp

The UserAlias directive lets you map one username to another. Since by convention both the usernames ftp and anonymous are allowed for anonymous FTP (and in fact, the original Unix ftpd automatically accepted the username anonymous as an alias for ftp), in Example 9-6 anonymous" is being explicitly mapped as an alias for the real user account "ftp."

Note that if the alias you map is an actual account on the server, users logging in as that username will not have that actual user's privileges; they'll have those of the account to which the alias is mapped, which, of course, is hopefully an unprivileged account. That might seem obvious, but it's an important security feature (i.e., it's one less mistake you as an administrator can make!). Thus, if I specify UserAlias wizzo ftp, forgetting that wizzo is a privileged user on my system, when I later connect as wizzo, I will have ftp's privileges, not wizzo's.

MaxClients 30

This directive does the same thing here it does elsewhere (limits the total connecting clients), but specifically for these particular anonymous users.

Which Commands Can ProFTPD Limit?

ProFTPD's configuration directives, including the <Limit> configuration block and the ExtendedLog directive, accept FTP commands as arguments. Confusing to some users, however, may be the fact that these aren't end-user commands entered into FTP client software; they're the FTP protocol commands that the client software sends to the server over an FTP control channel. Thus, put, cd, get, et al are not valid arguments to ProFTPD directives. Instead, use the commands in Table 9-1.

Table 9-1. FTP commands that ProFTPD may limit

Command

Description

End-user equivalent

CWD

Change working directory

cd

DELE file

Delete a file

delete

MKD

Make a new directory

mkdir

RMD

Remove a directory

rmdir

RNFR RNTO

Space-separated pair of commands; rename a file or directory

rename

SITE_CHMOD

Change the mode on a file or directory

chmod

RETR

Retrieve (download) a file

get

STOR

Store (upload) a file

put

ALL

Not a command; wildcard referring to "all FTP commands"

N/A

LOGIN

Not really a command; used by ProFTPD to limit login attempts

N/A

DIRS

Not really a command; wildcard that refers to all directory-list-related commands (e.g., LIST, NLIST, etc.)

N/A

READ

Wildcard that refers to all file-reading commands but not directory-listing commands

N/A

WRITE

Wildcard that refers to all write/overwrite attempts by client (STOR, MKD, RMD, etc.)

N/A

DisplayLogin welcome.msg

DisplayLogin tells ProFTPD to display the contents of the specified file (welcome.msg, in this example) after successful logon. This directive may also be defined at the server level, not just in <Anonymous> configuration blocks.

ExtendedLog /var/log/ftp_uploads WRITE uploadz

This directive lets you specify a special log file (/var/log/ftp_uploads in Example 9-6) to which messages will be written with the specified format (e.g., uploadz) when the specified command is executed (WRITE in Example 9-6). If no command is specified, all FTP actions applicable to the command block or server configuration will be logged, and if no custom format is specified, the default format will be used.

This directive may be used for directories specified in <Directory> configuration blocks. It may also be used in broader contexts, as is the case in Example 9-6, in which it applies to all WRITE commands issued by all anonymous users applicable to this block.

AllowFilter "^[a-zA-Z0-9 ,.+/_\-]*$"

This handy directive limits the allowable characters in FTP commands to those contained in the specified regular expression. In Example 9-6, the regexp ("^[a-zA-Z0-9 ,.+/_\-]*$") tells ProFTPD to reject any command string that contains anything except alphanumeric characters, whitespace, and the few punctuation marks commonly found in legitimate filenames. (Since commands' arguments are parsed too, it's important to make sure any characters contained in files you wish to share are included in this regular expression.)

<Limit LOGIN>

AllowAll

</Limit>

Here, finally, we present the base-server configuration with an exception to its "deny all logins" policy. Limits specified within a nested configuration block apply only to that block and to any additional blocks nested within it. Thus, even though in Example 9-6 it appears as though all logins will be permitted, in fact, only anonymous logins to the server will work (i.e., logins to the account ftp or its alias anonymous).

<Limit WRITE>

DenyAll

</Limit>

This <Limit> block says that all applicable anonymous clients will be forbidden to write, overwrite, or create any files or directories.

<Directory incoming/*>...

ProFTPD lets you apply groups of directives to a specific directory or directory tree via the <Directory> configuration block. In Example 9-6, the <Directory> block applies to /home/ftp/incoming/ and its subdirectories: this is to be a publicly writable directory.

<Limit READ DIRS CWD>

DenyAll

</Limit>

First, we specify that the incoming/ directory won't be readable, listable, or recurseable. We want anonymous users to be able to write files into it, period. Letting them do anything else opens the door for abuses such as sharing pornography, pirated software, etc.

<Limit STOR>

AllowAll

</Limit>

Finally, in this <Limit> we explicitly allow the writing of files to this directory. We could have instead used the wildcard WRITE, but it would allow the creation of directories, and all we want is to allow is file uploads.

That may have seemed like a lot of work, but we've got a lot to show for it: a hardened ProFTPD installation that allows only anonymous logins to a restricted chroot environment, with a special log file for all attempted uploads.

Hopefully, you also now understand at least the basics of how to configure ProFTPD. These examples are by no means all inclusive; there are many other configuration directives you may use. See the "ProFTPD Configuration Directives" page (Configuration.html), included with ProFTPD packages and source code, for a comprehensive reference for proftpd.conf.

9.1.2.7 Virtual-server setup

Before we move on to other things, there's one more type of ProFTPD configuration we should examine due to its sheer usefulness: virtual servers. I've alluded to these a couple of times in the chapter, but to review, virtual-server definitions host multiple FTP sites on the same host in such a way that they appear to reside on separate hosts.

Let's look at one example that adds a virtual server to the configuration file illustrated in Examples Example 9-4 through Example 9-6. Suppose our FTP server has, in addition to its primary IP address 55.44.33.22, the IP alias 55.44.33.23 bound to the same interface. A virtual-server definition for this second IP address might look like this (Example 9-7).

Example 9-7. A virtual server definition in /etc/proftpd.conf
<VirtualHost 55.44.33.23>
   
  Port 21
  <Limit LOGIN>
    DenyAll
  </Limit>
   
  <Anonymous /home/ftp_hohner>
    User                    ftp
    Group                   ftp
    UserAlias               anonymous ftp
    MaxClients              30
    DisplayLogin            welcome_hohner.msg
    AllowFilter             "^[a-zA-Z0-9 ,]*$"
   
    <Limit LOGIN>
      AllowAll
    </Limit>
   
    <Limit WRITE>
      DenyAll
    </Limit>
   
    </Anonymous>
</VirtualHost>

Besides the <VirtualHost> configuration block itself, whose syntax is fairly obvious (you must specify the IP address or resolvable name of the virtual host), you've seen all these directives in earlier examples. Even so, two things are worth pointing out.

First, the IP specified in the <VirtualHost> tag can be the host's primary address — i.e., the IP of the base server. However, if you do this, you must use the Port directive to specify a different port from the base server's in the virtual host setup. A virtual server can have the same IP address or the same listening port as the base server, but not both.

Second, absent from this configuration block but implicit nonetheless are the settings for ServerIdent, AllowRetrieveRestart, MaxClients, MaxClientsPerHost, Umask, User, and Group defined earlier in the <Global> definitions in Example 9-5 (so are the first eight directives listed in Example 9-4.)

By the way, you may have noticed that I didn't bother specifying ServerName or Masquerade Address. Since the global ServerIdent setting is off, these wouldn't be displayed anyway.

Creating IP aliases in Linux is simple. The most direct method is to use this form of ifconfig:

ifconfig ifacename:n alias

where ifacename is the name of the physical interface to which you wish to bind the alias, n is an integer (use 0 for the interface's first alias and increment by 1 for each additional alias on the same interface), and alias is the IP address you wish to add. The command to create the IP alias used in Example 7-7 would look like this:

ifconfig eth0:0 55.44.33.23

You can add such a command to your /etc/init.d/network startup script to make the IP alias persistent across reboots. Alternatively, your Linux distribution may let you create IP aliases in its network-configuration utility or GUI.

    Team LiB   Previous Section   Next Section