FTP can be used in one of two ways: passive or active. Generally, the choice of active or passive is made to determine who has the problem with firewalling.
With active FTP, when a user connects to a remote FTP server and requests information or a file, the FTP server makes a new connection back to the client to transfer the requested data. This is called the data connection. To start, the FTP client chooses a random port to receive the data connection. The client sends the port number it chose to the FTP server and listens for an incoming connection on that port. The FTP server then initiates a connection to the client's address at the chosen port and transfers the data. This is a problem for users attempting to gain access to FTP servers from behind a NAT gateway. Because of how NAT works, the FTP server initiates the data connection by connecting to the external address of the NAT gateway on the chosen port. The NAT machine will receive this, but, because it has no mapping for the packet in its state table, it will drop the packet and won't deliver it to the client.
With passive mode FTP (the default mode with OpenBSD's
ftp(1) client), the client requests
that the server pick a random port to listen on for the data connection.
The server informs the client of the port it has chosen, and the client
connects to this port to transfer the data.
Unfortunately, this is not always possible or desirable because of the
possibility of a firewall in front of the FTP server blocking the incoming
To force active mode FTP, use the
-A flag to
or set passive mode to "off" by issuing the command "
at the "
PF provides a solution for this situation by diverting FTP traffic through an FTP proxy server. This process acts to "guide" the FTP traffic through the NAT gateway/firewall by actively adding needed rules to PF and removing them when done via the anchor system. The FTP proxy used by PF is ftp-proxy(8).
To activate it, put something like this early in the rules section
pass in quick on $int_if inet proto tcp to port 21 divert-to 127.0.0.1 port 8021This diverts FTP from the clients to the ftp-proxy program, which is listening on port 8021 of the server.
An anchor in the rules section is also needed:
anchor "ftp-proxy/*"The proxy server has to be started and running on the OpenBSD box.
# rcctl enable ftpproxy # rcctl start ftpproxyTo support active mode connections from certain (fussy) clients, the
-rflag may be needed.
pass in on egress proto tcp to port 21 pass in on egress proto tcp to port > 49151That range of ports can be tightened up if desired. In the case of ftpd(8), that is done using the sysctl(8) variables
ftp-proxy can be run in a mode that causes it to forward all FTP connections to a specific FTP server. The proxy will be set up to listen on port 21 of the firewall and forward all connections to the backend server.
# rcctl set ftpproxy flags -R 10.10.10.1 -p 21 -b 192.168.0.1Here 10.10.10.1 is the IP address of the actual FTP server, 21 is the port ftp-proxy will listen on, and 192.168.0.1 is the address on the firewall that the proxy will bind to.
Now for the
ext_ip = "192.168.0.1" ftp_ip = "10.10.10.1" match out on egress inet from $int_if nat-to (egress) anchor "ftp-proxy/*" pass in on egress inet proto tcp to $ext_ip port 21 pass out on $int_if inet proto tcp to $ftp_ip port 21 user _ftp_proxyHere the connection inbound to port 21 is allowed on the external interface, as well as the corresponding outbound connection to the FTP server. The
user _ftp_proxyaddition to the outbound rule ensures that only connections initiated by ftp-proxy are permitted.
tftp-proxy is set up in much the same way as ftp-proxy was in the FTP client behind the firewall section above.
match out on egress inet from $int_if nat-to (egress) anchor "tftp-proxy/*" pass in quick on $int_if inet proto udp from $lan to port tftp \ divert-to 127.0.0.1 port 6969 pass out quick on $ext_if inet proto udp from $lan to port tftp \ group _tftp_proxy divert-replyThe rules above allow TFTP outbound from the internal network to TFTP servers on the external network.
The last step is to enable and start tftp-proxy.
# rcctl enable tftpproxy # rcctl start tftpproxy