Еще один вариант динамического DNS на своей площадке или как я отказался от dyndns

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

Сначала можно было пользоваться одним доменным именем на учетной записи бесплатно. Позже доменное имя начало сбрасываться каждый месяц — это периодически требовало усилий по восстановлению работы. Другие бесплатные сервисы не приглянулись и в какой-то момент я подписался на их платную услугу за $25 в год, что дало мне возможность использовать до 30 доменных имен.

Было неудобство — клиентская программа под Windows визуализирует все доменные имена моей учетки при настройке и любой клиент случайно или умышленно может повредить привязку чужого доменного имени. В общем, гнетет, но терпимо. На прошлой неделе пришла пора продлевать на год подписку. Цена выросла до $30, да и рубль упал к этому моменту до 60 руб за доллар. Жалко мне стало рублей и я таки решил дожать динамический DNS на своей площадке.

Исходные данные:
  • windows и *nix машины с динамическими ip
  • freebsd 9.3
  • bind9
  • apache 2.4
  • php 5.3
  • свой домен

Что-нужно получить:
  • простую в использовании клиентскую часть


Решение:

Предположим, наш домен — MyDomain.ru. Опишем его как мастер зону в /etc/namedb/named.conf:

zone "MyDomain.ru" {
           type master;
           file "/etc/namedb/master/MyDomain.ru";
};

Cоответственно в /etc/namedb/master/MyDomain.ru пропишем что-нибудь типа:

$ORIGIN .
$TTL 3600       ; 1 hour
MyDomain.ru              IN SOA  ns1.MyDomain.ru. root.MyDomain.ru. (
                                2015032014 ; serial
                                10800      ; refresh (3 hours)
                                3600       ; retry (1 hour)
                                604800     ; expire (1 week)
                                86400      ; minimum (1 day)
                                )
                        NS      ns1.MyDomain.ru.
                        NS      ns2.MyDomain.ru.
                        A       188.120.254.163
                        MX      10 mail.MyDomain.ru.
                        MX      20 mail.MyDomain.ru.
$ORIGIN MyDomain.ru.
mail                    A       192.168.0.1
ns1                     A       192.168.0.1
ns2                     A       192.168.0.1
smtp                    A       192.168.0.1
www                     A       192.168.0.1

После выполнения:

echo 'named_enable="YES" ' >> /etc/rc.conf
/etc/rc.d/named start


Получаем работающий dns-сервер, проверяем пингом разрешение имен в адреса, если есть файерволл не забываем открыть 53 и 953 порты на вход. Настраиваем rndc — утилиту управления демоном named.

rndc-confgen

Ее вывод распределяем по двум файлам:

key "rndc-key" {
    algorithm hmac-md5;
    secret "rxeXMLrA\1py6mDLhGO7dA==";
};

options {
    default-key "rndc-key";
    default-server 127.0.0.1;
    default-port 953;
};


Помещаем в /etc/namedb/rndc.conf, а

# key "rndc-key" {
#       algorithm hmac-md5;
#       secret "rxeXMLrA\1py6mDLhGO7dA==";
# };
#
# controls {
#       inet 127.0.0.1 port 953
#       allow { 127.0.0.1; } keys { "rndc-key"; };
# };

предварительно убрав комментарий добавляем в /etc/namedb/named.conf. Если все правильно, то следующая команда должна выполнится успешно:

rndc reload


Можно посмотреть статус
rndc status

Можно почитать man по rndc

Нужно добавить немного прав юзеру, от имени которого работает named:

chown bind /etc/named/master

Создаем пользователя ddns (c домашним каталогом /home/ddns), от имени которого будет работать скрипт обновления записи dns-сервера.
Создаем скрипт следующего содержания и размещаем его в /home/ddns/ddns.sh, к примеру:

#!/usr/local/bin/bash
TTL=120
SERVER=127.0.0.1
ZONE=MyDomain.ru.
HOSTNAME=$1.$ZONE
KEY="rxeXMLrA\1py6mDLhGO7dA=="
new_ip_address=$2
nsupdate << EOF
server $SERVER
key rndc-key $KEY
zone $ZONE
update delete $HOSTNAME A
update add $HOSTNAME $TTL A $new_ip_address
send
EOF


Не забывает
chown ddns:ddns /home/ddns/ddns.sh 
chmod 700 /home/ddns/ddns.sh 

Вызов его с двумя параметрами

/home/ddns/ddns test 192.168.0.2

должен добавить в нашу зону домен третьего уровня test.MyDomain.ru c адресом 192.168.0.2.

Теперь осталось как-то разрешить обращаться клиентам к этому скрипту, чтобы они могли обновлять свои имена в зоне на актуальные ip-адреса. Я решил это с помощью веб-сервера. В конфигурации апача разрешил виртуальные сервера и создал один для собственных нужд.

<VirtualHost *:80>
    ServerAdmin root@MyDomain.ru
    DocumentRoot "/usr/local/www/ddns"
    ServerName ddns.MyDomain.ru
    DirectoryIndex index.php
    ErrorLog "/var/log/apache/ddns-error.log"
    CustomLog "/var/log/apache/ddns-access.log" common
    <Directory "/usr/local/www/ddns">
        AllowOverride All
        Order allow,deny
        Require valid-user
        Allow from all
        AuthName "Who are you?"
        AuthType Basic
        AuthUserFile /usr/local/www/ddns/.htpasswd
    </Directory>
</VirtualHost>

Основной ньюанс в том, что доступ к этому сайту возможен только с указанием имени пользователя и пароля. Учетные данные хранятся в /usr/local/www/ddns/.htpasswd, в том же каталоге размещаем php-скрипт следующего содержания:

<?php
$User = $_SERVER["REMOTE_USER"];
$IP = $_SERVER["REMOTE_ADDR"];
echo $IP;
$CMD = 'sudo -u ddns /home/ddns/ddns.sh ' . $User . ' ' . $IP;
exec ($CMD);
?>


К сожалению у меня не получилось запустить из php bash-скрипт напрямую, полагаю это вызвано настройками безопасности пользователя www, от имени которого работает служба веб-сервера. Поэтому использую sudo от имени пользователя ddns (обычный пользователь, в домашнем каталоге которого лежит сам скрипт). Вот содержимаое файла /usr/local/etc/sudoers для этого:

www ALL=(ddns) NOPASSWD: /home/ddns/ddns.sh

Ну и не забываем, собственно, наполнить файл учетными данными:

htpasswd -сb /usr/local/www/ddns/.htpasswd test test-password
htpasswd -b /usr/local/www/ddns/.htpasswd test1 test1-password
htpasswd -b /usr/local/www/ddns/.htpasswd test2 test2-password

Если правильно настроен sudo, нет проблем с правами доступ пользователя www, то при обращении клиента к ресурсу
http://ddns.MyDomain.ru 

получаем приглашение на ввод учетных данных.

Вводим логин и пароль, получаем в браузере ответ с ip-адресом клиента. На сервере, при этом, php-скрипт по логину клиента и его ip-адресу обновляет доменное имя в dns. Еще считаю разумным добавить в настройку named для нашей зоны следующие строки:

 update-policy {
 grant rndc-key name test.MyDomain.ru. A;
 grant rndc-key name test1.MyDomain.ru. A;
 grant rndc-key name test2.MyDomain.ru. A;
 };

Чтобы ключем rndc-key можно было править только разрешенные доменные имена. Еще хорошо бы общение клиента и сервера оформить через https, чтобы открытые пароли не гуляли в интернете.

Осталось только допилить клиента. Там все просто.

curl -u test:test-password http://ddns.MyDomain.ru

Утилита curl в *nix системах должна быть по-умолчанию, для Windows ее придется скачать. Оформить эту команду в bat-файл или sh-скрипт и разместить в планировщике задач или cron'е соответственно с интервалом вызова в 5 минут.

Чтобы подключить следующего клиента в эту схему, придумываем логин (домен 3-го уровня) и пароль для него. Правим клиентский скрипт и добавляем учетные данные в .htpasswd.
Поделиться публикацией
AdBlock похитил этот баннер, но баннеры не зубы — отрастут

Подробнее
Реклама

Комментарии 34

    0
    Спасибо. Меня этот поставщик dyndns тоже начал напрягать) MikroTik сейчас стал бесплатно давать сервис аналогичный для своих устройств. Приятный бонус.
      0
      sudo /root/ddns/ddns.sh
      

      Я как-то очень занервничал когда увидел это :-) А зачем тут вообще root? nsupdate, я так понимаю, через loopback с сервером общается.
        0
        Да, это совсем не обязательно, скрипту ненужны привилегии рута, поправил пути чтобы не смущали на /home/ddns
          +1
          а sudo убрать?
            0
            К сожалению совсем без sudo не получается. Не хочет у меня php из под www стартовать bash-скрипт, пришлось извернуться через обычного пользователя, о чем я поправил статью.
              0
              Так может у вас там хомяк был 0700? В этой задаче sudo не нужен (скрипт на баше, впрочем, тоже, все это можно было бы сделать сразу из PHP и не громоздить сущностей).
                0
                Согласен, но в php у меня меньше опыта чем в bash, и потому пока лишняя сущность. Хотя конечно Ваше замечание интересно, попробую.
        +3
        Не буду спрашивать, зачем всё это, но на всякий случай напомню о нормальном dns-хостинге, умеющем, в том числе DDNS по подобному банальному протоколу: dns.he.net/
          +1
          Не буду спорить на сколько он нормальный и качественный, не пользовал к сожалению. Свое решение позиционирую в первую очередь как решение для своей площадки. Интересно может быть тем, у кого свой dns-сервер. Кто-то может быть пользует свои почтовые сервера, кому-то может быть не нравится dropbox и он разворачивает owncloud. Считаю что всякое имеет право быть — если оно справляется со своей задачей.
            0
            Он замечательный, но, например, wildcard-домены у него заблокированы.
            +1
            Блокировку в php-скрипте не забыли, чтобы два клиента, одновременно обратившиеся к скрипту, не мешали друг другу? Что будет, если сервер упадёт? Адреса у всех клиентов обновляться не будут? Это всё вещи, которые в dyndns продуманы и учтены.
              0
              уточните о какой блокировке идет речь?

              понятно что я не рассматривал это в контексте отказоустойчивого кластера — это обычный виртуальный сервер на 30 клиентов. Ясно что если он упадет, будет заминка у клиентов, у меня отвалится еще пара сервисов. Хотя опять же забота хостера держать мой сервер по питанию и доступу в мир. Ни в коей мере это ведь не конкуренция dyndns, но в узких кругах моей мамы — мой сервис одержал победу ;)
                0
                Вы открываете и пересохраняете файл зоны из PHP-скрипта, так? Если два скрипта будут писать в один файл, это может кончиться плохо. Если один читает, когда второй в процессе записи, это ещё вариант сбоя. Если два прочитали, сделали изменения (каждый своё) и записали вместе, то может кто-то один «победить», затерев изменения другого и т.д. Проблем разных может быть много.

                Покажите код, как вы это делаете. Я его поревьюю и скажу, если вы чего делаете не так.
                  +1
                  Нет, зону обновляет сам bind через push. Там все атомарно, насколько я помню.
                    +1
                    Так весь код в статье. Сам php-скрипт никоим образом не трогает файлы зон, он лишь использует интерфейс взаимодействую через rndc, я полагаю что ребята-разработчики bind'а должны были позаботиться здесь о блокировках при одновременном доступе к зоне.
                      0
                      О, пардон, я пропустил этот скрипт. С ним всё хорошо получается.
                0
                Тоже долго сидел на DynDNS.
                В конец достал.
                Благо альтернатив — пруд-пруди.
                Правда не во всех роутерах можно указать желаемый.
                Мне повезло что в моем есть поддержка https://www.dnsomatic.com/.
                А уже к нему можно подключить много чего. Остановился на ChangeIP и уже второй год как забыл о том что где-то что-то может истекать.
                  0
                  Тоже пользовался динднс, пока он работал. Свет клином сошелся на нем потому, что он есть практически во всех роутерах, а во многих роутерах — только он один. Этот факт наверняка существенно увеличил их базу клиентов.

                  В итоге перешел на freedns. Его нету в роутерах, но свои плюсы сервис имеет. Обновление днс осуществляется http(s) запросом, по крону раз в 5 минут.
                    0
                    Когда-то тоже делал подобное решение, но менее хардкорное и менее прозрачное с точки зрения DNS. Насколько быстро в вашем случае обновляется запись? Браузеры честно каждый раз идут к вашему днс серверу смотреть, куда указывает поддомен, или есть нюансы?
                      0
                      Время жизни записи выставлено в 120 с. Теоретически должно обновиться в две минуты, но часто вызывает проблему именно кэш самого dns-клиента на той же windows-машине. DynDNS с этим решили хитро, при установке своего клиента предлагают также использовать свои dns якобы для ускорения. Т.е. если с такой машины выйти в инет, клиент быстро обновится и благодаря прописанным родным dns-серверам мы тоже очень быстро обманемся. Вообще конечно по статистике пока еще рано что-то писать.
                      0
                      freedns.afraid.org/
                      Делегируется им свой домен, дальше — полет фантазии.
                      Обновление — дергается URL по крону. Бесплатно, стабильно.
                        0
                        freedns.afraid.org/

                        Надо заходить раз в полгода (иначе приостанавливается) и ограничение 5 доменов на учетную запись.
                        А так всё хорошо.
                        0
                        Я немного запарился и написал скрипт, отправляющий данные в cloudflare по их api.
                        Он и понадежнее будет, бесплатный, не упадет если надо, и клиенты не потеряются с адресацией.
                          +1
                          Ставим rdns — утилиту управления демоном named.
                          Помещаем в /etc/namedb/rdnc.conf

                          Кто-то Вас однозначно за эти ляпы вспомнит «не злым, добрым словом»…

                          Не забывает chown ddns:ddns /home/ddns/ddns.sh

                          chmod не менее важен.

                          В целом статья, как мануал, рассчитана на довольно хорошо подготовленного юниксоида с уклоном во FreeBSD (Вы бы хоть в тегах указали, что статься писана под эту ОС), т.к. опечаток, недосказанностей и явных ляпов в ней, к сожалению, уйма. Т.е. для новичка-копипастера она, как минимум, вредна (в виду явных ошибок и того же копи-паста), а для опытных она как бы не сильно полезна, т.к. задача проста, а статья кишит ошибками.
                          Уж простите за критику.
                            0
                            Спасибо, учту
                              0
                              Тогда вот еще добавки:
                              1. В 10-й фре bind убрали из базы -> нужно ставить и конфиги будут, соответственно, в /usr/local/etc
                              2. Утилита называется rndc, если bind открывает сокет для нее на лупбеке, то файервол для нее трогать ни к чему. Кстати, распространенная ошибка, связанная с ним, заключается в том, что открывают 53 порт только для udp, забыв про tcp.
                              3. Зачем выполнять команду rndc reload? Для проверки работоспособности лучше подойдет rndc status.
                              4. В настройках bind'а не замечено allow-update -> nsupdate работать не будет, т.к. динамические обновления отключены по умолчанию.
                              5. Использовать ключ для rndc в качестве tsig'а — мягко выражаясь, неправильно.
                              6. В конце концов, зачем писать скрипт на шелле, сомневаюсь, что для пыхи нет какого-нибудь днс модуля, через который можно соорудить дднс без костылей.
                                0
                                1. В статье указано что сабж раворачивается на freebsd 9/3
                                2. Файервол трогается в принципе чтобы dns-сервер не остался в вакууме и был виден миру, собственно зачем он иначе. Про tcp/udp не указываю, обычно это значит что надо указывать ip в целом.
                                3. дополнил статью
                                4. вместо allow-update используется update-policy — в статье указано, вполне себе работает.
                                5. поясните пожалуйста этот момент.
                                6. ну это один из вариантов, он изначально вырастал из скрипта, php появился позже. Farcaller выше уже упоминал лишние сущности. Можно конечно поспорить о том что модуль php тоже по своему костыль. Здесь хорошо бы напрямую через exec вызывать nsupdate, но пока крутится и так.
                                  0
                                  1. Это да, я прочитал, но обновляться-то нужно, не всегда ж на девятке жить, да и базовый bind тоже лучше поменять на какую-нибудь ESV.
                                  3.
                                  шепотом
                                  там ошибочка :)

                                  4. Да, я ее пропустил, пардон.
                                  5. Вы используете один и тот же ключ для аутентификации rndc и для подписи динамических апдейтов, это немножко несекьюрненько. То есть можно сказать «Да что там может случиться? Зачем все усложнять?», но это тот случай, когда проще перебдеть и сделать раздельные ключи, чем думать об этом, когда кто-то скажет: «А давай я буду слать эти апдейты со своего компа?». Кстати, что вы будете делать в этой ситуации?
                                  6. Вот вам в копилочку еще одна слабость вашего скрипта: он позволяет только один апдейт за раз, при том, что даже сама nsupdate умеет менять записи пачками.
                                    0
                                    1. обновлять конечно надо, вот 11 стабильный выйдет — перееду на 10 ку :)
                                    3. спасибо, забегался с корректировками…
                                    5. понял, про единый ключ размышлял, надо конечно переделать на отдельный ключ для моего сервиса, так конечно будет правильней
                                    6. я не сказал бы что это слабость, все же ориентация скрипта именно такая — что каждому пользователю — один поддомен. Пусть это будет его силой :)
                            0
                            Что помешает клиенту Б изменить зону клиента А? никакой проверки как я понимаю нет, вы удаляете старую запись и добавляете новую.
                              0
                              читайте статью, для изменения нужен логин и пароль
                                0
                                читайте комментарий. я написал клиенту Б изменить зону клиента А. те в случае если у вас пользователей больше чем только вы, то защиты друг от друга никакой не предусмотрено?
                                У меня есть логин в системе (как пользователь), допустим, тогда я смогу изменить вашу зону (как я понимаю).
                                  +1
                                  каждой зоне соответствуют свои учетные данные, которые прописаны в /usr/local/www/ddns/.htpasswd
                                  т.е.
                                  клиентА парольА
                                  клиентБ парольБ

                                  затем клиентА авторизовавшись — обновляет зону КлиентА.домен.ру, это заложено в скрипте, если он знает учетные данные КлиентаБ, он сможет обновить зону клиентаБ, если он знает пин-код от моей карты, он сможет забрать мои деньги )
                                    0
                                    Понял. немного тупанул, подумав что авторизуете в домене 2 уровня.

                            Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                            Самое читаемое