有到主機(jī)的路由分層定位)
FTP 這個(gè)東西說(shuō)起來(lái)老但真到要用的場(chǎng)合一點(diǎn)不含糊——老系統(tǒng)的對(duì)賬文件、設(shè)備日志回傳、第三方接口的批量數(shù)據(jù)交換很多還壓在它身上。而一旦連不上報(bào)出來(lái)的那兩句話(huà)往往只有兩種形態(tài)ftp connect: 拒絕連接和ftp connect: 沒(méi)有到主機(jī)的路由。前者是 TCP 層被人當(dāng)面關(guān)上了門(mén)后者是網(wǎng)絡(luò)層壓根沒(méi)找到門(mén)在哪。這兩種報(bào)錯(cuò)看起來(lái)都像“連不上”但排查路徑幾乎是兩條不同的路混著查只會(huì)把自己繞進(jìn)去。這篇內(nèi)容面向的是正在被 FTP 連接問(wèn)題卡住的人不管是剛接手運(yùn)維的新手還是寫(xiě)了幾年腳本、被客戶(hù)一句“你們 FTP 連不上”懟回來(lái)的開(kāi)發(fā)。下面我會(huì)把這兩類(lèi)報(bào)錯(cuò)的底層成因拆開(kāi)講清楚再給出一套可以照著敲命令的分層排查流程最后附上被動(dòng)模式、防火墻、云上安全組這些實(shí)際最容易翻車(chē)的環(huán)節(jié)。文中涉及的系統(tǒng)和配置均按通用發(fā)行版描述具體路徑以你自己環(huán)境為準(zhǔn)。1. 先搞清楚兩種報(bào)錯(cuò)到底差在哪很多人排查 FTP 的第一步是打開(kāi)客戶(hù)端反復(fù)重連看運(yùn)氣。這個(gè)動(dòng)作除了消耗耐心沒(méi)有別的產(chǎn)出。真正該做的第一件事是把這兩句報(bào)錯(cuò)翻譯成網(wǎng)絡(luò)分層里的具體位置。它們對(duì)應(yīng)的不是同一個(gè)層也不是同一類(lèi)故障。1.1 拒絕連接TCP 層收到的那個(gè) RST 包拒絕連接Connection refused在協(xié)議層面的含義非常明確客戶(hù)端發(fā)出了 SYN對(duì)端回了一個(gè) RST。這說(shuō)明什么說(shuō)明數(shù)據(jù)包已經(jīng)成功到達(dá)了目標(biāo)主機(jī)路由是通的目標(biāo)主機(jī)的內(nèi)核也正常處理了這個(gè)包只是內(nèi)核告訴客戶(hù)端“這個(gè)端口上沒(méi)有任何進(jìn)程在等你的連接”于是回了拒絕。這條鏈路里路由器、交換機(jī)、中間網(wǎng)絡(luò)設(shè)備全都是好的問(wèn)題百分之百出在目標(biāo)主機(jī)這一側(cè)的應(yīng)用層或者主機(jī)防火墻層。你可以把它理解成地址找對(duì)了樓也進(jìn)去了前臺(tái)告訴你“這個(gè)房間沒(méi)人”。具體到 FTP21 端口被拒絕最常見(jiàn)的幾個(gè)原因按出現(xiàn)頻率排大概是FTP 服務(wù)進(jìn)程根本沒(méi)起來(lái)或者起來(lái)之后崩了服務(wù)起來(lái)了但只監(jiān)聽(tīng)了127.0.0.1外部從公網(wǎng)或內(nèi)網(wǎng)其它機(jī)器連進(jìn)來(lái)就等不到主機(jī)防火墻用了REJECT --reject-with tcp-reset這類(lèi)策略主動(dòng)回 RST端口寫(xiě)錯(cuò)了客戶(hù)端連 21服務(wù)端改成了 2121或者中間做了端口映射但映射規(guī)則沒(méi)生效端口被別的進(jìn)程占了FTP 啟動(dòng)失敗表面上像是沒(méi)開(kāi)。注意最后一條特別有迷惑性。systemctl status顯示 active但實(shí)際綁定失敗了這種情況在配置改過(guò)端口、或者機(jī)器上跑了兩套同類(lèi)服務(wù)時(shí)很常見(jiàn)。1.2 沒(méi)有到主機(jī)的路由網(wǎng)絡(luò)層就已經(jīng)斷了沒(méi)有到主機(jī)的路由No route to host是另一回事。它通常意味著客戶(hù)端的協(xié)議棧在發(fā)包階段就沒(méi)有找到可達(dá)路徑或者是中途收到了 ICMP 的不可達(dá)消息。跟“拒絕連接”比起來(lái)它更靠下、更靠前。典型場(chǎng)景有這么幾類(lèi)。第一類(lèi)是客戶(hù)端本機(jī)的路由表里沒(méi)有到目標(biāo)網(wǎng)段的條目也沒(méi)有默認(rèn)網(wǎng)關(guān)包發(fā)不出去。第二類(lèi)是目標(biāo)主機(jī)不在同一個(gè)二層網(wǎng)絡(luò)中間網(wǎng)關(guān)不認(rèn)識(shí)這個(gè)網(wǎng)段。第三類(lèi)是防火墻策略把包丟了并且回了一個(gè) ICMP host prohibited客戶(hù)端就會(huì)把它解釋成“沒(méi)有到主機(jī)的路由”。第四類(lèi)是 ARP 解析失敗同網(wǎng)段內(nèi)目標(biāo)主機(jī)沒(méi)響應(yīng) ARP 請(qǐng)求內(nèi)核報(bào) host unreachable。這里有個(gè)很關(guān)鍵的認(rèn)知點(diǎn)沒(méi)有到主機(jī)的路由不一定真的是路由問(wèn)題。如果防火墻用REJECT --reject-with icmp-host-prohibited客戶(hù)端收到的就是 ICMP 類(lèi)型 3 代碼 10表現(xiàn)和真的路由不通幾乎一模一樣。我在實(shí)際排查里遇到過(guò)好幾次最后發(fā)現(xiàn)是目標(biāo)機(jī)上的防火墻規(guī)則順序?qū)懛戳税言试S規(guī)則放在了拒絕規(guī)則后面。而如果是DROP策略客戶(hù)端那邊通常表現(xiàn)為連接一直掛著最后超時(shí)而不是立刻報(bào)錯(cuò)。所以“立刻報(bào)錯(cuò)”和“轉(zhuǎn)圈到超時(shí)”是區(qū)分 REJECT 和 DROP 的一個(gè)很實(shí)用的手感。1.3 一張表分清兩類(lèi)故障的邊界對(duì)比項(xiàng)拒絕連接沒(méi)有到主機(jī)的路由報(bào)錯(cuò)層級(jí)傳輸層TCP網(wǎng)絡(luò)層IP/ICMP報(bào)文特征收到 RST收到 ICMP 不可達(dá)或本機(jī)路由失敗目標(biāo)主機(jī)是否可達(dá)可達(dá)內(nèi)核正常響應(yīng)可能不可達(dá)也可能被策略偽裝成不可達(dá)服務(wù)是否在監(jiān)聽(tīng)沒(méi)監(jiān)聽(tīng)或監(jiān)聽(tīng)地址不對(duì)與服務(wù)監(jiān)聽(tīng)狀態(tài)基本無(wú)關(guān)常見(jiàn)原因服務(wù)未啟動(dòng)、綁 127.0.0.1、防火墻 REJECT路由缺失、網(wǎng)關(guān)錯(cuò)誤、防火墻 ICMP REJECT、網(wǎng)段隔離排查起點(diǎn)目標(biāo)機(jī)ss -lntp客戶(hù)端ip route與ping把這張表記在心里后面所有動(dòng)作都是往下鉆。拿反了方向就會(huì)出現(xiàn)“明明防火墻全放開(kāi)了還是連不上”這種僵局因?yàn)閱?wèn)題根本不在防火墻的策略?xún)?nèi)容上而在包有沒(méi)有到。2. 排查前的準(zhǔn)備把“分層”這件事落到命令行上分層排查不是一句口號(hào)它得變成一條固定順序的命令清單。順序?qū)α艘淮尉湍馨逊秶车粢话腠樞騺y了改了一堆配置問(wèn)題還在原地。2.1 排查順序?yàn)槭裁幢仨毷亲韵露霞偃?FTP 連不上我先看服務(wù)狀態(tài)還是先 ping答案必須是先 ping。理由很直白如果網(wǎng)絡(luò)層都不通你去看/etc/vsftpd/vsftpd.conf里的每一個(gè)參數(shù)都是白費(fèi)。反過(guò)來(lái)如果 ping 得通、端口探測(cè)被拒那再去查服務(wù)配置才有意義。我固定的排查順序是四步一步不跳鏈路與地址確認(rèn)本機(jī)網(wǎng)卡有沒(méi)有地址、掩碼對(duì)不對(duì)、網(wǎng)關(guān)通不通網(wǎng)絡(luò)可達(dá)ping目標(biāo) IP看是超時(shí)、不可達(dá)還是通端口可達(dá)telnet或nc探 21 端口看是拒絕還是超時(shí)應(yīng)用狀態(tài)服務(wù)進(jìn)程、監(jiān)聽(tīng)地址、配置文件、日志。這四步跑完故障基本已經(jīng)被鎖死在某一層了。下面每一步我會(huì)展開(kāi)說(shuō)但順序千萬(wàn)別換來(lái)?yè)Q去。2.2 手上要備好的幾把工具工欲善其事工具先裝齊?;A(chǔ)鏡像里常常缺telnet和nc很多人到現(xiàn)場(chǎng)才發(fā)現(xiàn)敲不出來(lái)。ping基礎(chǔ)連通性幾乎所有系統(tǒng)自帶ip route/route -n看路由表和默認(rèn)網(wǎng)關(guān)telnet或nc -vz探測(cè)端口判斷拒絕還是超時(shí)traceroute或mtr看路徑在哪一跳斷掉ss -lntp看本機(jī)監(jiān)聽(tīng)端口和對(duì)應(yīng)進(jìn)程tcpdump抓包看 SYN 有沒(méi)有發(fā)出去、有沒(méi)有回包這是終極大招firewall-cmd --list-all/iptables -L -n -v看防火墻實(shí)際生效規(guī)則。提示在某些精簡(jiǎn)系統(tǒng)上telnet客戶(hù)端和nc默認(rèn)都沒(méi)裝。提前準(zhǔn)備好比到了現(xiàn)場(chǎng)臨時(shí)找包要省太多時(shí)間。2.3 記錄基線(xiàn)信息別一上來(lái)就改配置我踩過(guò)最虧的一次坑是接手一臺(tái)別人配過(guò)的機(jī)器一上來(lái)就懷疑防火墻結(jié)果把本來(lái)正確的規(guī)則改壞了最后發(fā)現(xiàn)只是 FTP 配置里listenNO。從那以后我給自己定了個(gè)規(guī)矩動(dòng)手之前先把現(xiàn)場(chǎng)信息原樣記錄一份。至少要記下這些目標(biāo)機(jī)的ip addr、ip route、ss -lntp | grep 21、防火墻規(guī)則全量輸出、FTP 服務(wù)狀態(tài)與最近 50 行日志??蛻?hù)端這邊記下ip route和ping結(jié)果。這些信息除了幫你定位還有一個(gè)作用——出問(wèn)題時(shí)能快速回滾不至于越修越亂。2.4 用 tcpdump 看清 SYN 的去向這一步很多人覺(jué)得高級(jí)其實(shí)它是最樸素的判斷手段。在服務(wù)端執(zhí)行抓包同時(shí)在客戶(hù)端發(fā)起連接# 服務(wù)端抓 21 端口的包 tcpdump -i any -nn port 21 -c 20如果抓到了客戶(hù)端來(lái)的 SYN但服務(wù)端沒(méi)回 SYN-ACK那就是本機(jī)沒(méi)監(jiān)聽(tīng)或者防火墻攔了如果回了 RST那就是端口沒(méi)人等對(duì)應(yīng)“拒絕連接”如果壓根沒(méi)抓到 SYN問(wèn)題在中間網(wǎng)絡(luò)或者路由對(duì)應(yīng)“沒(méi)有到主機(jī)的路由”這一類(lèi)。一次抓包能省掉半小時(shí)的猜測(cè)。3. 拒絕連接Connection refused的逐層拆解與修復(fù)確定是“拒絕連接”之后排查范圍就縮小到目標(biāo)機(jī)內(nèi)部了。下面這四步我基本按順序來(lái)絕大多數(shù)情況在第二步就能定位。3.1 第一步確認(rèn)服務(wù)到底起沒(méi)起先在目標(biāo)機(jī)上執(zhí)行systemctl status vsftpd看兩個(gè)東西Active那一行是active (running)還是failed以及下面日志里有沒(méi)有Failed to start之類(lèi)的內(nèi)容。如果狀態(tài)是 failed直接看日志journalctl -u vsftpd -n 80 --no-pager日志里常見(jiàn)的是配置文件語(yǔ)法錯(cuò)誤、端口被占用、權(quán)限不足。比如500 OOPS: could not bind listening IPv4 socket基本就是端口被占了或者配置沖突。如果狀態(tài)是 running也別高興太早。繼續(xù)看監(jiān)聽(tīng)ss -lntp | grep -E :(21|ftp)正常應(yīng)該看到類(lèi)似LISTEN 0 32 0.0.0.0:21 0.0.0.0:* users:((vsftpd,pid1234,fd3))的輸出。如果這里是127.0.0.1:21那就是下一個(gè)問(wèn)題了。3.2 第二步確認(rèn)監(jiān)聽(tīng)地址綁對(duì)沒(méi)綁對(duì)ss輸出里那個(gè)0.0.0.0:21和127.0.0.1:21的區(qū)別決定了外部能不能連進(jìn)來(lái)。0.0.0.0表示監(jiān)聽(tīng)所有 IPv4 地址127.0.0.1表示只聽(tīng)本機(jī)回環(huán)。造成只聽(tīng)回環(huán)的原因在 vsftpd 上通常是這兩行配置listenYES listen_address127.0.0.1listen_address一旦被指定成回環(huán)地址外部連接必然被拒。改法是把這一行注釋掉或者明確寫(xiě)成機(jī)器的業(yè)務(wù) IPlistenYES listen_ipv6NO # listen_address127.0.0.1改完記得systemctl restart vsftpd然后再用ss確認(rèn)一次。這里有個(gè)細(xì)節(jié)listenYES和listen_ipv6YES不能同時(shí)打開(kāi)否則 vsftpd 啟動(dòng)會(huì)直接報(bào)錯(cuò)退出而systemctl status有時(shí)候顯示得很含糊容易誤判成服務(wù)正常。3.3 第三步防火墻是 REJECT 還是 DROP監(jiān)聽(tīng)沒(méi)問(wèn)題端口也對(duì)外了那就要看防火墻。先確認(rèn)防火墻狀態(tài)# firewalld 環(huán)境 firewall-cmd --state firewall-cmd --list-all # iptables 環(huán)境 iptables -L INPUT -n -v --line-numbers判斷的關(guān)鍵在于規(guī)則里用的是REJECT還是DROP。REJECT會(huì)往客戶(hù)端回一個(gè)消息DROP是直接丟棄、不回任何東西。所以客戶(hù)端立刻報(bào)“拒絕連接”多半是REJECT --reject-with tcp-reset客戶(hù)端一直轉(zhuǎn)圈到超時(shí)多半是DROP客戶(hù)端報(bào)“沒(méi)有到主機(jī)的路由”可能是REJECT --reject-with icmp-host-prohibited。放行 21 端口的寫(xiě)法firewalld 環(huán)境下firewall-cmd --permanent --add-serviceftp firewall-cmd --reload或者按端口放行firewall-cmd --permanent --add-port21/tcp firewall-cmd --reloadiptables 環(huán)境下iptables -I INPUT -p tcp --dport 21 -j ACCEPT注意-I是插到鏈?zhǔn)?A是追加到鏈尾。如果原有規(guī)則里有前置的拒絕規(guī)則-A加在后面的允許規(guī)則根本不會(huì)被匹配到。這就是我在第 1 節(jié)提到的“規(guī)則順序?qū)懛础钡牡湫秃蠊?。注意?iptables 規(guī)則前先確認(rèn)有沒(méi)有持久化機(jī)制否則重啟后規(guī)則全丟第二天又是同一個(gè)故障。iptables-save配合netfilter-persistent或iptables-services是常見(jiàn)做法。3.4 第四步云主機(jī)上的安全組與端口映射如果目標(biāo)機(jī)跑在云上主機(jī)內(nèi)部防火墻全放開(kāi)了還是拒絕連接那就要往上找一層。云平臺(tái)通常有獨(dú)立于操作系統(tǒng)的網(wǎng)絡(luò)訪(fǎng)問(wèn)控制一般叫安全組或者網(wǎng)絡(luò) ACL它在實(shí)例外面生效你在機(jī)器里iptables -L是看不到的。排查方式是登錄云控制臺(tái)找到該實(shí)例綁定的安全組看入方向規(guī)則里有沒(méi)有放行 21 端口源地址范圍是否覆蓋了你的客戶(hù)端 IP。這里最常見(jiàn)的兩個(gè)坑一是安全組只放行了內(nèi)網(wǎng)地址而你從公網(wǎng)連二是規(guī)則的優(yōu)先級(jí)被更靠前的拒絕規(guī)則蓋掉了。還有一種情況是中間做了端口映射比如公網(wǎng) 2121 映射到內(nèi)網(wǎng) 21。這時(shí)候客戶(hù)端必須連 2121連 21 自然是被拒。同時(shí)被動(dòng)模式還要額外處理數(shù)據(jù)端口這個(gè)在第 5 節(jié)展開(kāi)。3.5 驗(yàn)證與回歸測(cè)試改完不要只在自己機(jī)器上試一次就收工。我的驗(yàn)證清單是這樣的本機(jī)telnet 127.0.0.1 21確認(rèn)服務(wù)活著同網(wǎng)段另一臺(tái)機(jī)器telnet 內(nèi)網(wǎng)IP 21確認(rèn)內(nèi)網(wǎng)可達(dá)客戶(hù)端所在網(wǎng)絡(luò)telnet 公網(wǎng)IP 21確認(rèn)外部可達(dá)用真實(shí) FTP 客戶(hù)端登錄一次做一次ls和一次put重啟一次服務(wù)再重復(fù)第 4 步確認(rèn)配置持久化沒(méi)問(wèn)題。第 5 步很多人省掉結(jié)果下次機(jī)器重啟故障復(fù)現(xiàn)。配置這東西不重啟驗(yàn)證一遍就等于沒(méi)改。4. 沒(méi)有到主機(jī)的路由No route to host的逐層拆解與修復(fù)這類(lèi)報(bào)錯(cuò)的排查思路和上一節(jié)完全不同。核心是回答一個(gè)問(wèn)題包到底有沒(méi)有離開(kāi)客戶(hù)端以及離開(kāi)之后走到了哪一跳。4.1 先 ping 一下但 ping 不通不代表服務(wù)有問(wèn)題第一步永遠(yuǎn)是ping 目標(biāo)IP。但要注意ping不通有三種可能含義完全不同Destination Host Unreachable本機(jī)或中間設(shè)備明確告訴你到不了路由或 ARP 有問(wèn)題Request timeout包發(fā)出去了沒(méi)有任何回應(yīng)可能是對(duì)方禁 ping也可能是防火墻 DROPName or service not known這是域名解析問(wèn)題壓根還沒(méi)到網(wǎng)絡(luò)層。關(guān)鍵認(rèn)知是服務(wù)器禁 ping 是很常見(jiàn)的加固手段ping 不通不代表 FTP 連不上。所以 ping 只能作為參考不能作為結(jié)論。真正有決定性的還是端口探測(cè)nc -vz -w 3 目標(biāo)IP 21或者telnet 目標(biāo)IP 21看返回是Connection refused、Connection timed out還是No route to host。這三者的含義和排查方向完全不同前面表格里已經(jīng)對(duì)上了。4.2 路由表與網(wǎng)關(guān)檢查確認(rèn)是本機(jī)路由問(wèn)題時(shí)先看路由表ip route輸出里要關(guān)注兩件事有沒(méi)有到目標(biāo)網(wǎng)段的具體路由以及default via后面那個(gè)網(wǎng)關(guān)地址是不是當(dāng)前網(wǎng)段的合法地址。如果默認(rèn)網(wǎng)關(guān)寫(xiě)錯(cuò)了比如網(wǎng)段是192.168.10.0/24網(wǎng)關(guān)卻寫(xiě)成了192.168.20.1那所有跨網(wǎng)段的包都發(fā)不出去。再看本機(jī)地址和掩碼ip addr掩碼寫(xiě)錯(cuò)是很容易被忽略的坑。把/24寫(xiě)成/16本機(jī)會(huì)認(rèn)為整個(gè)192.168.0.0/16都在同一個(gè)二層網(wǎng)絡(luò)里于是對(duì)本來(lái)需要經(jīng)過(guò)網(wǎng)關(guān)的地址直接發(fā) ARP 請(qǐng)求結(jié)果自然是 host unreachable。這種錯(cuò)誤在手工配 IP 的機(jī)器上出現(xiàn)頻率相當(dāng)高。4.3 防火墻 DROP 策略的典型表現(xiàn)前面說(shuō)過(guò)DROP通常表現(xiàn)為超時(shí)而不是“沒(méi)有到主機(jī)的路由”但如果規(guī)則用的是REJECT --reject-with icmp-host-prohibited客戶(hù)端收到的就是 ICMP 類(lèi)型 3 代碼 10報(bào)錯(cuò)文案和真正的路由不通一模一樣。判斷方法是看目標(biāo)機(jī)上的規(guī)則iptables -L INPUT -n -v --line-numbers如果看到類(lèi)似這樣的規(guī)則REJECT all -- 0.0.0.0/0 0.0.0.0/0 reject-with icmp-host-prohibited并且它排在允許 21 端口的規(guī)則前面那故障原因就找到了。處理方式是調(diào)整順序把允許規(guī)則插到它前面iptables -I INPUT 1 -p tcp --dport 21 -j ACCEPT-I INPUT 1里的1表示插到第一條。這個(gè)數(shù)字很關(guān)鍵插錯(cuò)位置等于沒(méi)插。4.4 網(wǎng)段隔離、VLAN 與云上子網(wǎng)如果客戶(hù)端和服務(wù)端不在同一個(gè)二層網(wǎng)絡(luò)中間又有多層網(wǎng)絡(luò)設(shè)備那就要考慮策略隔離了。企業(yè)內(nèi)網(wǎng)常見(jiàn)的做法是把辦公網(wǎng)、服務(wù)器網(wǎng)、測(cè)試網(wǎng)分到不同 VLANVLAN 之間的訪(fǎng)問(wèn)由三層設(shè)備上的 ACL 控制。ACL 默認(rèn)拒絕的時(shí)候表現(xiàn)出來(lái)就是“沒(méi)有到主機(jī)的路由”。云上的情況類(lèi)似但對(duì)象換成了子網(wǎng)和路由表。兩個(gè)子網(wǎng)之間如果沒(méi)有對(duì)等連接或者路由條目包就出不去。這時(shí)候在實(shí)例內(nèi)部怎么改防火墻都沒(méi)用得去網(wǎng)絡(luò)配置層面加路由。排查手段是traceroutetraceroute -n -T -p 21 目標(biāo)IP-T表示用 TCP 探測(cè)-p 21指定端口??摧敵鲈谀囊惶_(kāi)始出現(xiàn)* * *就能大致判斷阻斷點(diǎn)在哪一層設(shè)備上。如果第一跳就是星號(hào)問(wèn)題在本機(jī)或網(wǎng)關(guān)如果中間幾跳正常、最后斷了問(wèn)題在靠近目標(biāo)的一側(cè)。4.5 復(fù)現(xiàn)驗(yàn)證方法改完路由或者 ACL 之后驗(yàn)證要覆蓋兩個(gè)方向。正向驗(yàn)證是從客戶(hù)端 ping 和探端口反向驗(yàn)證是從服務(wù)端 ping 客戶(hù)端同時(shí)看服務(wù)端有沒(méi)有收到 SYN# 服務(wù)端抓包確認(rèn)有 SYN 進(jìn)來(lái) tcpdump -i any -nn tcp port 21 and tcp[tcpflags] tcp-syn ! 0抓到 SYN 說(shuō)明網(wǎng)絡(luò)通了剩下的就是應(yīng)用層的事抓不到說(shuō)明包還在路上被攔著。這個(gè)判定比反復(fù)改配置靠譜得多。5. 連上了卻傳不了文件被動(dòng)模式那些坑端口通了、登錄成功了、ls也出來(lái)了結(jié)果put一個(gè)文件卡死不動(dòng)——這是 FTP 最經(jīng)典的“第二步故障”。它跟前面兩類(lèi)報(bào)錯(cuò)不是一回事但對(duì)使用者的感受是一樣的連不上。5.1 主動(dòng)模式與被動(dòng)模式的本質(zhì)區(qū)別FTP 用兩條連接一條控制連接走 21 端口一條數(shù)據(jù)連接傳文件??刂七B接永遠(yuǎn)是客戶(hù)端主動(dòng)連服務(wù)端這個(gè)沒(méi)問(wèn)題。數(shù)據(jù)連接的方向取決于模式。主動(dòng)模式PORT下服務(wù)端從自己的 20 端口主動(dòng)連客戶(hù)端的一個(gè)隨機(jī)端口。問(wèn)題在于客戶(hù)端通常在 NAT 或者防火墻后面服務(wù)端根本連不進(jìn)去于是卡住。被動(dòng)模式PASV下客戶(hù)端向服務(wù)端要一個(gè)端口服務(wù)端返回一個(gè)隨機(jī)高位端口客戶(hù)端主動(dòng)去連這個(gè)端口。這個(gè)方向?qū)蛻?hù)端友好所以現(xiàn)代客戶(hù)端默認(rèn)都用被動(dòng)模式。但被動(dòng)模式又帶來(lái)一個(gè)新問(wèn)題服務(wù)端返回的那個(gè)端口是隨機(jī)的防火墻如果只放行了 21數(shù)據(jù)連接必然被攔。表現(xiàn)出來(lái)就是登錄正常、列目錄正常一傳文件就超時(shí)。5.2 pasv_min_port 與 pasv_max_port 的配置解決辦法是把被動(dòng)模式的端口范圍固定下來(lái)然后在防火墻上放行這一段。vsftpd 的配置是這樣pasv_enableYES pasv_min_port30000 pasv_max_port30100范圍選多大按并發(fā)連接數(shù)估。每個(gè)數(shù)據(jù)傳輸占一個(gè)端口理論最大并發(fā)數(shù)等于端口數(shù)量。一般內(nèi)部使用 100 個(gè)端口足夠50 個(gè)并發(fā)以?xún)?nèi)完全扛得住。如果用戶(hù)量大可以放到 500 個(gè)。如果服務(wù)端在 NAT 后面還要加一行pasv_address告訴客戶(hù)端應(yīng)該連哪個(gè)公網(wǎng)地址pasv_address公網(wǎng)IP這一行不加服務(wù)端會(huì)把內(nèi)網(wǎng)地址返回給客戶(hù)端客戶(hù)端拿著一個(gè)內(nèi)網(wǎng) IP 去連自然連不上。這是我見(jiàn)過(guò)最多的被動(dòng)模式故障原因沒(méi)有之一。5.3 防火墻放行數(shù)據(jù)端口配置改完防火墻同步放行firewall-cmd --permanent --add-port30000-30100/tcp firewall-cmd --reload云平臺(tái)的安全組也要同步加一條規(guī)則端口范圍寫(xiě)30000-30100協(xié)議 TCP。這一步經(jīng)常被漏掉因?yàn)樵跈C(jī)器內(nèi)部折騰半天都正常一出公網(wǎng)就廢。還有 SELinux 這一層。如果系統(tǒng)開(kāi)了 SELinux 且策略比較嚴(yán)FTP 讀寫(xiě)家目錄會(huì)被攔getenforce setsebool -P ftpd_full_access on-P表示持久化不加的話(huà)重啟就失效。改完用getsebool ftpd_full_access確認(rèn)一下?tīng)顟B(tài)是 on。6. 常見(jiàn)問(wèn)題速查表與避坑經(jīng)驗(yàn)前面幾節(jié)講的是原理和流程這一節(jié)講的是我這些年實(shí)際踩出來(lái)的經(jīng)驗(yàn)。有些坑在文檔里不會(huì)寫(xiě)但遇到了能讓人折騰一整天。6.1 排查速查表現(xiàn)象最可能的原因快速驗(yàn)證處理動(dòng)作立刻報(bào)拒絕連接服務(wù)未啟動(dòng) / 綁回環(huán)ss -lntp | grep 21啟動(dòng)服務(wù)改listen_address立刻報(bào)拒絕連接服務(wù)正常防火墻 REJECTiptables -L -n -v放行 21注意規(guī)則順序轉(zhuǎn)圈后超時(shí)防火墻 DROPtcpdump看有無(wú)回包檢查 DROP 規(guī)則與安全組報(bào)沒(méi)有到主機(jī)的路由路由缺失或 ICMP REJECTip route、traceroute補(bǔ)路由或調(diào)整 REJECT 類(lèi)型ping 通但端口不通服務(wù)未監(jiān)聽(tīng)或端口錯(cuò)誤nc -vz探端口核對(duì)端口與監(jiān)聽(tīng)地址登錄成功但ls卡住被動(dòng)模式端口被攔客戶(hù)端開(kāi) PASV 調(diào)試日志固定 pasv 端口段并放行登錄成功但put失敗目錄權(quán)限或 SELinuxls -ld、getenforce改權(quán)限或開(kāi)ftpd_full_access重啟后故障復(fù)現(xiàn)配置未持久化重啟再測(cè)一遍iptables-save、--permanent6.2 幾個(gè)我踩過(guò)的坑坑一systemctl status是 active但端口沒(méi)起來(lái)。有一次配置文件里listen和listen_ipv6同時(shí)為 YESvsftpd 啟動(dòng)后立刻退出但 systemd 的重啟策略讓它反復(fù)拉起status看到的永遠(yuǎn)是 active。真正定位是靠journalctl里的重復(fù)報(bào)錯(cuò)。所以看狀態(tài)一定要配合看日志和看監(jiān)聽(tīng)??佣耐?iptables 忘了持久化。這個(gè)坑的殺傷力在于它有延遲——當(dāng)天一切正常第二天機(jī)器重啟故障原樣復(fù)現(xiàn)而且看起來(lái)像是“莫名其妙又壞了”?,F(xiàn)在我的習(xí)慣是改完立刻iptables-save /etc/sysconfig/iptablesfirewalld 環(huán)境則確保每條命令都帶--permanent??尤蛻?hù)端防火墻也攔。一直在服務(wù)端折騰最后發(fā)現(xiàn)是客戶(hù)端本機(jī)的出站規(guī)則攔了高位端口。被動(dòng)模式下客戶(hù)端要連服務(wù)端的 30000 以上端口有些終端安全軟件默認(rèn)只放行常用端口。這個(gè)排查起來(lái)很快在客戶(hù)端抓個(gè)包就一目了然??铀腘AT 環(huán)境下的pasv_address忘了配。表現(xiàn)是內(nèi)網(wǎng)用戶(hù)全正常外網(wǎng)用戶(hù)一律傳不了文件。因?yàn)榉?wù)端返回的是內(nèi)網(wǎng)地址外網(wǎng)客戶(hù)端拿著192.168.x.x去連當(dāng)然連不上。這個(gè)問(wèn)題的隱蔽性在于日志里看不出任何異常只能靠抓包或者看客戶(hù)端返回的 PASV 響應(yīng)內(nèi)容。坑五賬號(hào)密碼對(duì)了但登錄被拒。這不是網(wǎng)絡(luò)問(wèn)題是認(rèn)證層。常見(jiàn)原因是ftpusers和user_list文件里默認(rèn)拒絕了一批系統(tǒng)賬戶(hù)或者chroot_local_userYES配合家目錄可寫(xiě)導(dǎo)致 vsftpd 拒絕登錄。日志在/var/log/secure里搜vsftpd關(guān)鍵字能直接看到原因。6.3 長(zhǎng)期運(yùn)維上的一點(diǎn)建議FTP 這套東西本身沒(méi)有健康檢查機(jī)制出問(wèn)題往往是被動(dòng)發(fā)現(xiàn)的。我的做法是在監(jiān)控里加兩個(gè)探測(cè)點(diǎn)一個(gè)是 21 端口的 TCP 連通性探測(cè)另一個(gè)是真實(shí)的登錄加列目錄操作頻率五分鐘一次。前者能發(fā)現(xiàn)服務(wù)掛了后者能發(fā)現(xiàn)被動(dòng)模式配置被改壞了。另外配置變更一定要留記錄。FTP 的配置文件不長(zhǎng)但每一項(xiàng)都影響面很大尤其是被動(dòng)模式那幾行。我見(jiàn)過(guò)一次事故是有人為了“優(yōu)化性能”把pasv_min_port和pasv_max_port注釋掉了結(jié)果防火墻規(guī)則對(duì)應(yīng)的端口段全部失效外網(wǎng)用戶(hù)全部傳不了文件而內(nèi)網(wǎng)測(cè)試一切正常直到客戶(hù)投訴才發(fā)現(xiàn)。最后再分享一個(gè)小技巧排查 FTP 問(wèn)題時(shí)盡量用命令行客戶(hù)端ftp或lftp而不是圖形客戶(hù)端。命令行會(huì)原樣輸出服務(wù)端的響應(yīng)碼比如227 Entering Passive Mode (192,168,1,10,117,40)這里面的數(shù)字就是服務(wù)端返回的被動(dòng)端口117*2564030000一眼就能看出它返回的是不是內(nèi)網(wǎng)地址、端口在不在放行范圍內(nèi)。圖形客戶(hù)端把這些信息全藏起來(lái)了排查時(shí)等于蒙著眼睛。我在實(shí)際處理這類(lèi)問(wèn)題的體會(huì)是FTP 報(bào)錯(cuò)文案的區(qū)分度其實(shí)很高拒絕連接和沒(méi)有到主機(jī)的路由已經(jīng)把排查方向指得很明確難的不是判斷而是忍住不跳過(guò)驗(yàn)證步驟直接改配置。按鏈路、網(wǎng)絡(luò)、端口、應(yīng)用這四層老老實(shí)實(shí)走一遍絕大多數(shù)問(wèn)題在十分鐘內(nèi)就能定位到具體那一行配置上。