FTP Server Behind a NAT Gateway Using OpenBSD 3.0
by Dan Brosemer <odin@cleannorth.org>Warning
This document is very old and has not been maintained since it was written. I lost it for a while and found it again in the wayback machine so here it is. As is. I may find time to read it over and update it again eventually, but for now, YMMV.
Intro
It's a question that comes up on the mailing lists every now and then, and I've never seen a satisfactory answer. How do I set up an FTP server behind a NAT?
DaemonNews ran an article on it in a while ago, which you can read at http://www.daemonnews.org/200109/ftpnat.html, but that was for ipf on OpenBSD, and so it won't work with 3.0.
I've put together a fully working solution for OpenBSD 3.0 based on the DaemonNews article, and a little elbow grease. I have not tested the efficiency of this solution, so use at your own risk.
Details
Active FTP is easy to support. We simply add a 'rdr' directive to nat.conf to send port 21 over to our FTP server, and make sure the FTP server can make connections out on arbitrary high-numbered ports.
Passive FTP is a little different. In a normal NAT configuration, the FTP server will think its IP is something like 192.168.1.2, and hosts outside our private network will have no route for that.
So, we have to make the FTP server think its IP is the external IP of the whole NAT setup. It turns out that when you think of the problem from this angle, OpenBSD makes it surprisingly simple.
- We assign a new IP to the FTP server as an alias to make things simpler.
- We redirect incoming FTP control connections from the gateway machine to the FTP server.
- We configure the lo1 interface on the FTP server with the external IP of the entire NAT setup (making sure the netmask is set to /32!).
- We narrow the selection of ports that ftpd has to choose from for passive connections to make our lives simpler.
- We redirect connections to the FTP server on the aliased IP to our fake network on the lo1 interface.
- We do another NAT on the FTP server to map the external IP to the internal IP when traffic is leaving the FTP server to keep things consistent in our NAT environment.
- We redirect all the possible ports that could be used for passive FTP to the FTP server and then to the external IP on the lo1 interface.
Implementation
- On the FTP server:
In/etc/hostname.<ext_if>add the following line:
inet alias <ftp_ip> 255.255.255.255 NONE - On the Gateway:
Add to/etc/nat.conf:
rdr on <ext_if> proto tcp from any to <ext_ip> port 21 -> <ftp_ip> port 21 - On the FTP server:
Create/etc/hostname.lo1with the following text:
inet <ext_ip> 255.255.255.255 NONE - On the FTP server:
Add to/etc/sysctl.conf:
net.inet.ip.porthilast=49161 #this gives us 49152-49161 for passive FTP
Make sure your start your ftpd with the -h directive. Something like:/usr/libexec/ftpd -DllUSAhwill do quite nicely. - On the FTP server:
Add to/etc/nat.conf:rdr on <ftp_if> from any to <ftp_ip> port 21 -> <ext_ip> port 21 - On the FTP server:
Before any rdr directives, in/etc/nat.conf, add:
nat on <ftp_if> from <ext_ip> to any -> <ftp_ip> - On the Gateway:
Add to/etc/nat.conf:rdr on <ext_if> from any to <ext_ip> port 49152 -> <ftp_ip> port 49152 rdr on <ext_if> from any to <ext_ip> port 49153 -> <ftp_ip> port 49153 rdr on <ext_if> from any to <ext_ip> port 49154 -> <ftp_ip> port 49154 rdr on <ext_if> from any to <ext_ip> port 49155 -> <ftp_ip> port 49155 rdr on <ext_if> from any to <ext_ip> port 49156 -> <ftp_ip> port 49156 rdr on <ext_if> from any to <ext_ip> port 49157 -> <ftp_ip> port 49157 rdr on <ext_if> from any to <ext_ip> port 49158 -> <ftp_ip> port 49158 rdr on <ext_if> from any to <ext_ip> port 49159 -> <ftp_ip> port 49159 rdr on <ext_if> from any to <ext_ip> port 49160 -> <ftp_ip> port 49160 rdr on <ext_if> from any to <ext_ip> port 49161 -> <ftp_ip> port 49161
And On the FTP server:
Add to/etc/nat.conf:rdr on <ftp_if> from any to <ftp_ip> port 49152 -> <ext_ip> port 49152 rdr on <ftp_if> from any to <ftp_ip> port 49153 -> <ext_ip> port 49153 rdr on <ftp_if> from any to <ftp_ip> port 49154 -> <ext_ip> port 49154 rdr on <ftp_if> from any to <ftp_ip> port 49155 -> <ext_ip> port 49155 rdr on <ftp_if> from any to <ftp_ip> port 49156 -> <ext_ip> port 49156 rdr on <ftp_if> from any to <ftp_ip> port 49157 -> <ext_ip> port 49157 rdr on <ftp_if> from any to <ftp_ip> port 49158 -> <ext_ip> port 49158 rdr on <ftp_if> from any to <ftp_ip> port 49159 -> <ext_ip> port 49159 rdr on <ftp_if> from any to <ftp_ip> port 49160 -> <ext_ip> port 49160 rdr on <ftp_if> from any to <ftp_ip> port 49161 -> <ext_ip> port 49161
Now, just make sure your firewall doesn't block tcp ports 21, or 49152-49161 in, and allows your FTP server to make connections out.
You'll either want to reboot your gateway and ftp server or issue a few ifconfig, pfctl, and sysctl commands that are beyond the scope of this document.