SSD Nodes Learn 🎉 VPS từ $5.50/tháng
Hướng dẫn Matt ConnorBởi Matt Connor · Cập nhật ngày 2026-08-21

FTP passive mode: firewall làm treo danh sách thư mục

FTP đăng nhập được nhưng danh sách thư mục bị treo vì kênh dữ liệu dùng port riêng. Cấu hình dải port passive và firewall rule đúng để hết timeout.

Vì sao FTP đăng nhập được nhưng danh sách thư mục bị treo

FTP passive mode bị lỗi trên firewall vì FTP sử dụng 2 kết nối TCP, không phải 1. Kết nối đến port 21 truyền thông tin đăng nhập và các lệnh, nên username và password được chấp nhận, đồng thời firewall trông có vẻ đã cấu hình đúng. Sau đó, ls đầu tiên cần một kết nối thứ hai trên port khác. Firewall không cho phép kết nối đó, nên client chờ đến khi timeout.

Cách khắc phục là cấu hình một dải port cố định cho các kết nối dữ liệu đó và thêm firewall rule cho phép đúng dải port này. Server nằm sau NAT (network address translation) cần thêm một thiết lập để quảng bá đúng địa chỉ. Trước đây, connection tracking helper tự xử lý việc này. Hiện nay chúng không còn làm vậy nữa. Bạn cần biết lý do trước khi áp dụng lại một hướng dẫn cũ.

Kênh điều khiển và kênh dữ liệu

FTP (file transfer protocol) được quy định trong RFC 959, ra đời trước cả NAT và firewall stateful. Một phiên mở một kết nối điều khiển đến cổng TCP 21 và giữ kết nối này trong suốt phiên. Các lệnh được gửi dưới dạng plain text. Phản hồi gồm một mã gồm 3 chữ số và một dòng văn bản. Kết nối này không truyền nội dung file.

Mỗi phần dữ liệu sử dụng một kết nối TCP riêng: một kết nối cho danh sách thư mục (LIST), một kết nối cho mỗi lần download (RETR), và một kết nối cho mỗi lần upload (STOR). Kết nối được mở, sử dụng một lần rồi đóng. Việc xác thực diễn ra hoàn toàn trên kênh điều khiển, nên lỗi trên đường truyền dữ liệu luôn có cùng biểu hiện: đăng nhập thành công rồi bị treo. Nếu client in ra phản hồi 230 rồi dừng khi lấy danh sách, vấn đề nằm ở kênh dữ liệu, không phải thông tin đăng nhập.

Chế độ active: server kết nối ngược lại đến client

Ở chế độ active, client chọn một port, lắng nghe trên port đó rồi cho server biết cần kết nối đến đâu:

PORT 192,168,1,50,195,80

Bốn số đầu tiên là địa chỉ IP của client. Hai số cuối là port, được mã hóa thành 2 byte: 195 * 256 + 80 = 50000. Sau đó, server mở kết nối dữ liệu từ port 20 của chính nó đến port 50000 trên client.

Theo góc nhìn của client, đây là kết nối inbound không được yêu cầu, nên firewall của client sẽ drop kết nối này. Nếu client nằm sau home router, địa chỉ trong lệnh PORT là địa chỉ private mà server hoàn toàn không thể truy cập. Active mode là nguyên nhân khiến FTP mang tiếng là không hoạt động.

Chế độ passive: client mở cả hai kết nối

Chế độ passive đảo chiều kết nối dữ liệu. Client gửi PASV và server trả về địa chỉ cùng một port của nó:

227 Entering Passive Mode (203,0,113,10,195,80)

Cách mã hóa giống nhau, nên client kết nối đến 203.0.113.10 trên port 50000. Client lúc này mở cả hai kết nối. Vì vậy, chế độ passive hoạt động qua NAT phía client và mọi client hiện nay đều thử chế độ này trước.

Vấn đề chỉ chuyển vị trí chứ không biến mất. Kết nối inbound không được yêu cầu lúc này đi vào server của bạn, trên một port cao thay đổi sau mỗi lần truyền. Firewall đó thuộc quyền quản lý của bạn, nên vấn đề cũng thuộc về bạn.

EPSV (extended passive mode, RFC 2428) hoạt động theo cùng nguyên lý nhưng có phản hồi gọn hơn:

229 Entering Extended Passive Mode (|||50000|)

Phản hồi không chứa địa chỉ. Client dùng lại địa chỉ đã có cho kết nối control. Đây là yếu tố giúp chế độ này hoạt động qua IPv6 và loại bỏ một nhóm lỗi NAT. Manual của curl nêu rằng curl thường thử EPSV trước PASV. Port vẫn được chọn tại runtime, nên EPSV không thay đổi gì trong các rule của firewall.

Vì sao một rule firewall thông thường không thể cho phép data channel

Vì số port chưa tồn tại khi bạn viết rule. Server chọn port cho từng lần truyền. Với giá trị mặc định, tài liệu vsftpd ghi pasv_min_portpasv_max_port0, nghĩa là “dùng bất kỳ port nào”, nên data connection có thể đến trên bất kỳ port nào lớn hơn 1023. sudo ufw allow 21/tcp chỉ cho phép control channel, không cho phép gì khác. Đây chính xác là cấu hình tạo ra tình trạng đăng nhập được nhưng không xem được danh sách file. Nếu khái niệm service listening trên một port cố định vẫn chưa rõ, cách port và listening socket hoạt động trên Linux sẽ cung cấp phần nền cần thiết.

Stateful firewall có theo dõi connection và kernel có thể cho phép một connection mới là RELATED của một connection hiện có. Với FTP, cần có thành phần đọc control stream và lấy port từ dòng 227 hoặc PORT. Mặc định không có thành phần nào làm việc đó.

Vì sao connection tracking helper cho FTP không còn là đáp án

Kernel module nf_conntrack_ftp là module mà các hướng dẫn cũ thường yêu cầu bạn dùng. Module này đọc control channel dạng plaintext, tìm cổng được thông báo và đăng ký một expectation, để data connection được cho phép mà không cần rule chỉ rõ cổng đó. Từ khi các hướng dẫn này được viết đã có 4 thay đổi.

Tự động gán helper đã bị tắt. Kernel ghi rõ sysctl nf_conntrack_helper là “0 - disabled (default)” và thêm rằng: “Nếu bị tắt, cần thiết lập các iptables rule để gán helper cho các connection.” Chỉ load module thì không có tác dụng.

Trên các kernel hiện tại, switch này đã bị loại bỏ. Chạy sysctl net.netfilter.nf_conntrack_helper. Nếu nhận được sysctl: cannot stat /proc/sys/net/netfilter/nf_conntrack_helper: No such file or directory, nghĩa là kernel không còn cơ chế tự động gán helper để bật lại. Nếu nhận được một số khác, switch vẫn còn và giá trị mặc định là 0.

Các firewall front end cũng đã deprecated cơ chế này. man ufw-framework trên Ubuntu 24.04 ghi về dòng IPT_MODULES trong /etc/default/ufw: “Việc load vô điều kiện các connection tracking module (nf_conntrack_*) theo cách này đã bị deprecated”, đồng thời cho biết các helper rule “phải được quản lý trong RULES FILES”. firewalld ghi AutomaticHelpers trong firewalld.conf là “Deprecated. Tùy chọn này bị bỏ qua và không còn được sử dụng.” Hiện tại, việc gắn helper yêu cầu tự viết một rule rõ ràng với target CT, phức tạp hơn cách khắc phục bên dưới và sẽ ngừng hoạt động ngay khi bạn bật TLS. iptables và nftables trên Ubuntu giải thích các rule đó thực sự nằm ở đâu.

TLS kết thúc vấn đề này. Helper hoạt động bằng cách đọc control channel dưới dạng văn bản. Khi mã hóa channel đó, helper chỉ thấy ciphertext nên không thể tìm cổng. Không có cách khắc phục cho việc này, và cũng không nên có: một middlebox có thể đọc control channel của bạn cũng là middlebox đã đọc mật khẩu của bạn.

Khai báo dải port passive trên server

Mọi FTP server đều có thể được cấu hình để chọn port passive trong một dải do bạn chỉ định. Tên option khác nhau tùy server, vì vậy hãy xem tài liệu của server bạn đang chạy.

vsftpd, trong /etc/vsftpd.conf:

pasv_enable=YES
pasv_min_port=30000
pasv_max_port=30099

pasv_enable mặc định đã là YES. Hai option port mặc định là 0, tức hành vi “dùng bất kỳ port nào” được mô tả ở trên. Áp dụng thay đổi bằng sudo systemctl restart vsftpd, rồi xác nhận service đã khởi động lại bằng systemctl status vsftpd. vsftpd từ chối khởi động khi gặp dòng cấu hình không thể phân tích, thay vì bỏ qua dòng đó. Nếu restart thất bại, hãy đọc journalctl -u vsftpd -n 20 để tìm dòng 500 OOPS: chỉ rõ option bạn vừa nhập.

ProFTPD, trong proftpd.conf:

PassivePorts 30000 30099

Tài liệu ProFTPD không nêu giá trị mặc định ở đây. Nếu không có directive này, kernel sẽ chọn port. Tài liệu cũng nêu rằng khi không còn port nào trong dải khả dụng, server sẽ fallback về port do kernel cấp phát và ghi một thông báo vào log. Vì vậy, dải quá nhỏ sẽ thỉnh thoảng thất bại thay vì thất bại rõ ràng. Trường hợp này khó chẩn đoán hơn nhiều. Hãy dùng các port non-privileged, từ 1024 trở lên.

Pure-FTPd nhận một flag là -p first:last. Tài liệu tại man pure-ftpd mô tả flag này là “Chỉ dùng các port trong dải từ port đầu đến port cuối, bao gồm cả hai đầu, cho các lượt download ở passive mode”, và flag này “giúp pure-ftpd tương thích hơn với packet filter”. Các bản build được đóng gói thường đưa flag này vào một file cấu hình. Vì vậy, hãy xem tài liệu riêng của distribution để biết tên file thay vì đoán.

Bạn cần bao nhiêu port? Mỗi data connection đang hoạt động cần một port. Một TCP port đã đóng sẽ ở trạng thái TIME_WAIT trong vài phút trước khi có thể được dùng lại, vì vậy hãy dự phòng số port gấp vài lần mức cao nhất dự kiến. Một trăm port là đủ thoải mái cho vài người dùng. Một server public có nhiều traffic sẽ cần nhiều hơn đáng kể.

Dải port nên nằm ở đâu? Trước tiên hãy chạy sysctl net.ipv4.ip_local_port_range. Trên Ubuntu mặc định, kết quả là 32768 60999, tức các port kernel cấp cho outgoing connection. Một dải passive nằm trong khoảng này có thể xung đột với outgoing connection đang giữ port đó, vì vậy hãy đặt dải bên dưới khoảng này. 30000 đến 30099 không bị dùng trên một máy mặc định. Hãy kiểm tra chính máy của bạn thay vì tin vào con số đó.

Mở cùng dải cổng trên firewall

ufw ghi dải cổng bằng dấu hai chấm. Tài liệu của ufw nêu rằng một dải hoặc danh sách cổng “cũng có thể được dùng để chỉ định nhiều cổng; trong trường hợp đó, bắt buộc phải chỉ rõ protocol”:

sudo ufw allow 21/tcp
sudo ufw allow 30000:30099/tcp
sudo ufw status verbose

ufw status verbose lúc này phải liệt kê cả hai rule. Nếu chạy cùng lệnh nhưng không có /tcp, ufw sẽ từ chối và báo lỗi yêu cầu chỉ rõ tcp hoặc udp, vì ufw không tự đoán protocol. cú pháp rule của ufw trên VPS trình bày phần còn lại.

firewalld ghi dải cổng bằng dấu gạch ngang và cần reload:

sudo firewall-cmd --permanent --add-service=ftp
sudo firewall-cmd --permanent --add-port=30000-30099/tcp
sudo firewall-cmd --reload
sudo firewall-cmd --list-all

--add-service=ftp mở 21/tcp và yêu cầu ftp helper, đúng với tên được định nghĩa trong service definition đi kèm. Lệnh này không mở dải cổng passive, nên nếu chỉ chạy lệnh đó thì vấn đề vẫn còn nguyên. zone và service của firewalld trên VPS giải thích đầy đủ hơn.

nftables thuần, bên trong input chain:

tcp dport { 21, 30000-30099 } accept

Còn một firewall khác cần nhớ: hầu hết nhà cung cấp chạy network firewall trong control panel, nằm bên ngoài operating system. Nếu các rule trên máy chủ đều đúng nhưng packet vẫn không bao giờ đến được máy chủ, hãy mở cùng dải cổng ở đó.

Khai báo địa chỉ public của server khi server nằm sau NAT

Chạy ip -4 addr show trên server. Nếu địa chỉ trên interface là địa chỉ mà client dùng để kết nối, hãy bỏ qua phần này. Nếu interface giữ một địa chỉ private (10.x, 172.16 đến 172.31.x, 192.168.x) và platform ánh xạ một địa chỉ public vào đó, server không biết địa chỉ public của chính nó. Tài liệu vsftpd ghi rằng giá trị mặc định của pasv_address là “địa chỉ được lấy từ socket kết nối đến”, vì vậy phản hồi 227 chứa địa chỉ private và client bị gửi đến một địa chỉ mà nó không thể truy cập.

FileZilla gọi chính xác lỗi này là:

Server sent passive reply with unroutable address. Using server address instead.

FileZilla tự sửa lỗi rồi tiếp tục. Nhiều client khác không làm vậy. Chúng kết nối đến 10.0.0.5 rồi bị treo.

curl cũng ẩn vấn đề này, điều này quan trọng nếu bạn dùng curl để kiểm thử. Tài liệu của curl ghi rằng --ftp-skip-pasv-ip “được bật theo mặc định (được thêm từ 7.74.0)”, vì vậy curl bỏ qua địa chỉ trong phản hồi 227 và dùng lại địa chỉ của control connection. Một transfer hoạt động với curl vẫn có thể thất bại trong client đồ họa chỉ vì lý do này.

Hãy đặt địa chỉ một cách rõ ràng. vsftpd dùng pasv_address=203.0.113.10, cùng với pasv_addr_resolve=YES (mặc định là NO) nếu bạn muốn ghi hostname. ProFTPD dùng MasqueradeAddress; tùy chọn này chấp nhận địa chỉ, tên DNS hoặc tên interface. Pure-FTPd dùng -P, được tài liệu mô tả cho trường hợp “server nằm sau một box masquerading (NAT)”. EPSV tránh hoàn toàn vấn đề này vì phản hồi của nó không có trường địa chỉ, nhưng bạn không thể dựa vào đó vì client quyết định sẽ gửi command nào.

Thay đổi khi dùng TLS

FTPS là FTP chạy trên TLS (bảo mật tầng truyền tải). Client kết nối đến port 21 như bình thường, gửi AUTH TLS để bảo vệ control channel, sau đó gửi PROT P để mã hóa cả data channel. FTP thuần truyền password trên network dưới dạng văn bản có thể đọc được, vì vậy nếu bắt buộc phải dùng FTP, hãy chạy FTPS. vsftpd được phát hành với ssl_enable mặc định là NO.

Có 2 hệ quả. Không connection tracking helper nào có thể hoạt động. Đây chính là vấn đề nêu ở trên nhưng nhìn từ phía ngược lại. Ngoài ra, sudo tcpdump -nAi any 'tcp port 21' sẽ không còn hiển thị phản hồi 227, vì vậy khi cần biết server đã quảng bá address và port nào, hãy đọc log của chính server thay vì đọc dữ liệu trên network.

Kiểm tra thay đổi từ bên ngoài

Chạy các lệnh này từ một máy khác. Kiểm tra ngay trên server sẽ bỏ qua firewall mà bạn đang cố sửa.

sudo ss -ltnp | grep :21
curl -v --disable-epsv --user ftpuser:secret ftp://example.com/
nc -vz example.com 30000

ss phải hiển thị FTP daemon đang listen trên port 21. Khi server không hoạt động, không có tiến trình nào listen trên dải passive, vì các socket đó được tạo cho mỗi lần truyền dữ liệu rồi đóng lại.

--disable-epsv buộc curl đi theo đường dẫn PASV. Đây là đường dẫn làm lộ lỗi địa chỉ. Trace sẽ in phản hồi của server, sau đó là địa chỉ và port mà curl kết nối đến:

< 227 Entering Passive Mode (203,0,113,10,117,52)

117 * 256 + 52 = 30004, nằm trong dải đã khai báo. Nếu dòng đó chứa private address, nghĩa là pasv_address chưa được đặt. Nếu port nằm ngoài dải của bạn, nghĩa là server chưa đọc thay đổi cấu hình. Hãy kiểm tra bạn đã sửa đúng file mà service đang chạy sử dụng.

nc tự trả lời câu hỏi về firewall. Connection refused xuất hiện ngay lập tức nghĩa là packet đã đến server nhưng không tìm thấy tiến trình nào đang listen, đây là kết quả đúng đối với passive port đang idle: rule của bạn hoạt động. Nếu kết nối treo đến khi nc bỏ cuộc, nghĩa là có thành phần đã âm thầm drop packet. Đó có thể là firewall trên máy hoặc firewall trong panel của nhà cung cấp. Phân biệt này cũng giống như phần SSH bị từ chối hay hết thời gian chờ đã mô tả và áp dụng cho mọi port.

Bạn có nên tiếp tục chạy FTP không?

Với công việc mới thì không. SFTP (SSH file transfer protocol) chạy bên trong một kết nối SSH duy nhất trên cổng 22. Không có kênh thứ hai, không có dải passive, không cần cấu hình NAT và không cần daemon bổ sung để bảo mật, vì OpenSSH đã cung cấp sẵn chức năng này. sftp user@example.com hoạt động trên máy chủ mà bạn chưa từng cấu hình dịch vụ truyền file. Để cấp cho ai đó quyền truy cập file mà không cấp thêm quyền nào khác, sshd_config dùng ForceCommand internal-sftp cùng với ChrootDirectory. Thư mục đó phải thuộc sở hữu của root và không được cho user ghi vào. Nếu không, sshd sẽ từ chối session và ghi log một dòng bad ownership or modes for chroot directory.

FTP vẫn phù hợp khi phía bên kia không thể thay đổi. Scanner và máy in đa chức năng thường được phát hành với firmware chỉ hỗ trợ FTP. Thiết bị phòng thí nghiệm và thiết bị công nghiệp thường chạy một image cố định mà không ai muốn chứng nhận lại. Đối tác kinh doanh có thể cung cấp một FTPS drop và không chấp nhận bổ sung protocol cho riêng một nhà cung cấp. Trong các trường hợp đó, cấu hình passive range cùng firewall rule tương ứng là toàn bộ công việc cần làm. Hãy chạy FTPS thay vì plain FTP. Thiết kế hai kênh là một quyết định từ năm 1985 và hiện phải hoạt động trong môi trường mà nó chưa từng dự liệu, như được trình bày trong lịch sử các protocol truyền file.

FAQ

Vì sao FTP đăng nhập được nhưng treo khi liệt kê thư mục?

Đăng nhập chỉ dùng kết nối điều khiển trên cổng 21, là cổng firewall của bạn cho phép. Liệt kê thư mục cần thêm một kết nối TCP trên cổng khác, nhưng kết nối đó bị chặn. Khai báo một dải cổng passive trên FTP server và mở cùng dải đó trong firewall thì việc liệt kê sẽ hoàn tất. Danh sách bị treo là vấn đề ở kênh dữ liệu, không phải vấn đề mật khẩu.

Tôi cần mở những cổng nào cho FTP passive mode?

Mở cổng 21 cho kênh điều khiển, cùng với dải cổng bạn đã cấu hình cho các kết nối dữ liệu passive. Không có dải tiêu chuẩn vì bạn tự chọn dải này. Dải như 30000 đến 30099 có thể hoạt động: chọn kích thước dựa trên số transfer đồng thời cao nhất và tránh trùng với dải cổng outgoing của kernel, có thể xem bằng sysctl net.ipv4.ip_local_port_range. Nếu nhà cung cấp có network firewall trong control panel, hãy mở cùng dải đó tại đó.

Tôi còn cần nf_conntrack_ftp không?

Không. Với kernel hiện tại, bạn cũng không thể dựa vào module này. Việc tự động gán helper bị tắt mặc định. Trên các kernel mới, switch net.netfilter.nf_conntrack_helper đã bị xóa nên sysctl báo file không tồn tại. Manual của ufw xem việc load các module đó một cách vô điều kiện là đã deprecated, còn firewalld hoàn toàn bỏ qua AutomaticHelpers. Helper cũng phải đọc kênh điều khiển dưới dạng plain text, nên sẽ ngừng hoạt động ngay khi bạn bật FTPS. Thay vào đó, hãy khai báo một dải cổng passive.

Vì sao FTP client báo phản hồi passive có địa chỉ không thể định tuyến?

Server trả lời PASV bằng địa chỉ mà nó thấy trên interface của chính nó, và địa chỉ đó là private. Điều này xảy ra khi platform ánh xạ một địa chỉ public vào một địa chỉ private. Hãy đặt rõ địa chỉ public: pasv_address trong vsftpd, MasqueradeAddress trong ProFTPD hoặc -P trong Pure-FTPd. FileZilla xử lý tình huống này bằng cách dùng lại địa chỉ mà nó đã kết nối, đồng thời ghi log "Using server address instead". Vì vậy, một số client vẫn hoạt động dù cấu hình sai, còn các client khác bị treo.

Tôi nên dùng FTPS hay SFTP?

Dùng SFTP cho mọi trường hợp bạn kiểm soát cả hai đầu: chỉ cần một kết nối qua SSH trên cổng 22, không cần mở kênh dữ liệu và SFTP thường đã chạy sẵn. FTPS là FTP chạy trên TLS, nên vẫn giữ thiết kế hai kênh cùng mọi vấn đề firewall đi kèm. Chọn FTPS khi phía bên kia không hỗ trợ giao thức nào khác. Không dùng plain FTP qua Internet vì mật khẩu đi qua network dưới dạng văn bản có thể đọc được.