Предисловие


Картинка для привлечения внимания Для личных нужд дома поднял VSphere, на котором кручу виртуальный маршрутизатор и Ubuntu сервер в качестве медиа-сервера и еще кучи всяких вкусняшек, и этот сервер должен быть доступен из Интернет. Но проблема в том, что мой провайдер дает статику за деньги, которым всегда можно найти более полезное применение. Поэтому я пользовался связкой ddclient + cloudflare.

Все было хорошо, пока ddclient не перестал работать. Немного поковыряв его, я понял что пришло время костылей и велосипедов, так как времени на поиск проблемы стало уходить слишком много. В итоге все вылилось в небольшой демон, который просто работает, а мне больше и не надо.
Кому интересно – добро пожаловать под кат.

Используемые инструменты и как «это» работает


Итак первым делом я узнал на сайте cloudflare, все что нужно знать об API. И уже было сел реализовывать все на Python(после знакомства с Python, я все чаще применяю его для каких-то простых задач или когда нужно быстро сделать прототип), как вдруг наткнулся на практически готовую реализацию.
В общем за основу был взят враппер python-cloudflare.

Взял один из примеров для обновления DNS и добавил использование файла конфигурации и возможность обновлять несколько A записей в пределах зоны и естественно неограниченное количество зон.

Логика следующая:

  1. Скрипт получает из файла конфигурации список зон и проходит по ним в цикле
  2. В каждой зоне скрипт проходит в цикле по каждой DNS записи типа А или АААА и сверяет Public IP с записью
  3. Если IP отличается — меняет его, если нет пропускает итерацию цикла и переходит к следующей
  4. Засыпает на время, указанное в конфиге

Установка и настройка


Наверное можно было бы сделать и .deb пакет, но я в этом не силен, да и не так уж все сложно.
Процесс вполне подробно я описал в README.md на странице репозитория.

Но на всякий случай опишу на русском общими словами:

  1. Убедитесь что у вас установлен python3 и python3-pip, если нет — установите (в Windows python3-pip устанавливается вместе с Python)
  2. Клонируйте или скачайте репозиторий
  3. Установите необходимые зависимости.

    python3 -m pip install -r requirements.txt

  4. Запустите установочный скрипт
    Для Linux:

    chmod +x install.sh
    sudo ./install.sh

    Для Windows: windows_install.bat
  5. Отредактируйте файл конфигурации
    Для Linux:

    sudoedit /etc/zen-cf-ddns.conf

    Для Windows:

    Откройте файл zen-cf-ddns.conf в папке, куда установили скрипт.

    Это обычный JSON файл, по настройкам ничего сложно – специально описал как пример 2 разные зоны в нем.

Что скрывается за установщиками?


install.sh для Linux:

  1. Создается пользователь для запуска демона, без создания домашней директории и возможности логина.

    sudo useradd -r -s /bin/false zen-cf-ddns

  2. Создается файл для записи лога в /var/log/
  3. Делаем владельцем файла лога недавно созданного пользователя
  4. Копируются файлы по своим местам (конфиг в /etc, исполняемый файл в /usr/bin, файл службы в /lib/systemd/system)
  5. Активируется служба

windows_install.bat для Windows:

  1. Копирует исполняемый файл и файл конфигурации в указанную пользователем папку
  2. Создает задачу в планировщике запускать скрипт при старте системы
    schtasks /create /tn "CloudFlare Update IP" /tr "%newLocation%" /sc onstart

После изменения конфига скрипт нужно перезапустить, в Linux все просто и привычно:

sudo service zen-cf-ddns start
sudo service zen-cf-ddns stop
sudo service zen-cf-ddns restart
sudo service zen-cf-ddns status

для Windows придется убивать процесс pythonw и заново запускать скрипт(очень лень мне писать службу под Windows на C#):

taskkill /im pythonw.exe

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

Для тех, кто хочет посмотреть не самый красивый код Python, вот репозиторий на GitHub.

Лицензия MIT, так что делайте с этим добром, что хотите.

P.S.: Понимаю, что получилось немного костыльно, но со своей задачей справляется на ура.

UPD: 11.10.2019 17:37
Нашел еще 1 проблему, и если кто-то подскажет как ее решить — буду очень благодарен.
Проблема в том, что если установить зависимости без sudo python -m pip install -r ..., то из под пользователя сервиса модули не будут видны, а мне бы не хотелось заставлять ставить пользователей модули под sudo, да и не правильно это.
Как правильно сделать, чтобы было красиво?
UPD: 11.10.2019 19:16 Проблема решена использованием venv.
Получилось несколько изменений. Очередной релиз на днях будет.

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


  1. gecube
    11.10.2019 12:29
    +1

    Инструкции по установке дали, причем не очень верные (ну, не используйте service start/stop в 2019-м — есть же systemctl во всех дистрибах, кроме не-systemdшных).
    А вот рассказать про мотивировочную часть, как не спали ночами и писали — нет. А это самая мякотка. Ну, и из статьи непонятно как всё-таки работает Ваш аналог dyndns.


    Ну, и да — речь ведь про ситуацию, когда есть внешний адрес, но динамический? А не история, когда провайдер НАТирует весь трафик от клиентов? Например, в последнем я подловил всех опсосов.


    1. UrbanRider Автор
      11.10.2019 12:48

      Соглашусь, скрипт спокойно работает с systemctl, так как демонизирован через systemd. Я просто привык по старинке.

      Пр мотивировочную часть говорить особо нечего, от идеи до 1й реализации прошло часа 2, а потом в течении пары дней чуток допиливал.

      Работает все просто: Берем себе любой домен и делегируем в cloudflare и создаем DNS запись типа А, дальше настраиваем конфиг, прописывая зону и запись, которую нужно обновлять и все.
      Да речь действительно идет о ситуации когда есть внешний динамический адрес.

      Спасибо за интересный комментарий.


  1. GennPen
    11.10.2019 14:38

    Логика следующая:

    Весьма странная логика.
    Сверять текущий IP и DNS адрес лучше только при (пере)запуске процесса.
    Далее периодически (обычно раз в минуту) считывать IP из системы, если он отличается от предыдущей проверки, то обновлять запись в DNS.


    1. UrbanRider Автор
      11.10.2019 14:57

      Так и происходит же:

      1. Проверка при запуске сервиса каждой зоны
      2. Если в зоне нет изменений переходим к следующей(пропуск итерации цикла)
      3. Дальше засыпаем на время, указанное в конфиге (5 минут по-умолчанию)
      4. Повторяем


      Мне казалось, что я понятно расписал.


    1. UrbanRider Автор
      11.10.2019 15:45
      +1

      Не нашел как отредактировать или удалить свой комментарий, так что добавлю еще 1.
      Похоже вы правы. Придется скрипту еще и свой кэш заиметь. На днях будет новый коммит на гитхабе. Спасибо.


      1. GennPen
        11.10.2019 16:10
        +1

        Ну вот и я про то же. Нет смысла постоянно дергать DNS при каждой итерации скрипта, только обновлять запись при смене IP адреса. Тогда можно хоть каждую секунду мониторить смену IP адреса и оперативно обновлять его на DNS.