FTP’s passive mode might feel like a networking black magic, but it’s actually just a clever way to sidestep firewalls by making the client initiate all connections.
Let’s see it in action. Imagine a client wants to download report.txt from an FTP server.
First, the client connects to the FTP server’s control port (usually 21) to send commands:
<-- USER anonymous
--> 331 Anonymous access allowed, send e-mail address
<-- PASS ftp@example.com
--> 230 User logged in
<-- CWD /pub
--> 250 Directory changed to /pub
<-- PASV
--> 227 Entering Passive Mode (192,168,1,10,15,128)
The server, 192.168.1.10, tells the client it’s ready to open a data connection on IP 192.168.1.10 and port 15 * 256 + 128 = 3968. This is the crucial part: the server listened on a port it chose.
<-- RETR report.txt
--> 150 Opening BINARY mode data connection for report.txt (12345 bytes)
Now, the client initiates the data connection to 192.168.1.10:3968.
--> [DATA CONNECTION ESTABLISHED to 192.168.1.10:3968]
The server then sends the file data over this connection.
<-- [DATA TRANSFER COMPLETE]
--> 226 Transfer complete
<-- QUIT
--> 221 Goodbye
This works because the client, not the server, initiated the data connection, making it look like legitimate outbound traffic to most firewalls. The "passive port range" is simply the pool of ports the FTP server is allowed to listen on for these data connections.
The problem this solves is the inherent difficulty FTP has with firewalls in its active mode. In active mode, the client tells the server which port it is listening on for data, and the server then initiates a connection back to the client. This inbound connection from the server to the client is often blocked by firewalls. Passive mode flips this, making the client initiate both control and data connections.
The internal mechanism is straightforward: when the client sends the PASV command, the FTP server picks an available port from its configured passive port range, binds to it, and tells the client the IP and port it should connect to. The server then waits for the client to establish the data connection on that specific port before sending any data.
On an vsftpd server, you configure this in /etc/vsftpd.conf. You’d typically set a range like pasv_min_port=40000 and pasv_max_port=41000. The server will then randomly select a port within this 40000-41000 inclusive range for each passive data connection. You must ensure that this entire port range is open in your firewall for inbound traffic. If pasv_enable=YES is not set (it’s often the default), passive mode won’t even be offered.
The surprising part is how the server communicates its chosen port. It encodes the port number into the IP address it sends back in the 227 Entering Passive Mode response. The last two octets of the IP address are split, with the first representing the higher-order byte and the second the lower-order byte of the port number. So, (..., x, y) means port x * 256 + y. In our example, (192,168,1,10,15,128) means the server is listening on 192.168.1.10 on port (15 * 256) + 128 = 3840 + 128 = 3968.
The next hurdle you’ll likely face is dealing with FTP servers that are behind NAT, where the server’s internal IP address needs to be translated to its public IP address for clients on the internet to connect to.