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
Passwords are transferred in the clear
The protocol demands the use of at least two TCP connections (control and data) on separate ports
When a session is established, data is communicated via ports selected at random
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.
If your configuration is based on a PF version that is old enough to warrant using any FTP proxy other than the first one here, I strongly urge you to upgrade to a more recent system.
Enabling FTP transfers through your gateway is amazingly simple, thanks to the FTP proxy program included in the OpenBSD base system. The program is called, you guessed it, ftp-proxy.
The FTP protocol being what it is, the proxy needs to dynamically insert rules in your rule set. ftp-proxy interacts with your configuration via an anchor where the proxy inserts and deletes the rules it constructs to handle your FTP traffic.
To enable ftp-proxy, you need to add this line to your /etc/rc.conf.local file:
Or, if you want to keep text file editing to a minimum and you're running a recent-ish version of OpenBSD, you can use rcctlto enable and start the daemon:
$ doas rcctl enable ftpproxy $ doas rcctl start ftpproxy
If you want to start the proxy in IPv6 mode, you enable and start the ftpproxy6 service in the same manner.
On FreeBSD, you also need to add
You can start the proxy manually by running /usr/sbin/ftp-proxy if you like, and you may in fact want to in order to check that the changes to the PF configuration we are about to do have the effect we want.
For a basic configuration, you only need to add three elements to your /etc/pf.conf. First, the anchor:
In pre-OpenBSD 4.7 based versions, two anchors were needed:
nat-anchor "ftp-proxy/*" rdr-anchor "ftp-proxy/*"
The proxy will insert the rules it generates for the FTP sessions here. Then you also need a pass rule to let ftp traffic in to the proxy.
pass in quick inet proto tcp to port ftp divert-to 127.0.0.1 port 8021
Note the divert-to part. This diverts the traffic to the local port where the proxy listens. Also make sure both the anchor and the divert rule are placed before any match rules for NAT.
Pre-OpenBSD 5.0 proxies, used rdr-to:
pass in quick proto tcp to port ftp rdr-to 127.0.0.1 port 8021 pass in quick inet6 proto tcp to port ftp rdr-to ::1 port 8021
And finally, the pre-OpenBSD 4.7 version of this rule is
rdr pass on $int_if proto tcp from any to any port ftp -> 127.0.0.1 \ port 8021
(IPv4 only here to encourage you to upgrade to something modern.) Finally, add a pass rule to let the packets pass from the proxy to the rest of the world:
pass out proto tcp from $proxy to any port ftp
where $proxy expands to the address the proxy daemon is bound to.
Reload your PF configuration,
$ doas pfctl -f /etc/pf.conf
and before you know it, your users will thank you for making FTP work.
This example covers a basic setup where the clients in your local net need to contact FTP servers elsewhere. The basic configuration here should work well with most combinations of FTP clients and servers. As you will notice from looking at the man page, you can change the proxy's behavior in various ways by adding options to the ftpproxy_flags= line. You may bump into clients or servers with specific quirks that you need to compensate for in your configuration, or you may want to integrate the proxy in your setup in specific ways such as assigning FTP traffic to a specific queue. For these and other finer points of ftp-proxy configuration, your best bet is to start by studying the man page.
If you are looking for ways to run an FTP server protected by PF and ftp-proxy, you could look into running a separate ftp-proxy in reverse mode (using the -R option), on a separate port with its own redirecting pass rule.