Моя звать Борат. Я вас люблю. Я люблю АйТи. Нраица!

Борат

Любит автоматизацию FTP

Эта история расскажет о том, как наша компания автоматизировала передачу файлов по FTPS в условиях, когда не подходят стандартные решения и у разработчиков появляются мысли сделать fork ftplib, что, вообще-то, более похоже на депрессию из-за этой задачи.

Компания, в которой я работаю, занимается бизнесом в сфере DaaS.

На одном из этапов нашего пайплайна мы столкнулись с проблемой автоматизации передачи файлов клиенту по FTP.

Мы не могли решить проблему, поэтому появилась идея автоматизировать нажатия кнопок у FileZilla.

Мы использовали: wmctrl и xte.

  • wmctrl – помогал управлять окном FileZilla;

  • xte – нажимал на кнопки и горячие клавиши.

Это разработанный макрос для GUI - насильник местный. Не хороший!

Борат

Возмущён макросами для GUI

Автоматизация GUI - странное решение

Потому что это не гарантировало доставку файлов до клиента. Работая с автоматизацией GUI мы не можем точно отследить ошибки, проверить параметры, условия, посмотреть подробности какой-нибудь части. Кроме ведения логов, конечно. FileZilla предоставляет нам такую возможность, но это “доказательство от обратного”. Гораздо важнее начинать с перехвата ошибок на первом этапе - подключения к серверу. Чтобы можно было, хотя бы, переподключиться к FTP-серверу.

Итак. Сервер клиента использует FTPS, нужно явно указать порт, IP, данные для авторизации.

FileZilla имеет возможность сохранить параметры входа, и мы даже можем использовать параметр в командной строке, чтобы запустить клиент с подключением по реквизитам, указав название сохранённых данных из менеджера сессий.

Но и это не имеет смысла, так как отследить, в какой директории сейчас находится FileZilla, нельзя.

В ходе использования клиента мы обнаружили, что, если в локальной директории находится всего две папки: [‘..’, ‘some-dir’], то, при выделении через CTRL + A и по нажатию Enter, нас перенесёт в директорию выше, так как FileZilla имеет такой баг и нажимает именно на папку “..”, что является переходом на уровень вверх (спасибо, кэп).

Задача: по FTPS переслать файлы на сервер клиента. При условии, что роутер клиента в качестве ответа при подключении по FTPS выдаёт внутренний IP.

Не смотря на множество модулей Python, у нас случилась экстраординарная ситуация, которая вряд ли могла быть предусмотрена разработчиками таких известных библиотек, как pyftp, sftpy, ftplib.

В строго регламентированных документами компаниях (наш заказчик таким и является) – сетевики клиента порой не в праве менять что-то под исполнителя, поэтому на этом пути нам встретился ТАКОЙ неудачный вариант. И это надо было как-то решать.

Про пакеты FTP для Python

Все ftp-клиенты для Пайтона при подключении просто зависали. Там ни ответа, ни привета.

Было прочитано с сотню статей, куча документации и огромный стейк видео был пересмотрен. В итоге мы потом поняли, что сервер возвращает нам вообще неподходящий IP-адрес после подключения.

Все эти библиотеки берут этот IP и используют в дальнейших операциях. Например, при авторизации туда посылают запрос. Зачем это сделано? Мы же и так задали вручную нужный нам IP. Вот такие приколы от разработчиков этих модулей мы получили.

Путь к решению

Кстати, стандартный FTP в терминале Linux тоже нам не подходил. Он не умел FTPS-передачи делать.

Оказалось, что при запросе в Гугле, типа “cli ftp clients” часто упоминается lftp и другие.

Александр В. Лукьянов

Разработал LFTP в 1996 году

Так выпал день, что всё-таки мы попробовали LFTP, и ответ уже был другой! Хотя бы не зависает запрос. Но и что-то не то выдаёт.

А проблема была в том, что SSL-сертификат не проходил верификацию. Наверное, он просто самоподписанный. К большому счастью это можно отключить. Потому что у LFTP мощные настройки. Здесь: и логи, и путь к логам, и SSL-настройки, и многое-многое другое.

Код – примеры

LFTP может передавать кучу файлов на сервер и логировать процесс работы:

lftp -u username,password ip << SCRIPT

set xfer:log-file "/path/to/your/file.log"

cd /home/xxx/dir/ # перейти в директорию удалённого сервера

mput *.logs # множественная выборка и отправка подходящих файлов

SCRIPT

LFTP может зеркалировать две директории, то есть передавать файлы на сервер, но не перезаписывать имеющиеся, да ещё и в 10 потоков:

lftp -u username,password ip << SCRIPT

mirror -R -P 10 /src/dir /dest/dir/

SCRIPT

Нраица!

Борат

Автоматизировал FTPS


Автор решения, кода и статьи: Денис Ковтуненко

Комментарии (29)


  1. SerjV
    21.05.2023 11:40
    +1

    по FTPS переслать файлы на сервер клиента. При условии, что роутер клиента в качестве ответа при подключении по FTPS выдаёт внутренний IP.

    Это что, и клиент, и сервер - оба за NAT'ом?.. И сервер не поддерживает подстановку публичного IP для passive mode?


    1. merinoff
      21.05.2023 11:40
      +10

      Они, чтобы решить проблему, смотрели видео, а не запустили вайршарк и за секунду все поняли. Зачем вы такие сложные вопросы задаете :)


      1. SerjV
        21.05.2023 11:40
        +1

        ну вайршарк тут вряд ли поможет, скорее фиддлер (хотя не уверен, умеет он вкрывать что-то, кроме http(s)). Но вообще всё видно в логе самого ftp-клиента - это сервер отдаёт неправильный ip для пассив моды, или наоборот - клиент неправильный ip для активной. Для особо "одарённых" случаев после аутентификации можно включать шифрование на управляющем соединении (ну по крайней мере в стандарте это есть), и тогда "вумные" файрволы сами всё поправят в потоке управляющего соединения.


        1. baldr
          21.05.2023 11:40

          Чисто теоретически даже curl должен уметь соединяться по FTPS и по ключам -vvv выводить все что знает в дебаг-режиме.


          1. SerjV
            21.05.2023 11:40

            ну это да, у консольных клиентов наличие этой фичи - стандарт де-факто.


      1. redudri
        21.05.2023 11:40
        +1

        Ждём следующую статью про пользу WireShark в решении относительно простых повседневных задач.


        1. baldr
          21.05.2023 11:40
          +1

          Зря вы так язвите - иногда получается долго тупить и при "решении простых повседневных задач" и проще реально снять дамп и посмотреть что там пересылается по факту.


          1. redudri
            21.05.2023 11:40
            +5

            Ну если каждый новичок будет публиковать на хабр статью о том, что его программа падала раз в неделю, но он добавил в код оператор delete, и всё заработало, то тут будет русскоязычный аналог Reddit.

            Было бы интересно почитать про то, как автор изучил исходные коды всех предлагаемых в Python библиотек, не нашел там решение, внёс изменения, чтобы добиться решения, или написал полностью свою библиотеку. Но нет, посмотрели видео, отключили верификацию сертификатов, нашли утилиту, которая работает из коробки, дали рекламу на свой канал в Телеге.

            Это честная работа, но на статью на хабре не тянет, КМК.


            1. nickname9 Автор
              21.05.2023 11:40

              В статье предложен работающий вариант автоматизации FTPS (с помощью LFTP для Линукс), если какой-то бизнес захочет это сделать — найдёт статью и повторит. Работающих других решений найдено не было, поэтому статья стоит того, чтобы тут находиться. Всё просто — это решает запрос бизнеса.


      1. nickname9 Автор
        21.05.2023 11:40
        -1

        Проблема была ясна — нужно было лишь найти FTP-клиент, который эту задачу решал бы также, как это получалось через FileZilla. Через FileZilla мы как раз и узнали про неверный IP в ответе сервера. Вайршарк был бы излишеством.
        Посыл статьи в том, что не все FTP-клиенты для Python могут решать задачи с некоторыми трудными серверами


        1. inkvizitor68sl
          21.05.2023 11:40

          Вайршарк был бы излишеством.

          Действительно, можно и в tcpdump посмотреть было.


          1. SerjV
            21.05.2023 11:40
            +1

            вот только вряд ли бы он что-то показал в случае ftpS :) Потому что control connection зашифрован.

            Но вот есть вместо PASV дать серверу EPSV (оч странно было бы, если бы сервер умел TLS, но не умел EPSV/EPRT), то в ответе с хорошей вероятностью IP-адреса бы не было, только порт, что решило бы проблему.


    1. nickname9 Автор
      21.05.2023 11:40
      -1

      Насколько мы поняли — FileZilla умеет поправлять IP, который используется в подключении, а у заказчика стоял FTP-сервер, который этого не делал. Может быть, что у них там какая-то защита стояла, может быть Файрволл, может быть что-то ещё. По их регламенту — они нам этого не говорили, исходные данные были только такие: «Вот ваш логин, пароль, а вот наш айпи и порт».
      А был он за NAT'ом или нет — не ясно. Главное то, что LFTP умеет решать эту проблему и мы смогли это автоматизировать хотя бы на уровне Bash


      1. arheops
        21.05.2023 11:40

        Так библиотеки тоже умеют. Другой вопрос, пыталися ли вы включить нужные режимы?


        1. nickname9 Автор
          21.05.2023 11:40

          Да, конечно пытались, нам ничего не помогло, в основном включали и выключали PASV


      1. SerjV
        21.05.2023 11:40

        а EPSV сервер поддерживает, или только PASV?


        1. nickname9 Автор
          21.05.2023 11:40

          Скорее всего не поддерживает EPSV, отдельно протестил использование EPSV: от сервера нет отклика. Использовал ftplib.

          Несмотря на то, что «проблема известная» (как пишут в комментариях), решения никто не предложил (в тех статьях, да ещё которые работали бы, я имею в виду). А статья рассказывает про LFTP-утилиту для Linux — вот она работает.

          Подробности настроек FTP-сервера нам не сообщали.


    1. nickname9 Автор
      21.05.2023 11:40

      Нет, сервер не даёт отклика при использовании passive mode. Статья именно потому и написана, потому что всё давалось очень тяжело.


  1. baldr
    21.05.2023 11:40
    +4

    А проблема была в том, что SSL-сертификат не проходил верификацию. Наверное, он просто самоподписанный.

    Эту проверку можно отключить везде, включая питоновский ftplib.

    Задача: по FTPS переслать файлы на сервер клиента. При условии, что роутер клиента в качестве ответа при подключении по FTPS выдаёт внутренний IP.

    Да, это частая проблема при запуске FTP-сервера с параметрами по-умолчанию. Древнючий FTP-протокол был разработан задолго до NAT. Проблема известная.

    Вот это не решает вашу проблему?


    1. nickname9 Автор
      21.05.2023 11:40
      -1

      Мы бы не смогли дойти до такого описания проблемы, потому что:

      Все ftp-клиенты для Пайтона при подключении просто зависали. Там ни ответа, ни привета.


      1. baldr
        21.05.2023 11:40
        +5

        Было прочитано с сотню статей, куча документации и огромный стейк видео был пересмотрен.

        Вы, конечно, простите за прямоту. Но у меня поиск в гугл по простому запросу "python ftps" выводит уже вторым результатом (первым - ссылка на офсайт ftplib) ссылку на описание именно этой проблемы (если это была единственная проблема). Сложно поверить что в "сотне статей" не было хотя бы намека на это.

        Опять же - это действительно широко известная проблема, которая, в общем-то, изначально сводится к неправильному углу искривления рук админов того сервера. При запуске с дефолтными параметрами ftp-сервер берет единственный известный ему ip-адрес и начинает его отдавать в ответе на PASV команду. Достаточно прописать в конфиге правильный внешний адрес - и все должно заработать, но я вас понимаю в этом - у меня тоже был такой FTP сервер, который никто для меня не стал менять и мне пришлось чинить таким костылем. Это, действительно, костыль - сервер работает четко по спецификации (протоколу 50 лет), клиенты (Python) тоже обязаны по-умолчанию работать по спецификации.


        1. nickname9 Автор
          21.05.2023 11:40

          Проверил всё то, что обсуждалось в комментариях:
          1. PASV мы пробовали, и я протестил ещё раз — нет, это не решает проблему, от сервера нет отклика.
          2. EPSV — тоже никакого толка, протестил — никакого отклика.

          Как настроен сервер и с чем мы столкнулись — в принципе сейчас не важно, потому что статья предлагает работающее решение — LFTP-утилита для Linux


          1. baldr
            21.05.2023 11:40

            PASV мы пробовали, и я протестил ещё раз — нет, это не решает проблему, от сервера нет отклика.

            Нет отклика до PASV, нет ответа на PASV или после? Сложно что-то советовать поскольку вы не описываете проблему совсем.

            Смотрите: сервер отвечает на PASV своим адресом/портом. У вас был этот ответ? Там был адрес тот же что вы использовали при коннекте?

            У lftp есть режим "verbose", в котором он вам покажет что он посылает на сервер и что получает. Если сравнить этот вывод с тем что отправлял ваш скрипт на питоне - можно понять что идет не так. Вы сравнивали?

            В принципе, из статьи непонятно какую проблему вы решали, поэтому можно выкинуть из нее все и оставить только "у нас не заработало, мы взяли lftp и заработало".

            Ваша статья очень похожа на многие другие из серии "я долго ковырялся, ничего не понял, приделал костыль, напишу на хабр". Для себя вы решили - ок. Другим показывать такое решение - ну не очень оно красивое для этого.


          1. SerjV
            21.05.2023 11:40

            1. PASV мы пробовали, и я протестил ещё раз — нет, это не решает проблему, от сервера нет отклика. 2. EPSV — тоже никакого толка, протестил — никакого отклика.

            Оч интересно. Если не работают ни PASV, ни EPSV, то работают только PORT/EPRT и, самое интересное - сервер устанавливает data connection в сторону клиента. Как следствие - сервер в приниципе не передаёт клиенту "неправильный" IP-адрес, роутер/файрвол на стороне сервера не причём, неправильный IP-адрес передаёт серверу сам ftp-клиент. Причём это проблема в случае, если ftp-клиент находится за NAT'ом (и не только потому, что серверу уходит неправильный IP-адрес клиента, а потому что роутер на стороне ftp-клиента должен догадаться, куда пробросить соединение с 20-го порта сервера - в обычной ситуации он может это сделать анализируя control connection, но в случае FTPS это ему обломали)

            Что вступает в диссонанс с "по FTPS переслать файлы на сервер клиента. При условии, что роутер клиента в качестве ответа при подключении по FTPS выдаёт внутренний IP."

            Т.е. совершенно не понятно, в чём заключается у вас проблема и за счёт чего её решили? Просто "магия: пробовали всё подряд, и вот это заработало непонятно почему"


            1. baldr
              21.05.2023 11:40

              Да ну, скорее всего PASV возвращает какой-то внутренний адрес, клиент на него лезет и долго пытается открыть соединение что и выглядит как "просто зависали. Там ни ответа, ни привета". Сделайте `telnet 192.168.34.56 20` (случайный адрес) и будет то же самое - "Trying ..." долго висит и если повезет - вернет таймаут.


              1. SerjV
                21.05.2023 11:40

                Да ну, скорее всего PASV возвращает какой-то внутренний адрес

                Если возвращает - то это уже не значит "нет отклика". А EPSV обычно вообще не возвращает IP, а только порт.

                Так что "нет отклика" - уже странно звучит в сочетании с утверждением "выдаёт внутренний IP"


  1. SozTr
    21.05.2023 11:40

    Почему не описана причина почему был выбран FTPS, а не SFTP.


    1. baldr
      21.05.2023 11:40

      Я не автор, но для меня ответ очевиден - потому что такой сервер был предоставлен вендором. Обычно приходится работать с тем что дают.

      Мне по работе тоже приходится собирать данные с нескольких серверов от разных поставщиков. Каждый раз это лотерея - FTP/SFTP/FTPS/SSH - и каждый раз что-то новое: то древнючие шифры на сервере при установке соединения, то нестандартные порты или левые сертификаты, то кривая конфигурация сервера. То 40к файлов лежит в папке, прав на удаление нет и при каждой попытке найти новые файлы все падает по таймауту..

      Например, поставщик данных - национальный оператор электроэнергии. Никто не собирается под запросы какого-то мелкого клиента менять настройки уже установленного 5 лет назад сервера. Крутись как хочешь.


    1. nickname9 Автор
      21.05.2023 11:40

      Заказчик использовал FTPS-сервер, потому что это их внутренний регламент. А такие правила не меняются для кого-то или под кого-то.