FTP behind NAT with TLS howto
Ever wondered how to set up a more secure FTP server?
I did. And the first thing that came to my mind was getting those username/password things encrypted.
Who did invent a plaintext login mechanism!?
Sheeesh…
So, let’s get started.
This howto will tell you how to set up an Proftpd based FTP server with TLS encryption for data&control channels.
Firstly, I assume that you have a working proftpd installation.
You can get it simply by:
apt-get install proftpd
So, you should be able to login to the server and sniff your credentials using i.e. tcpdump or tcpick.
Anyway, now we want to go into TLS stuff.
Firstly, let’s set up a plain TLS configuration.
Edit your /etc/proftpd/proftpd.conf, and add the following section:
TLSEngine on TLSProtocol TLSv1 TLSRequired off TLSRSACertificateFile ftpcert.pem TLSRSACertificateKeyFile ftpcert.pem TLSCACertificateFile cacert.pem TLSVerifyClient off TLSRenegotiate required off
Of course, you have to set proper paths to your Certificate and Key files (and CA certificate file as well).
Now. You can force all clients to use TLS encryption, with TLSRequired option. However, be sure that all clients are aware of it, because otherwise, they will be unable to log in (with most of the FTP clients).
For instance, Total Commander now supports FTP w/TLS (since version 7.02 I think), but you have to add openssl.dll to it’s installation dir.
Furthermore, decide if you want to use any Verification mechanism for your certificate (TLSVerifyClient directive).
TLSRenegotiate is used to keep some buggy clients happy, as they break during renegotiation session.
Now, do a restart and providing that you’ve enabled the mod_tls module, you have TLS working.
You can be happy now, but read further as you will probably encounter some problems later.
All seems quite well when you test your installation without NAT.
The problems start to show up when NAT goes into action.
Probably you will encounter the ‘hanging listing’ problem? :)
So, there is a number of issues here to check.
- If you are not able to get your FTP listing even without TLS, check if you have appriopriate modules loaded into kernel:
host:~# lsmod | grep ftp ip_nat_ftp 3200 0 ip_nat 16172 3 ip_nat_ftp,ipt_MASQUERADE,iptable_nat ip_conntrack_ftp 7280 1 ip_nat_ftp ip_conntrack 46644 6 ip_nat_ftp,ipt_MASQUERADE,xt_state,iptable_nat,ip_nat,ip_conntrack_ftp
- if your listing works ok with NAT wo/TLS, but it doesn’t w/TLS, then you got to the point that I struggled. So I will explain a bit. While using NAT, you start to use Passive Mode (PASV). In this mode, the FTP server, is starting to communicate via some high-ranged ports mapped accordingly to the established connection (source,destination) on the stateful firewall (iptables). In a non-TLS scenario that firewall’s nat_ftp module is aware of the ports that FTP server is using for the PASV mode (as it looks up the PASV commands inside the packets traversed). The problem appears when you start using TLS mode. Then, the firewall is unable to see the ports anymore, because the packets and control channel is encrypted. And we have a problem. Solution? Here it is:
I’m assuming that you are using iptables, that your policy is DROP on the INPUT chain, and that you have permitted RELATED,ESTABLISHED and all traffic to port 20,21 (standard ftp ports). Now. Add the following line to you protftpd.conf:
PassivePorts 50000 51000
What does it do? It tells the server to use this explicit range of ports when establishing FTP PASV connection. This way we know which ports it will use.
Now the only thing you can do to get it working is opening those ports on firewall:
iptables -I INPUT -m state --state NEW -j ACCEPT -p tcp -m multiport --ports 50000:51000
Voila! It should work now!
One more thing. If you have a lot of FTP clients/traffic, consider widening this range, as you may run out of the ports that can be assigned to users (when many of them are connecting simultaneously).
I got an error when using 50000:51000 – I don’t think : is valid here and according to the iptables man page only 15 ports can be specified with multiport. Also I had to add — to your second state option.
Here’s the rule I ended up using
# Allow ftp passive port connections inbound (as I’m using SSL now for FTP Control sessions ip_conntrack_ftp can’t see anything)
iptables -A INPUT -p tcp -i eth0 -m state –state NEW -j ACCEPT -m multiport –dports 51000,51001,51002,51003,51004,51005
I put these entries in my vsftpd.conf
pasv_min_port=51000
pasv_max_port=51005
Thanks for the info – it helped.
Hi John,
Nope, it is perfectly valid. You mix port specified by commas with a portrange specified with x:y notation.
man iptables
look for port range.
Thanks a lot for this post.
It really solved my problem!!!
The X:Y notation is perfectly valid, at least with iptables v. 1.4.6.
warden:~# iptables -D INPUT -p tcp –destination-port 50000:51000 -m state –state NEW -j ACCEPT
Chain INPUT (policy DROP 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
0 0 ACCEPT tcp — * * 0.0.0.0/0 0.0.0.0/0 tcp dpts:50000:51000 state NEW