That sad old FTP thing

Table of Contents
FTP through NAT: ftp-proxy
FTP, PF and routable addresses: ftpsesame, pftpx and ftp-proxy!
ftp-proxy, new style

The short list of real life TCP ports we looked at a few moments back contained, among other things, FTP. FTP is a sad old thing and a problem child, emphatically so for anyone trying to combine FTP and firewalls. FTP is an old and weird protocol, with a lot to not like. The most common points against it, are

All of these points make for challenges security-wise, even before considering any potential weaknesses in client or server software which may lead to security issues. These things have tended to happen.

Under any circumstances, other more modern and more secure options for file transfer exist, such as sftp or scp, which feature both authentication and data transfer via encrypted connections. Competent IT professionals should have a preference for some other form of file transfer than FTP.

Regardless of our professionalism and preferences, we are all too aware that at times we will need to handle things we would prefer not to. In the case of FTP through firewalls, the main part of our handling consists of redirecting the traffic to a small program which is written specifically for this purpose.

Depending on your configuration, which operating system you are using as the platform for your PF firewall and how you count them, three or four different options are available for this particular task.

We will present them in roughly chronological order according to their ages. The original FTP proxy for PF is described below in the Section called FTP through NAT: ftp-proxy. We then move on to two newer, intermediate solutions developed by Camiel Dobbelaar in the Section called FTP, PF and routable addresses: ftpsesame, pftpx and ftp-proxy! before finally moving on to the modern FTP proxy which was introduced in OpenBSD 3.9 in the Section called ftp-proxy, new style.

FTP through NAT: ftp-proxy

NoteOpenBSD 3.8 or earlier equivalents only
 

This section is headed for purely historical status when the last PF port to other systems has caught up. In November 2005, the old ftp-proxy (/usr/libexec/ftp-proxy) was replaced in OpenBSD-current with the new ftp-proxy, which lives in /usr/sbin. This is the software which is included in OpenBSD 3.9 onwards and what you will be using on modern PF versions. See the Section called ftp-proxy, new style for details.

The old style ftp-proxy which is a part of the base system on systems which offer a PF version based on OpenBSD3.8 or earlier is usually called via the inetd "super server" via an appropriate /etc/inetd.conf entry.[1]

The line quoted here specifies that ftp-proxy runs in NAT mode on the loopback interface, lo0:

127.0.0.1:8021 stream tcp nowait root /usr/libexec/ftp-proxy \
  ftp-proxy -n

This line is by default in your inetd.conf, commented out with a # character at the beginning of the line. To enable your change, you restart inetd.

On FreeBSD, NetBSD and other rcNG based BSDs you do this with the command

FreeBSD$ sudo /etc/rc.d/inetd restart

or equivalent. Consult man 8 inetd if you are unsure. At this point inetd is running with your new settings loaded.

Now for the actual redirection. Redirection rules and NAT rules fall into the same rule class. These rules may be referenced directly by other rules, and filtering rules may depend on these rules. Logically, rdr and nat rules need to be defined before the filtering rules.

We insert our rdr rule immediately after the nat rule in our /etc/pf.conf

rdr on $int_if proto tcp from any to any port ftp -> 127.0.0.1 \
         port 8021

In addition, the redirected traffic must be allowed to pass. We achive this with

pass in on $ext_if inet proto tcp from port ftp-data to ($ext_if) \
    user proxy flags S/SA keep state

Save pf.conf, then load the new rules with

$ sudo pfctl -f /etc/pf.conf

At this point you will probably have users noticing that FTP works before you get around to telling them what you've done.

This example assumes you are using NAT on a gateway with non routable addresses on the inside.

Notes

[1]

You may need to enable inetd by adding a inetd_enable="YES" line to your rc.conf and possibly adjust other inetd related configuration settings.