Beginner 5 min FTP 227

227 Entering Passive Mode — Wrong IP Behind NAT

Gejala

- FTP control connection and login succeed, but LIST or RETR/STOR hangs indefinitely and eventually times out
- The 227 PASV response in the client log shows a private IP address: (10,0,1,50,...) or (192,168,1,10,...) or (172,16,...)
- External clients fail while clients on the same local network as the server transfer files without any issue
- `nc -zv SERVER_PUBLIC_IP DATA_PORT` from the client machine fails to connect
- Cloud-hosted servers (AWS EC2, GCE, DigitalOcean) report their private instance IP, which is unreachable from the public internet

Penyebab Utama

  • FTP server behind NAT reports its private/internal IP in the PASV response because `pasv_address` is not configured — the server has no awareness of its public-facing IP address
  • Cloud instance (AWS EC2, GCE, Azure VM) uses the VPC private IP by default in vsftpd, and the NAT gateway's public IP is not visible to the OS
  • FTP server behind a load balancer or reverse proxy that does not rewrite the PASV response — the backend reports its own private IP
  • Docker container running vsftpd reports the container's internal IP (172.17.x.x) instead of the host's public IP
  • Dynamic public IP changed (home server, ISP rotation) but `pasv_address` still holds the old IP

Diagnosis

**Step 1 — Capture the raw PASV exchange**

In FileZilla (View → Message Log) or any FTP client with logging enabled:

```
→ PASV
← 227 Entering Passive Mode (10,0,1,50,195,149)
```

**Step 2 — Decode the IP and port from the 227 response**

```python
# Format: (h1,h2,h3,h4,p1,p2)
h1,h2,h3,h4,p1,p2 = 10,0,1,50,195,149
ip = f'{h1}.{h2}.{h3}.{h4}' # 10.0.1.50 ← private!
port = p1 * 256 + p2 # 50069
print(f'Client will try to connect to {ip}:{port}')
```

If the IP is in the RFC 1918 ranges (10/8, 172.16-31/12, 192.168/16), `pasv_address` is not set correctly.

**Step 3 — Find the server's public IP**

```bash
# On the server:
curl -s https://api4.my-ip.io/ip # shows public IPv4
# Or for AWS EC2:
curl -s http://169.254.169.254/latest/meta-data/public-ipv4
```

**Step 4 — Verify current vsftpd configuration**

```bash
grep -E 'pasv_address|pasv_min_port|pasv_max_port' /etc/vsftpd.conf
# If pasv_address is missing or set to a private IP, that is the problem
```

Resolusi

**Fix 1 — Set pasv_address in vsftpd**

```ini
# /etc/vsftpd.conf
pasv_enable=YES
pasv_address=203.0.113.42 # replace with your actual public IP
pasv_min_port=50000
pasv_max_port=51000
```

```bash
sudo systemctl restart vsftpd
# Verify the fix: reconnect and check the 227 response
# 227 Entering Passive Mode (203,0,113,42,195,149) ← public IP now
```

**Fix 2 — ProFTPD equivalent**

```apache
# /etc/proftpd/proftpd.conf
MasqueradeAddress 203.0.113.42
PassivePorts 50000 51000
```

**Fix 3 — Docker vsftpd — pass public IP as environment variable**

```yaml
# docker-compose.yml
services:
ftp:
image: fauria/vsftpd
environment:
- PASV_ADDRESS=203.0.113.42
- PASV_MIN_PORT=50000
- PASV_MAX_PORT=51000
ports:
- '21:21'
- '50000-51000:50000-51000'
```

**Fix 4 — Dynamic public IP (use DNS name instead)**

```ini
# vsftpd supports a hostname that resolves to the public IP:
pasv_address=ftp.example.com
pasv_addr_resolve=YES
```

Update the DNS A record whenever the IP changes, and vsftpd will resolve it on each PASV command.

Pencegahan

- Always configure `pasv_address` when deploying an FTP server on any system behind NAT — cloud instances, home servers, Docker containers, and VMs all require this setting
- Use a fixed (Elastic/Reserved/Static) IP for FTP servers to avoid the `pasv_address` becoming stale after IP rotation
- Test FTP passive mode from an external IP (not the server's own LAN) as part of your post-deployment checklist — the problem is invisible from inside
- Document the `pasv_address` setting in infrastructure-as-code (Ansible, Terraform user data) so it is set automatically on re-provisioning
- Consider replacing FTP with SFTP entirely — SFTP's single-connection model has no active/passive distinction and no NAT PASV IP problem

Kode Status Terkait

Istilah Terkait