Домашній хостинг сайтів з динамічним IP

У мене (як і у багатьох web-розробників) є з десяток сайтів, які необхідно десь розмістити (хостити).

Сайти практично не приносять прибутку, оскільки це якісь старі роботи (по різних причинах не пішли в продакшн), домашня сторінка, сайт заведений красивою пошти тощо. Але в той же час ці сайти шкода кидати, а тому доводиться кожен місяць на них цілком реальні гроші щоб купувати хостинг. Гроші, прямо скажемо, невеликі, але тим не менше їх шкода, оскільки віддачі від сайтів ніякої немає.

У той-же час в наявності є:

  • Домашній сервер на Ubuntu
  • Швидкий ethernet-інтернет від МТС
Але не є ключового — статичного IP. Якби він був, то все було б набагато простіше і цю статтю я б точно не писав. А видавати статичний IP мій МТС абсолютно не бажає (якщо тільки я не підключуся як бізнес-клієнт).

Зрозуміло є всім відомі Dynamic DNS сервіси на зразок noip.com, але вони успішно вирішують лише завдання віддаленого доступу до нашого серверу (по SSH або FTP), але для хостингу абсолютно нам не підходять, оскільки в налаштуваннях домену DNS-сервері нам потрібно обов'язково прописати A-запис з реальним IP-адресою (а не посилання на наш віртуальний домен).

Що робити?
Я не буду зупинятися на тому, як налаштувати linux сервер (і тим більше як її вибрати), оскільки припускаю, що він у вас вже є. Також я не буду детально розписувати налаштування nginx і Apache, оскільки знову-таки припускаю, що ви з цим впораєтеся самостійно.

Перше з чим у мене виникли проблеми — це як перенаправляти відвідувачів з моїх доменів (у мене є 2 домену) на мій домашній сервер. Тобто щоб клієнт який набрав domain.com потрапив рівно на мій домашній сервер з урахуванням того, що на ньому кожен день змінюється IP-адресу.

Для рішення нам потрібно налаштувати DNS-сервер, а саме такі записи: SOA, NS, MX, A, CNAME. Важливо, щоб ми мали можливість налаштуванням TTL (time to live), оскільки час життя наших записів має бути дуже невеликим, буквально 60-120 секунд. В іншому випадку при зміні IP-адреси сервера користувачі довго не зможуть потрапити на наш сервер (через кешування).

Отже, нам потрібен DNS сервер, варіанти рішення:

  1. Використовуємо сервіси які надають нам DNS-хостинг
  2. Використовуємо власний DNS-сервер у зв'язці з DDNS-доменом
Розглянемо обидва варіанти.

Використовуємо сервіси які надають нам DNS-хостинг
Для цього є ряд безкоштовних сервісів, з яких найпопулярнішим є freedns.afraid.org. На таких сервісах можна додати свій домен(и) і отримати можливість через API оновлювати у них A-запис за допомогою невеликого скрипта.

Виглядає цілком непогано, але біда в тому, що ці сервіси залишають за собою право довешивать до вашого піддомени домену третього рівня. Тобто ви зареєстрували у них user.ru, а вони спокійно довешают свої сайти виду hello.user.ru, shop.user.ru і так далі. Зрозуміло від цього можна відмовитися, але… за гроші. Платити гроші за такі сервіси сенсу я не бачу, оскільки за подібні гроші ви можете придбати повноцінний хостинг на якому-небудь провайдера без всяких танців навколо DNS налаштувань.

Інші сервіси розглядати не будемо, а зосередимося на другому варіанті.

Використовуємо власний DNS-сервер у зв'язці з DDNS-доменом
Для цього варіанту у нас, по-перше, повинен бути DDNS-домен (який оновлюється при зміні IP), наприклад, domain.ddns.net, а по-друге, доведеться встановити і налаштувати BIND на нашому сервері.

Всього необхідно зробити рівно 5 кроків. Скрізь під словами «domain» або «domain.ru» мається на увазі ваше ім'я домену (короткий або повне).

1. Налаштувати 2 або 3 DDNS-піддомену
Чому 2 або 3? Тому, що ряд реєстрантів не дозволить вам використовувати домен тільки з одним NS-сервером. Саме прикро, що не всі про це скажуть — ваш домен просто не буде працювати, але ви не будете розуміти чому.

Тут все просто — йдемо на noip.com, там реєструємо аккаунт і додаємо 3 безкоштовних піддомену (більше 3 не дасть).

2. Налаштовуємо власний DNS-сервер
Встановлюємо BIND:

$ sudo apt-get install bind9

Створюємо зони (по одній зоні на кожен наш домен):

$ sudo nano /etc/bind/zones.my

з вмістом:

zone "domain.ua" {
type master;
file "./etc/bind/db.domain.ua";
};

і власне файл з налаштуваннями зони:

$ nano /etc/bind/db.domain.ru

і пишемо всередині:

;
; BIND data file for local loopback interface
;
$TTL 60
@ IN SOA domain.ru. admin.domain.ru. (
1477015437 ; Serial
10800 ; Refresh
3600 ; Retry
604800 ; Expire
1800 ) ; Negative Cache TTL

@ IN NS domain.ddns.net.
@ IN NS domain.ddnsking.com.
@ IN NS domain.myftp.biz.

@ IN MX 10 mx.yandex.net.

@ IN A 1.2.3.4

mail IN CNAME domain.mail.yandex.net.
* IN CNAME domain.ru.

Примітка: звертаю увагу, що TTL встановлюємо рівним 60 секунд. У файлі /etc/bind/named.conf.local додаємо підключення нашої зони:

include "./etc/bind/zones.my";

Все, рестартуем BIND:

$ sudo service bind9 restart

І глянемо /var/log/syslog щоб там не було повідомлень про помилки

3. Налаштувати наш домен(и)
Йдемо в панель керування реєстратора і там в налаштуваннях нашого домену в якості NS-серверів вказуємо створені DDNS-піддомени:

nameserver1 = domain.ddns.net
nameserver2 = domain.ddnsking.com
nameserver3 = domain.myftp.biz

Після цього, можливо, доведеться почекати кілька годин (або навіть добу) поки налаштування среплицируются між усіма серверами.

4. Налаштувати періодичне оновлення IP-адрес
Мій роутер підтримує оновлення IP-адреси на одному домені, але мені потрібно це робити відразу для 3-х доменів. Плюс нам треба оновлювати IP-адресу в конфіги BIND'а, тому напишемо скрипт який буде робити:

  1. Визначати наш зовнішній IP-адресу
  2. Перевіряти змінився IP адресу, якщо не змінився, то нічого робити не треба
  3. Оновлювати IP-адреса у всіх DDNS-піддоменів через API сервісу noip.com
  4. Прописувати новий IP-адресу в конфіг BIND'а
  5. Перезапускати BIND
Сам скрипт нехай буде на шелле:

#!/bin/sh

# This script works via noip.com service + local Bind server

# Settings
ZONES_CONFIG=zones.my
IP_FILE=./current_ip.txt
DDNS_USER=user
DDNS_PASS=password
DDNS_HOST=domain.ddns.net
DDNS_HOSTS=domain.ddns.net,domain.ddnsking.com,domain.myftp.biz

# Start
DATE=$(date +"%Y-%m-%d %H:%M:%S")

# detect an external IP
IP=$(dig +short $DDNS_HOST)
if [ $? -ne 0 ] || [ -z $IP ] || [ $IP = "0.0.0.0" ] ; then
echo "$DATE can't detect a remote IP. Aborting."
exit 1
fi

# verify IP changing
PREV_IP="(unknown)"
if [ -e $IP_FILE ] ; then
PREV_IP=$(cat $IP_FILE)
fi

if [ $IP = $PREV_IP ] ; then
echo "$DATE IP '$IP' has not changed"
else
echo "$DATE IP has been changed from '$PREV_IP' to '$IP'"

echo "$DATE IP will be updated on DDNS server"
/usr/bin/curl -u $DDNS_USER:$DDNS_PASS "https://dynupdate.no-ip.com/nic/update?hostname=$DDNS_HOSTS&myip=$IP"
fi
echo $IP > $IP_FILE

# check BIND config
cd /etc/bind
if [ ! -e $ZONES_CONFIG ] ; then
echo "$DATE File $ZONES_CONFIG not found!"
exit 1
fi

# read the list of active zones
ZONE_FILES=$(grep file $ZONES_CONFIG | grep -v ^# | perl -ne '/file "(.+)"/ && print "$1\n"')
for ZONE_FILE in $ZONE_FILES; do
echo "$DATE Process the zone config $ZONE_FILE"

cat $ZONE_FILE | perl -ne "s/([\t ]+В[\t ]+A[\t ]+)[\d\.]*/\${1}${IP}/; print \${_}" > $ZONE_FILE.tmp

if [ $(diff -w $ZONE_FILE $ZONE_FILE.tmp | wc -l) -ne 0 ] ; then
# update serial number
STAMP=$(date +%s)
cat $ZONE_FILE.tmp | perl -ne "s/\d+(?=.+Serial)/$STAMP/; print \${_}" > $ZONE_FILE

# reload BIND
service bind9 reload

echo "$DATE Config $ZONE_FILE is updated"
else
# nothing to do
rm $ZONE_FILE.tmp
echo "$DATE Config $ZONE_FILE is NOT changed"
fi
done

Скрипт потрібно запускати під рутом (щоб йому вистачило прав оновлювати конфіги BIND'а і рестартовать його). Додаємо в crontab рута його запуск кожну хвилину:

* * * * * cd /home/root && ./update_bind_config.sh >> /var/log/update_bind_config.log

Пару слів про визначення поточного IP-адреси. В скрипті вище це робиться через резолвинг DDNS-піддомену domain.ddns.net. Тобто спочатку наш роутер його туди прописує, а потім ми читаємо. Це не дуже хороший варіант, оскільки ми завязываемся на роутер і можемо втратити кілька хвилин поки на DDNS-піддомені оновиться IP-адресу на актуальний. Весь цей час наш сервер буде недоступний.

Тому у себе я використовував покращений варіант, який заодно не лазить в інтернет:

IP=$(perl -le 'use LWP::UserAgent; my $content=LWP::UserAgent->new->get("http://router")->decoded_content(); $content =~ q(<span id="wan_ipaddr">([\d\.]+)</span>); print $1')

У даному варіанті ми завантажуємо головну сторінку роутера (через http), далі регэкспом знаходимо на ній поточний IP-адресу. Зрозуміло, цей варіант підходить далеко не всім, але на DD-WRT прошивках працює.

5. Налаштування роутера
Про необхідність налаштувати звернення до DDNS-сервісу я вже писав, але ще не забувайте про необхідність налаштувати форвардінг портів на вашому роутері:
  • HTTP, TCP, 80-ий порт
  • DNS — TCP+UDP, 53 порт


Висновок
Отже, що я отримав в результаті:

  • Мої сайти живуть на домашньому сервері, за який я нікому не плачу;
  • Мої домени резолвятся через мій власний DNS-сервер, час життя записи 1 хвилина, тобто оновлення відбувається дуже швидко;
  • NS-записів вказані не реальні IP-адреси (які у мене часто міняються), а DDNS-піддомени;
  • Актуальність записів у DDNS-піддоменів і в конфіги мого DNS-сервера забезпечується автоматично, без якого-небудь втручання з мого боку.
За моїми вимірами, коли МТС (мій провайдер) оновлює мені IP-адресу, то мої сайти починають працювати через десь 2 хвилини. Це цілком прийнятно для мене.

p.s. Якщо комусь сподобалася дана замітка, то я можу написати другу частину, де розповім як налаштувати роботу з використанням DNS-хостингу Яндекса. Це дозволить відмовитися від власного DNS-сервера, відмовитися від DDNS-піддоменів, плюс трохи покращить надійність роботи (оскільки DNS-сервер ніколи не буде змінювати свій IP). Саме таку схему я використовую в даний момент.
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.