Почему списки FTP зависают за VPN-шлюзом, который делает NAT
Вход и PWD проходят, а LIST и RETR встают, когда NAT переписывает только заголовки. Посмотрите, где должен находиться помощник FTP и как исправить пассивный режим.
Вход работает, PWD работает, а затем LIST висит, пока клиент не сдастся или не перейдёт в другой режим данных. Это разделение и есть признак: управляющее соединение к порту 21 здорово, а соединение данных строится с адреса или порта, до которого другая сторона не может добраться. За VPN-шлюзом, который тоже делает NAT, обычный виновник — команда FTP, в нагрузке которой указан туннельный адрес вроде 10.8.0.6. Перевод заголовков никогда не трогает это значение.
Далее — откуда берётся это значение и какая коробка имеет право его менять.
Ломается нагрузка, а не туннель
FTP помещает конечную точку данных внутрь управляющего потока. В активном режиме клиент отправляет PORT со своим адресом и портом, а сервер перезванивает на эту пару. В пассивном режиме сервер отвечает 227 с парой, и клиент звонит уже на неё.
Обе формы несут одно и то же утверждение: подключись к этому адресу на этом порту. Устройство, переписывающее только заголовки IP и TCP, оставляет утверждение как есть, поэтому одна сторона продолжает пытаться достичь адреса, который существует только по другую сторону туннеля. Соединение данных встаёт после того, как управляющее соединение уже объявлено здоровым.
Три режима, три нагрузки
Таблица ниже показывает, какой стороне велено звонить, и что с этим может сделать NAT, переписывающий только заголовки.
Читайте последний столбец как то, что отказывает первым. В активном режиме на частный адрес звонит сервер, в пассивном — клиент, а при FTPS нагрузка зашифрована, и промежуточной коробке работать ещё труднее.
| Mode | Value in the control stream | Who dials whom | Header-only NAT leaves |
|---|---|---|---|
| Active (PORT) | client address and port | server dials the client | a server dialing an unreachable tunnel address |
| Passive (227) | server address and port | client dials the server | a client dialing an address it cannot route to |
| FTPS | the same values, inside TLS | as above | ciphertext, so no rewrite happens at all |
Поставьте помощника туда, где происходит перевод
В Linux переписыванием нагрузки занимается помощник nf_conntrack_ftp. Он следит за управляющим соединением, узнаёт согласованный порт, создаёт ожидание для потока данных и на машине с NAT переписывает адрес и порт внутри нагрузки. Он помогает только на устройстве, которое переводит адрес клиента. Загрузка его на клиенте или на маршрутизаторе, который лишь пересылает поток, ничего не меняет в сессии.
Эти три команды показывают, есть ли модуль, подключён ли он и существует ли ожидание, пока сессия открыта:
modprobe nf_conntrack_ftp
lsmod | grep nf_conntrack_ftp
conntrack -L expect | grep ftp
Загрузить модуль — не то же самое, что его подключить. Ядра начиная с 4.7 поставляются с net.netfilter.nf_conntrack_helper=0, поэтому помощник ждёт явного правила, например iptables -t raw -A PREROUTING -p tcp --dport 21 -j CT --helper ftp. Если список ожиданий остаётся пустым во время попытки передачи, проверять нужно подключение, а не модуль.
Пассивный режим: скажите серверу, что объявлять
Пассивный режим проще сделать детерминированным, потому что серверу можно сказать, что вписать в ответ 227. В vsftpd это pasv_address вместе с pasv_min_port и pasv_max_port; ProFTPD использует MasqueradeAddress и PassivePorts. Задайте это первым, потому что правильно объявленный адрес снимает необходимость что-либо переписывать на той стороне.
Затем пропустите ровно этот диапазон портов через шлюз, например -A FORWARD -i tun0 -p tcp --dport 30000:30100 -j ACCEPT, чтобы правило совпадало с тем, что объявляет сервер.
Две настройки должны совпадать. Сервер, которому велено объявлять 30000-30100, тогда как шлюз разрешает 40000-40100, отказывает самым неудобным образом: управляющий разговор выглядит здоровым, а каждое соединение данных умирает. Сравните числа, прежде чем отлаживать что-то ещё.
Две подписи отказа и что убирает FTPS
Зависание на LIST указывает на первое соединение данных сессии, где должны работать и переписывание адреса, и ожидание. Зависание на RETR после работающего LIST указывает на саму передачу, то есть на пассивный диапазон или MTU туннеля. Вторая подпись — не проблема нагрузки, и настройки помощника её не исправят.
FTP поверх TLS убирает нагрузку, которую читает помощник. Промежуточная коробка не может переписать значение PORT или PASV, которое не может расшифровать, поэтому остаются прокси, завершающий TLS, сервер, объявляющий доступный адрес в ограниченном диапазоне, или другой протокол передачи.
Короткий порядок проверок
Идите от симптома наружу. Убедитесь, что LIST отказывает, пока PWD проходит, перехватите адрес внутри нагрузки PORT или 227 и спросите, какое устройство переводит этот адрес. Подключите помощника там, наблюдайте за списком ожиданий во время одной передачи, затем исправьте пассивный режим на сервере и отразите диапазон в правиле шлюза.
Держите отказ на пути данных отдельно от отказа при массовой передаче, потому что они ведут к разным настройкам.