SSD Nodes Learn 🎉 VPS เริ่ม $5.50/เดือน
คู่มือ Matt Connorโดย Matt Connor · อัปเดตเมื่อ 2026-08-21

วิธีแก้ปัญหา FTP ค้างตอนแสดงรายการไฟล์หลัง Firewall

ปัญหา FTP ล็อกอินได้แต่ค้างที่ Directory Listing เกิดจาก Data Channel ไม่ได้รับอนุญาตผ่าน Firewall เรียนรู้วิธีตั้งค่า Passive Port Range และกฎ Firewall ที่ถูกต้อง

เหตุใด FTP จึงล็อกอินได้แต่ค้างเมื่อแสดงรายการไดเรกทอรี

FTP ในโหมด passive มักมีปัญหาเมื่อผ่าน firewall เนื่องจาก FTP ใช้การเชื่อมต่อ TCP สองชุด ไม่ใช่ชุดเดียว การเชื่อมต่อผ่านพอร์ต 21 จะทำหน้าที่รับส่งข้อมูลการล็อกอินและคำสั่ง ดังนั้นชื่อผู้ใช้และรหัสผ่านจึงผ่านการตรวจสอบและดูเหมือนว่า firewall ทำงานถูกต้อง แต่การเชื่อมต่อแรก ls จำเป็นต้องมีการเชื่อมต่อชุดที่สองบนพอร์ตอื่น ซึ่งไม่มีกฎใน firewall อนุญาตให้การเชื่อมต่อนี้ผ่านไปได้ ไคลเอนต์จึงค้างรอจนกว่าจะหมดเวลา (timeout)

วิธีแก้ไขคือการกำหนดช่วงพอร์ตที่แน่นอนสำหรับการเชื่อมต่อข้อมูลเหล่านี้ และเพิ่มกฎใน firewall เพื่ออนุญาตช่วงพอร์ตดังกล่าว เซิร์ฟเวอร์ที่อยู่หลัง NAT (network address translation) จำเป็นต้องตั้งค่าเพิ่มเติมอีกหนึ่งอย่างเพื่อให้เซิร์ฟเวอร์ประกาศที่อยู่ IP ที่ถูกต้อง ในอดีตฟีเจอร์ connection tracking helper จะจัดการงานนี้ให้โดยอัตโนมัติ แต่ปัจจุบันไม่เป็นเช่นนั้นแล้ว และเหตุผลที่ฟีเจอร์นี้ถูกยกเลิกไปนั้นเป็นสิ่งที่ควรทราบก่อนที่จะคัดลอกวิธีการจากคู่มือเก่าๆ มาใช้งาน

ช่องทางควบคุมและช่องทางข้อมูล

FTP (file transfer protocol) ถูกกำหนดไว้ใน RFC 959 ซึ่งมีมาก่อนทั้ง NAT และ stateful firewall เซสชันจะเปิดการเชื่อมต่อควบคุมหนึ่งรายการไปยัง TCP port 21 และคงสถานะเปิดไว้ตลอดทั้งเซสชัน คำสั่งจะถูกส่งขึ้นไปเป็นข้อความธรรมดา ส่วนการตอบกลับจะมาในรูปแบบรหัสสามหลักตามด้วยบรรทัดข้อความ การเชื่อมต่อดังกล่าวจะไม่นำพาเนื้อหาของไฟล์

ข้อมูลแต่ละส่วนจะใช้การเชื่อมต่อ TCP ของตนเอง ได้แก่ หนึ่งรายการสำหรับการแสดงรายการไดเรกทอรี (LIST), หนึ่งรายการสำหรับการดาวน์โหลดแต่ละครั้ง (RETR) และหนึ่งรายการสำหรับการอัปโหลดแต่ละครั้ง (STOR) การเชื่อมต่อจะถูกเปิดขึ้น ใช้งานเพียงครั้งเดียว แล้วปิดลง การยืนยันตัวตนจะเกิดขึ้นบนช่องทางควบคุมทั้งหมด ดังนั้นหากเส้นทางข้อมูลขัดข้องจะมีลักษณะเหมือนกันเสมอ คือล็อกอินสำเร็จแล้วค้าง หากไคลเอนต์แสดงการตอบกลับ 230 แล้วหยุดนิ่งที่การแสดงรายการ แสดงว่าปัญหาอยู่ที่ช่องทางข้อมูล ไม่ใช่ที่ข้อมูลประจำตัว (credentials)

โหมด Active: เซิร์ฟเวอร์เชื่อมต่อกลับไปยังไคลเอนต์

ในโหมด Active ไคลเอนต์จะเลือกพอร์ตหนึ่งพอร์ต เปิดรอรับการเชื่อมต่อ และแจ้งให้เซิร์ฟเวอร์ทราบว่าจะให้เชื่อมต่อไปที่ใด:

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

ตัวเลขสี่ชุดแรกคือที่อยู่ IP ของไคลเอนต์ ส่วนตัวเลขสองชุดสุดท้ายคือพอร์ตที่ถูกเข้ารหัสไว้เป็นสองไบต์: 195 * 256 + 80 = 50000 จากนั้นเซิร์ฟเวอร์จะเปิดการเชื่อมต่อข้อมูล จาก พอร์ต 20 ของตนเอง ไปยัง พอร์ต 50000 บนไคลเอนต์

การเชื่อมต่อดังกล่าวถือเป็นขาเข้าและไม่ได้ถูกร้องขอจากมุมมองของไคลเอนต์ ไฟร์วอลล์ของไคลเอนต์จึงปฏิเสธการเชื่อมต่อนี้ หากไคลเอนต์อยู่หลังเราเตอร์ภายในบ้าน ที่อยู่ในคำสั่ง PORT จะเป็นที่อยู่แบบ private ซึ่งเซิร์ฟเวอร์ไม่สามารถเข้าถึงได้เลย โหมด Active คือสาเหตุที่ทำให้ FTP มีชื่อเสียงในเรื่องการใช้งานไม่ได้ผล

Passive mode: ไคลเอนต์เป็นผู้เปิดการเชื่อมต่อทั้งสองฝั่ง

Passive mode จะสลับทิศทางการเชื่อมต่อข้อมูล ไคลเอนต์จะส่งคำสั่ง PASV และเซิร์ฟเวอร์จะตอบกลับด้วยที่อยู่และพอร์ตของตนเอง:

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

ด้วยการเข้ารหัสรูปแบบเดียวกัน ไคลเอนต์จึงเชื่อมต่อไปยัง 203.0.113.10 ที่พอร์ต 50000 ขณะนี้ไคลเอนต์เป็นผู้เปิดการเชื่อมต่อทั้งสองฝั่ง ซึ่งเป็นเหตุผลว่าทำไม passive mode จึงใช้งานผ่าน NAT ฝั่งไคลเอนต์ได้ และเป็นเหตุผลที่ไคลเอนต์ในปัจจุบันทุกตัวเลือกใช้โหมดนี้เป็นอันดับแรก

ปัญหาไม่ได้หายไปเพียงแค่ย้ายที่เท่านั้น การเชื่อมต่อขาเข้าที่ไม่ได้ร้องขอจะเข้ามายังเซิร์ฟเวอร์ของคุณผ่านพอร์ตสูงที่เปลี่ยนไปทุกครั้งที่มีการรับส่งข้อมูล เนื่องจากไฟร์วอลล์นั้นเป็นของคุณ ปัญหานี้จึงกลายเป็นของคุณเช่นกัน

EPSV (extended passive mode, RFC 2428) ใช้แนวคิดเดียวกันแต่มีการตอบกลับที่สะอาดกว่า:

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

ในการตอบกลับไม่มีที่อยู่ระบุไว้ ไคลเอนต์จะนำที่อยู่ที่ใช้สำหรับการเชื่อมต่อควบคุม (control connection) มาใช้ซ้ำ ซึ่งเป็นสิ่งที่ทำให้โหมดนี้ทำงานบน IPv6 ได้และขจัดปัญหาบั๊กประเภท NAT ไปได้หนึ่งกลุ่มใหญ่ คู่มือของ curl ระบุว่าโดยปกติแล้ว curl จะพยายามใช้ EPSV ก่อน PASV เนื่องจากพอร์ตยังคงถูกเลือกในขณะรันไทม์ EPSV จึงไม่ได้เปลี่ยนแปลงกฎไฟร์วอลล์ของคุณแต่อย่างใด

เหตุใดกฎไฟร์วอลล์ปกติจึงไม่อนุญาตให้ใช้งานช่องทางรับส่งข้อมูล (data channel)

เนื่องจากหมายเลขพอร์ตยังไม่ถูกกำหนดขึ้นในขณะที่คุณเขียนกฎ ไฟร์วอลล์ เซิร์ฟเวอร์จะเลือกพอร์ตแบบสุ่มในแต่ละการถ่ายโอนข้อมูล หากใช้ค่าเริ่มต้นตามที่ pasv_min_port และ pasv_max_port ระบุไว้ในเอกสารของ vsftpd ว่าเป็น 0 ซึ่งหมายถึง "ใช้พอร์ตใดก็ได้" ดังนั้นการเชื่อมต่อข้อมูลจึงสามารถเข้ามาผ่านพอร์ตใดก็ได้ที่สูงกว่า 1023 กฎ sudo ufw allow 21/tcp จะอนุญาตเฉพาะช่องทางควบคุม (control channel) เท่านั้น ซึ่งเป็นสาเหตุที่ทำให้การล็อกอินใช้งานได้ปกติแต่ไม่สามารถแสดงรายการไฟล์ได้ หากแนวคิดเรื่องบริการที่รอรับการเชื่อมต่อบนพอร์ตคงที่ยังไม่ชัดเจน คุณสามารถศึกษา วิธีการทำงานของพอร์ตและ listening socket บน Linux เพื่อเป็นพื้นฐานความเข้าใจ

ไฟร์วอลล์แบบ stateful จะติดตามสถานะการเชื่อมต่อ และเคอร์เนลสามารถอนุญาตการเชื่อมต่อใหม่ในฐานะ RELATED ของการเชื่อมต่อเดิมได้ แต่สำหรับ FTP จำเป็นต้องมีกลไกในการอ่านกระแสข้อมูลควบคุมและดึงหมายเลขพอร์ตออกมาจากบรรทัด 227 หรือ PORT ซึ่งโดยปกติแล้วจะไม่มีการตั้งค่าดังกล่าวไว้เป็นค่าเริ่มต้น

เหตุผลที่ FTP connection tracking helper ไม่ใช่คำตอบอีกต่อไป

Kernel module nf_conntrack_ftp คือสิ่งที่คู่มือรุ่นเก่าแนะนำให้ใช้ โมดูลนี้จะอ่าน control channel ที่เป็นข้อความธรรมดา ค้นหาพอร์ตที่ประกาศไว้ และลงทะเบียนสิ่งที่คาดหวัง (expectation) เพื่อให้ data connection สามารถผ่านเข้ามาได้โดยไม่ต้องมีกฎระบุพอร์ตนั้นโดยเฉพาะ แต่มี 4 สิ่งที่เปลี่ยนไปนับตั้งแต่คู่มือเหล่านั้นถูกเขียนขึ้น

การกำหนด helper อัตโนมัติถูกปิดใช้งาน Kernel ระบุค่า nf_conntrack_helper sysctl ไว้ว่า "0 - disabled (default)" และเสริมว่า "หากปิดใช้งาน จำเป็นต้องตั้งค่ากฎ iptables เพื่อกำหนด helper ให้กับการเชื่อมต่อ" การโหลดโมดูลเพียงอย่างเดียวจึงไม่เกิดผลใดๆ

ใน kernel ปัจจุบัน สวิตช์ดังกล่าวถูกถอดออกแล้ว ให้รันคำสั่ง sysctl net.netfilter.nf_conntrack_helper หากได้รับคำตอบเป็น sysctl: cannot stat /proc/sys/net/netfilter/nf_conntrack_helper: No such file or directory หมายความว่า kernel ไม่มีการกำหนด helper อัตโนมัติให้เปิดใช้งานอีกต่อไป หากได้รับตัวเลขกลับมา แสดงว่าสวิตช์ยังคงอยู่และค่าเริ่มต้นคือ 0

เครื่องมือจัดการ firewall ต่างๆ ก็เลิกสนับสนุนเช่นกัน man ufw-framework บน Ubuntu 24.04 ระบุถึงบรรทัด IPT_MODULES ใน /etc/default/ufw ว่า "การโหลดโมดูล connection tracking (nf_conntrack_*) ในลักษณะนี้โดยไม่มีเงื่อนไขถือว่าเลิกใช้แล้ว" และเสริมว่ากฎของ helper "ต้องจัดการผ่านไฟล์กฎ (RULES FILES)" ส่วน firewalld ระบุถึง AutomaticHelpers ใน firewalld.conf ว่า "เลิกใช้แล้ว ตัวเลือกนี้จะถูกละเว้นและไม่มีการใช้งานอีกต่อไป" การแนบ helper ในปัจจุบันหมายถึงการเขียนกฎด้วยเป้าหมาย CT ด้วยตนเอง ซึ่งยุ่งยากกว่าวิธีแก้ไขด้านล่างและจะหยุดทำงานทันทีที่คุณเปิดใช้งาน TLS เนื้อหาใน iptables และ nftables บน Ubuntu จะอธิบายว่ากฎเหล่านั้นควรอยู่ที่ใด

TLS เป็นตัวยุติข้อโต้แย้งนี้ Helper ทำงานโดยการอ่าน control channel ในรูปแบบข้อความ หากคุณเข้ารหัสช่องทางนั้น Helper จะเห็นเพียง ciphertext จึงไม่สามารถค้นหาพอร์ตได้ และไม่มีวิธีแก้ไขสำหรับเรื่องนี้ ซึ่งก็ควรเป็นเช่นนั้น เพราะ middlebox ที่สามารถอ่าน control channel ของคุณได้ ก็คือ middlebox ที่สามารถอ่านรหัสผ่านของคุณได้เช่นกัน

การกำหนดช่วงพอร์ตแบบ passive บนเซิร์ฟเวอร์

คุณสามารถกำหนดให้ FTP server เลือกใช้พอร์ตแบบ passive จากช่วงที่คุณระบุได้ ชื่อตัวเลือกจะแตกต่างกันไปตามซอฟต์แวร์ ดังนั้นโปรดตรวจสอบเอกสารประกอบของเซิร์ฟเวอร์ที่คุณใช้งานจริง

สำหรับ vsftpd ใน /etc/vsftpd.conf:

pasv_enable=YES
pasv_min_port=30000
pasv_max_port=30099

pasv_enable มีค่าเริ่มต้นเป็น YES อยู่แล้ว ตัวเลือกพอร์ตทั้งสองมีค่าเริ่มต้นเป็น 0 ซึ่งเป็นพฤติกรรม "ใช้พอร์ตใดก็ได้" ตามที่อธิบายไว้ข้างต้น ให้ใช้คำสั่ง sudo systemctl restart vsftpd เพื่อนำการตั้งค่าไปใช้ จากนั้นตรวจสอบว่าบริการกลับมาทำงานปกติด้วย systemctl status vsftpd โดย vsftpd จะปฏิเสธการเริ่มทำงานหากพบการตั้งค่าที่ไม่สามารถอ่านได้ แทนที่จะเพิกเฉย ดังนั้นหากการรีสตาร์ทล้มเหลว ให้ตรวจสอบ journalctl -u vsftpd -n 20 เพื่อดูบรรทัด 500 OOPS: ที่ระบุตัวเลือกที่คุณเพิ่งพิมพ์ลงไป

สำหรับ ProFTPD ใน proftpd.conf:

PassivePorts 30000 30099

ProFTPD ไม่ได้ระบุค่าเริ่มต้นไว้ในส่วนนี้ หากไม่มีคำสั่งดังกล่าว kernel จะเป็นผู้เลือกพอร์ตให้เอง เอกสารประกอบระบุว่าเมื่อไม่มีพอร์ตว่างในช่วงที่คุณกำหนด เซิร์ฟเวอร์จะกลับไปใช้พอร์ตที่ kernel จัดสรรให้และบันทึกข้อความลงใน log ดังนั้นช่วงพอร์ตที่แคบเกินไปจะทำให้เกิดความล้มเหลวเป็นครั้งคราวแทนที่จะล้มเหลวอย่างชัดเจน ซึ่งจะวินิจฉัยปัญหาได้ยากกว่ามาก ควรใช้พอร์ตที่ไม่มีสิทธิ์พิเศษ (non-privileged ports) ตั้งแต่ 1024 ขึ้นไป

Pure-FTPd ใช้ flag คือ -p first:last ซึ่งระบุไว้ใน man pure-ftpd ว่า "ใช้เฉพาะพอร์ตในช่วง first ถึง last เท่านั้นสำหรับการดาวน์โหลดในโหมด passive" ซึ่ง "ทำให้ pure-ftpd ทำงานร่วมกับ packet filter ได้ดียิ่งขึ้น" โดยทั่วไปแล้วแพ็กเกจที่ติดตั้งมาพร้อมกับระบบมักจะรวม flag นี้ไว้ในไฟล์ config ดังนั้นให้ตรวจสอบเอกสารของ distribution ที่คุณใช้เพื่อหาชื่อไฟล์ แทนการคาดเดา

คุณต้องใช้พอร์ตจำนวนเท่าใด? คำตอบคือหนึ่งพอร์ตต่อหนึ่งการเชื่อมต่อข้อมูลที่กำลังทำงานอยู่ พอร์ต TCP ที่ปิดไปแล้วจะยังคงอยู่ในสถานะ TIME_WAIT เป็นเวลาสองสามนาทีก่อนที่จะนำกลับมาใช้ใหม่ได้ ดังนั้นควรเผื่อจำนวนพอร์ตไว้เป็นหลายเท่าของปริมาณการใช้งานสูงสุดที่คาดการณ์ไว้ การใช้พอร์ตจำนวน 100 พอร์ตถือว่าเพียงพอสำหรับผู้ใช้จำนวนไม่มาก แต่สำหรับเซิร์ฟเวอร์สาธารณะที่มีการใช้งานสูงจำเป็นต้องใช้จำนวนมากกว่านั้นมาก

ช่วงพอร์ตควรอยู่ที่ใด? ให้รันคำสั่ง sysctl net.ipv4.ip_local_port_range ก่อน บน Ubuntu ค่าเริ่มต้นจะเป็น 32768 60999 ซึ่งเป็นช่วงพอร์ตที่ kernel จัดสรรให้สำหรับการเชื่อมต่อขาออก หากช่วงพอร์ตแบบ passive อยู่ภายในช่วงนี้ อาจเกิดการชนกันกับการเชื่อมต่อขาออกที่กำลังใช้งานพอร์ตนั้นอยู่ ดังนั้นควรเลือกช่วงพอร์ตที่ต่ำกว่าช่วงดังกล่าว ช่วง 30000 ถึง 30099 ถือว่าปลอดภัยสำหรับระบบมาตรฐานทั่วไป แต่ควรตรวจสอบระบบของคุณเองแทนที่จะเชื่อตัวเลขนี้เพียงอย่างเดียว

เปิดช่วงพอร์ตเดียวกันในไฟร์วอลล์

ufw เขียนช่วงพอร์ตโดยใช้เครื่องหมายทวิภาค (colon) และคู่มือระบุว่าช่วงพอร์ตหรือรายการ "อาจใช้เพื่อระบุพอร์ตหลายพอร์ตได้เช่นกัน ในกรณีนี้จำเป็นต้องระบุโปรโตคอลด้วย":

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

ufw status verbose ควรแสดงรายการทั้งสองรายการในขณะนี้ คำสั่งเดียวกันหากไม่มี /tcp จะถูกปฏิเสธพร้อมข้อความแจ้งเตือนให้ระบุ tcp หรือ udp เนื่องจาก ufw จะไม่คาดเดาเอง ไวยากรณ์กฎของ ufw บน VPS ครอบคลุมรายละเอียดส่วนที่เหลือ

firewalld เขียนช่วงพอร์ตโดยใช้เครื่องหมายยัติภังค์ (hyphen) และจำเป็นต้องโหลดการตั้งค่าใหม่:

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 เปิดพอร์ต 21/tcp และร้องขอ ftp helper ซึ่งนิยามบริการที่มาพร้อมกับระบบได้ระบุไว้ แต่มันไม่ได้เปิดช่วงพอร์ต passive ของคุณ ดังนั้นหากใช้เพียงคำสั่งนี้ คุณจะยังคงอยู่ในสถานะเดิม โซนและบริการของ firewalld บน VPS มีภาพรวมที่กว้างกว่านี้

สำหรับ nftables โดยตรง ภายใน input chain ของคุณ:

tcp dport { 21, 30000-30099 } accept

ยังมีไฟร์วอลล์อีกหนึ่งจุดที่ต้องจำไว้: ผู้ให้บริการส่วนใหญ่มีไฟร์วอลล์เครือข่ายในแผงควบคุม (control panel) ซึ่งอยู่นอกเหนือระบบปฏิบัติการ หากกฎบนเซิร์ฟเวอร์ดูถูกต้องแล้วแต่แพ็กเก็ตยังคงมาไม่ถึง ให้เปิดช่วงพอร์ตเดียวกันในนั้นด้วย

แจ้งที่อยู่สาธารณะให้เซิร์ฟเวอร์ทราบเมื่ออยู่หลัง NAT

ให้รัน ip -4 addr show บนเซิร์ฟเวอร์ หากที่อยู่บนอินเทอร์เฟซเป็นที่อยู่ที่ไคลเอนต์ใช้เชื่อมต่อ ให้ข้ามส่วนนี้ไป แต่หากอินเทอร์เฟซถือที่อยู่ส่วนตัว (10.x, 172.16 ถึง 172.31.x, 192.168.x) และแพลตฟอร์มแมปที่อยู่สาธารณะเข้ามา เซิร์ฟเวอร์จะไม่ทราบที่อยู่สาธารณะของตนเอง vsftpd ระบุค่าเริ่มต้นสำหรับ pasv_address ไว้ว่า "ที่อยู่จะถูกดึงมาจากซ็อกเก็ตที่เชื่อมต่อเข้ามา" ดังนั้นการตอบกลับของ 227 จึงส่งที่อยู่ส่วนตัวไป และไคลเอนต์จะถูกส่งไปยังจุดที่เข้าถึงไม่ได้

FileZilla ระบุชื่อค่านี้ไว้อย่างชัดเจน:

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

FileZilla แก้ไขปัญหานี้และทำงานต่อได้ แต่ไคลเอนต์อื่นอีกหลายตัวทำไม่ได้ พวกมันจะหมุน 10.0.0.5 แล้วค้างไป

curl ก็ซ่อนปัญหานี้ไว้เช่นกัน ซึ่งสำคัญหากคุณใช้ curl ในการทดสอบ คู่มือระบุว่า --ftp-skip-pasv-ip "ถูกเปิดใช้งานโดยค่าเริ่มต้น (เพิ่มเข้ามาในเวอร์ชัน 7.74.0)" ดังนั้น curl จึงเพิกเฉยต่อที่อยู่ในคำตอบของ 227 และใช้ที่อยู่เดียวกับการเชื่อมต่อควบคุม (control connection) การถ่ายโอนข้อมูลที่ใช้งานได้ด้วย curl อาจยังคงล้มเหลวในไคลเอนต์แบบกราฟิกด้วยเหตุผลนี้

ให้กำหนดที่อยู่ไว้อย่างชัดเจน vsftpd ใช้ pasv_address=203.0.113.10 ร่วมกับ pasv_addr_resolve=YES (ค่าเริ่มต้นคือ NO) หากคุณต้องการระบุเป็นชื่อโฮสต์ ProFTPD ใช้ MasqueradeAddress ซึ่งรองรับทั้งที่อยู่ IP, ชื่อ DNS หรือชื่ออินเทอร์เฟซ Pure-FTPd ใช้ -P ซึ่งระบุไว้สำหรับกรณีที่ "เซิร์ฟเวอร์อยู่หลังอุปกรณ์ทำ masquerading (NAT)" EPSV ช่วยหลีกเลี่ยงปัญหานี้ได้ทั้งหมดเนื่องจากการตอบกลับไม่มีฟิลด์ที่อยู่ แต่คุณไม่สามารถพึ่งพาวิธีนี้ได้เสมอไปเนื่องจากไคลเอนต์เป็นผู้ตัดสินใจว่าจะส่งคำสั่งใด

การเปลี่ยนแปลงของ TLS

FTPS คือ FTP ที่ทำงานผ่าน TLS (Transport Layer Security) ไคลเอนต์จะเชื่อมต่อผ่านพอร์ต 21 ตามปกติ จากนั้นส่งคำสั่ง AUTH TLS เพื่อรักษาความปลอดภัยให้กับช่องทางควบคุม (control channel) และส่งคำสั่ง PROT P เพื่อเข้ารหัสช่องทางข้อมูล (data channel) ด้วยเช่นกัน FTP แบบปกติจะส่งรหัสผ่านผ่านเครือข่ายในรูปแบบข้อความที่อ่านได้ ดังนั้นหากจำเป็นต้องใช้งาน FTP ให้ใช้ FTPS แทน ซอฟต์แวร์ vsftpd มาพร้อมกับค่า ssl_enable ซึ่งโดยค่าเริ่มต้นจะถูกตั้งไว้ที่ NO

ผลที่ตามมามีสองประการ ประการแรกคือตัวช่วยติดตามการเชื่อมต่อ (connection tracking helper) จะไม่สามารถทำงานได้ ซึ่งเป็นมุมมองอีกด้านของประเด็นข้างต้น ประการที่สองคือ sudo tcpdump -nAi any 'tcp port 21' จะไม่แสดงการตอบกลับ 227 ให้คุณเห็นอีกต่อไป ดังนั้นเมื่อคุณต้องการทราบว่าเซิร์ฟเวอร์ประกาศที่อยู่และพอร์ตใดออกมา ให้ตรวจสอบจาก log ของเซิร์ฟเวอร์โดยตรงแทนการดักจับข้อมูลบนเครือข่าย

การทดสอบการเปลี่ยนแปลงจากภายนอก

ให้รันคำสั่งเหล่านี้จากเครื่องอื่น การทดสอบจากตัวเซิร์ฟเวอร์เองจะข้ามขั้นตอนการตรวจสอบ firewall ที่คุณกำลังพยายามแก้ไขอยู่

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

ss ควรแสดงให้เห็นว่า FTP daemon กำลังรอรับการเชื่อมต่ออยู่ที่พอร์ต 21 ในขณะที่เซิร์ฟเวอร์ไม่ได้ใช้งาน จะไม่มีการเปิดพอร์ตใดๆ ในช่วง passive range เนื่องจาก socket เหล่านั้นจะถูกสร้างขึ้นเพื่อการรับส่งข้อมูลและปิดตัวลงหลังจากเสร็จสิ้นเท่านั้น

--disable-epsv จะบังคับให้ curl ทำงานผ่านเส้นทาง PASV ซึ่งเป็นจุดที่เผยให้เห็นปัญหาเรื่องที่อยู่ IP ผลลัพธ์ที่ได้จะแสดงการตอบกลับจากเซิร์ฟเวอร์ ตามด้วยที่อยู่และพอร์ตที่ curl พยายามเชื่อมต่อ:

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

117 * 256 + 52 = 30004 ซึ่งอยู่ในช่วงที่กำหนดไว้ หากบรรทัดนั้นแสดงที่อยู่ IP ภายใน แสดงว่า pasv_address ยังไม่ได้ถูกตั้งค่า หากพอร์ตที่แสดงอยู่นอกเหนือช่วงที่คุณกำหนด แสดงว่าเซิร์ฟเวอร์ไม่ได้อ่านการตั้งค่าที่แก้ไขไป ให้ตรวจสอบว่าคุณได้แก้ไขไฟล์ที่ service กำลังใช้งานอยู่จริงหรือไม่

nc สามารถตอบคำถามเรื่อง firewall ได้ด้วยตัวเอง หากได้รับผลลัพธ์เป็น Connection refused ทันที แสดงว่าแพ็กเก็ตส่งไปถึงเซิร์ฟเวอร์แล้วแต่ไม่พบการเปิดพอร์ตไว้ ซึ่งเป็นผลลัพธ์ที่ถูกต้องสำหรับพอร์ต passive ในขณะที่ไม่มีการรับส่งข้อมูล หมายความว่ากฎ firewall ของคุณทำงานได้ปกติ หากการเชื่อมต่อค้างจนกระทั่ง nc ยอมแพ้ แสดงว่ามีบางอย่างดรอปแพ็กเก็ตทิ้งโดยไม่แจ้งเตือน ซึ่งอาจเป็น firewall บนตัวเครื่องเองหรือ firewall ในแผงควบคุมของผู้ให้บริการ ความแตกต่างนี้เป็นแบบเดียวกับที่อธิบายไว้ใน การปฏิเสธการเชื่อมต่อเทียบกับการหมดเวลาสำหรับ SSH และสามารถนำไปใช้ได้กับทุกพอร์ต

คุณยังควรใช้งาน FTP อยู่หรือไม่?

สำหรับงานใหม่ คำตอบคือไม่ SFTP (SSH file transfer protocol) ทำงานอยู่ภายใน SSH connection เดียวบนพอร์ต 22 โดยไม่มีช่องทางที่สอง ไม่ต้องกำหนดช่วงพอร์ต passive ไม่ต้องตั้งค่า NAT และไม่ต้องดูแล daemon เพิ่มเติม เพราะ OpenSSH มีฟังก์ชันนี้ให้ใช้งานอยู่แล้ว sftp user@example.com สามารถใช้งานได้บนเครื่องที่คุณไม่ได้ตั้งค่าบริการถ่ายโอนไฟล์ใดๆ ไว้เลย หากต้องการให้ผู้อื่นเข้าถึงไฟล์ได้เพียงอย่างเดียว sshd_config จะใช้ ForceCommand internal-sftp ร่วมกับ ChrootDirectory โดยไดเรกทอรีดังกล่าวจะต้องมี root เป็นเจ้าของและผู้ใช้ต้องไม่มีสิทธิ์เขียนไฟล์ มิฉะนั้น sshd จะปฏิเสธการเชื่อมต่อและบันทึกบรรทัด bad ownership or modes for chroot directory ลงใน log

FTP ยังคงมีความจำเป็นในกรณีที่ฝั่งตรงข้ามไม่สามารถเปลี่ยนแปลงได้ เครื่องสแกนและเครื่องพิมพ์มัลติฟังก์ชันมักมาพร้อมกับเฟิร์มแวร์ที่รองรับเฉพาะ FTP เท่านั้น อุปกรณ์ในห้องปฏิบัติการและอุปกรณ์อุตสาหกรรมมักรันอิมเมจแบบคงที่ซึ่งไม่มีใครรับรองการเปลี่ยนแปลงได้ คู่ค้าทางธุรกิจอาจเปิดช่องทาง FTPS ไว้และไม่ยอมเพิ่มโปรโตคอลอื่นให้เพียงเพื่อซัพพลายเออร์รายเดียว ในกรณีเหล่านี้ การกำหนดช่วงพอร์ต passive พร้อมกับกฎ firewall ที่สอดคล้องกันคือทั้งหมดที่ต้องทำ และควรเลือกใช้ FTPS แทน FTP แบบปกติ การออกแบบที่ใช้สองช่องทางเป็นแนวคิดตั้งแต่ปี 1985 ซึ่งปัจจุบันต้องทำงานในสภาพแวดล้อมที่ไม่ได้คาดการณ์ไว้แต่แรก เรื่องราวนี้ถูกกล่าวถึงไว้ใน ประวัติของโปรโตคอลการถ่ายโอนไฟล์

FAQ

ทำไม FTP ล็อกอินได้แต่แสดงรายการไฟล์ค้าง?

การล็อกอินใช้เพียง control connection บนพอร์ต 21 ซึ่ง firewall ของคุณอนุญาตอยู่แล้ว แต่การแสดงรายการไฟล์ต้องใช้ TCP connection ที่สองบนพอร์ตอื่น ซึ่งการเชื่อมต่อดังกล่าวถูกบล็อกไว้ ให้กำหนดช่วงพอร์ต passive บน FTP server และเปิดพอร์ตช่วงเดียวกันใน firewall แล้วรายการไฟล์จะแสดงผลได้ตามปกติ อาการค้างขณะแสดงรายการไฟล์เป็นปัญหาที่ช่องทางรับส่งข้อมูล (data channel) ไม่ใช่ปัญหาเรื่องรหัสผ่าน

ต้องเปิดพอร์ตใดบ้างสำหรับ FTP passive mode?

พอร์ต 21 สำหรับ control channel และช่วงพอร์ตที่คุณกำหนดไว้สำหรับ passive data connection ไม่มีช่วงพอร์ตมาตรฐานเพราะคุณเป็นผู้เลือกเอง ช่วงพอร์ตอย่าง 30000 ถึง 30099 สามารถใช้งานได้ โดยให้ปรับขนาดตามจำนวนการโอนถ่ายข้อมูลพร้อมกันสูงสุดของคุณ และหลีกเลี่ยงช่วงพอร์ตขาออกของ kernel ซึ่งคุณสามารถตรวจสอบได้ด้วย sysctl net.ipv4.ip_local_port_range หากผู้ให้บริการของคุณมี network firewall ในแผงควบคุม ให้เปิดช่วงพอร์ตเดียวกันนั้นที่นั่นด้วย

ยังจำเป็นต้องใช้ nf_conntrack_ftp หรือไม่?

ไม่จำเป็น และใน kernel ปัจจุบันคุณไม่สามารถพึ่งพามันได้ การกำหนด helper อัตโนมัติถูกปิดใช้งานโดยค่าเริ่มต้น และใน kernel รุ่นใหม่สวิตช์ net.netfilter.nf_conntrack_helper ถูกถอดออกไปแล้ว ทำให้ sysctl แจ้งว่าไม่พบไฟล์ดังกล่าว คู่มือของ ufw ระบุว่าการโหลดโมดูลเหล่านั้นโดยไม่มีเงื่อนไขถือเป็นวิธีที่ล้าสมัย (deprecated) และ firewalld ก็เพิกเฉยต่อ AutomaticHelpers โดยสิ้นเชิง นอกจากนี้ helper ยังต้องอ่าน control channel ที่เป็นข้อความธรรมดา ดังนั้นมันจะหยุดทำงานทันทีที่คุณเปิดใช้งาน FTPS ให้กำหนดช่วงพอร์ต passive แทนจะดีกว่า

ทำไม FTP client ของฉันถึงแจ้งว่า passive reply มีที่อยู่ที่ไม่สามารถระบุเส้นทางได้ (unroutable address)?

เซิร์ฟเวอร์ตอบกลับด้วย PASV โดยใช้ที่อยู่ IP ที่เห็นบนอินเทอร์เฟซของตัวเอง ซึ่งที่อยู่นั้นเป็น IP ภายใน (private) ปัญหานี้เกิดขึ้นเมื่อแพลตฟอร์มแมปที่อยู่สาธารณะเข้ากับที่อยู่ภายใน ให้กำหนดที่อยู่สาธารณะอย่างชัดเจน: pasv_address ใน vsftpd, MasqueradeAddress ใน ProFTPD หรือ -P ใน Pure-FTPd โปรแกรม FileZilla แก้ไขปัญหานี้โดยการนำที่อยู่ที่เชื่อมต่ออยู่แล้วมาใช้ซ้ำ และบันทึก log ว่า "Using server address instead" ซึ่งเป็นเหตุผลว่าทำไม client บางตัวถึงใช้งานได้แม้ตั้งค่าผิดพลาด ในขณะที่ตัวอื่นกลับค้าง

ควรใช้ FTPS หรือ SFTP?

ใช้ SFTP สำหรับทุกอย่างที่คุณควบคุมได้ทั้งสองฝั่ง เพราะใช้การเชื่อมต่อเดียวผ่าน SSH บนพอร์ต 22 ไม่ต้องเปิดช่องทางรับส่งข้อมูลเพิ่ม และมันทำงานอยู่แล้ว ส่วน FTPS คือ FTP ที่ครอบด้วย TLS จึงยังคงการออกแบบสองช่องทางและปัญหา firewall ทุกอย่างที่ตามมา ให้เลือกใช้ FTPS ก็ต่อเมื่ออีกฝั่งรองรับเฉพาะโปรโตคอลนี้เท่านั้น ห้ามใช้ FTP แบบธรรมดาผ่านอินเทอร์เน็ต เพราะรหัสผ่านจะถูกส่งผ่านเครือข่ายในรูปแบบข้อความที่อ่านได้ทันที