Отказоустойчивый DHCP на двух машинах FreeBSD с синхронизацией баз

Давно был интересен вопрос резервирования DHCP-сервера. Почитав интернет, внезапно оказалось, что не одному мне пришла в голову такая мысль. Все-таки если DHCP упадет, то много пользователей могут быть недовольны 🙂

Итак, внезапно:

  1. HOWTO Configure DHCP failover
  2. http://metalcandy.ru/how-to-forge-centos/221-fail-over-dhcp-cnetos

Допустим, DHCP у нас уже установлен, причем на двух серверах 192.168.8.10 и 192.168.8.11. Все это крутится на свежей FreeBSD 9.0 RELEASE.

Конфиги в примерах не полные, так что попытаемся изобразить всю картину.

Configuring /usr/local/etc/dhcpd.conf on the Primary

 # DHCP Server - Configuration file for Primary

 # Global configuration
# 
option domain-name "me.local";
option domain-name-servers 192.168.8.9, 192.168.8.8, 192.168.8.10, 192.168.8.11;
option ntp-servers 192.168.8.10, 192.168.8.11;
option log-servers 192.168.8.10;
# 1 day
default-lease-time 86400; 
# 1 week
max-lease-time 604800;
authoritative;
log-facility local7;
set vendorclass = option vendor-class-identifier;

 # DHCP Failover, Primary
 include "/usr/local/etc/dhcpd/dhcpd.conf_primary";

 # Subnet declaration
 include "/usr/local/etc/dhcpd/dhcpd.subnet";

 # Static IP addresses
 include "/usr/local/etc/dhcpd/dhcpd.static";

 # EOF

/usr/local/etc/dhcpd/dhcpd.conf_primary (разные для первичного и вторичного):

##########################
 # DHCP Failover, Primary #
 ##########################

 failover peer "ls" {                   # Failover configuration
        primary; # I am the primary
        address 192.168.8.10;               # My IP address
        port 647;
        peer address 192.168.8.11;          # Peer's IP address
        peer port 647;
        max-response-delay 60;
        max-unacked-updates 10;
        mclt 3600;
        split 128; # Leave this at 128, only defined on Primary
        load balance max seconds 3;
 }

/usr/local/etc/dhcpd/dhcpd.subnet (одинаковые на обоих серверах, за исключением названия, далее пример, объявляем здесь все подсети, которые нам нужны):

subnet 192.168.6.0 netmask 255.255.255.0 {
 interface em0;
   pool {
         failover peer "ls";
         range 192.168.6.201 192.168.6.220;
        }
   option routers 192.168.6.254;
}

/usr/local/etc/dhcpd/dhcpd.static
Эти файлы одинаковые на двух серверах, их мы будем синхронизировать.

host phone07 { hardware ethernet 00:0b:82:14:36:10; fixed-address phone07; }
host testcomp { hardware ethernet 1c:bd:b9:d8:1c:22; fixed-address testcomp; }

Как видно, при запросе с указанного мака сервер резолвит айпишку по имени, и выдает полученный адрес. На обоих серверах поднят BIND, который забирает зону с первичного сервера. В resolv.conf прописываем

domain me.local
search me.local me.site.ua
nameserver 127.0.0.1
nameserver 192.168.8.9
nameserver 192.168.8.8
nameserver 192.168.8.11

Таким образом, ищем в имена в указанных доменах, а если что-то случилось с локальным биндом, спрашиваем у друзей.

Теперь перейдем ко второму серверу:

Configuring /usr/local/etc/dhcpd.conf on the Secondary

 # DHCP Server - Configuration file for Secondary
 # Global configuration
# 
option domain-name "me.local";
option domain-name-servers 192.168.8.9, 192.168.8.8, 192.168.8.10, 192.168.8.11;
option ntp-servers 192.168.8.10, 192.168.8.11;
option log-servers 192.168.8.10;
# 1 day
default-lease-time 86400; 
# 1 week
max-lease-time 604800;
authoritative;
log-facility local7;
set vendorclass = option vendor-class-identifier;

 # DHCP Failover, Primary
 include "/usr/local/etc/dhcpd/dhcpd.conf_secondary";

 # Subnet declaration
 include "/usr/local/etc/dhcpd/dhcpd.subnet.DONOTEDIT";

 # Static IP addresses
 include "/usr/local/etc/dhcpd/dhcpd.static.DONOTEDIT";

 # EOF

/usr/local/etc/dhcpd/dhcpd.conf_secondary:

##########################
 # DHCP Failover, Secondary #
 ##########################

 failover peer "ls" {                   # Failover configuration
        secondary;                           # I am the secondary
        address 192.168.8.11;               # My IP address
        port 647;
        peer address 192.168.8.10;          # Peer's IP address
        peer port 647;
        max-response-delay 60;
        max-unacked-updates 10;
        mclt 3600;
        load balance max seconds 3;
 }

На вторичном сервере я планирую выдавать другой диапазон для динамики, а основная часть хостов будет получать адреса статикой по макам.Поэтому следующей задачей является синхронизация файлов /usr/local/etc/dhcpd/dhcp.static между серверами.

Читаем:

By keeping the sub-configurations in sync across servers (perhaps using rsync), maintenance is reduced to a minimum.

Ну как всегда, «легко сообразить, нетрудно догадаться» 🙂

Поставим задачи:

  1. При перезапуске демона на Primary сделать бекап конфигов .subnets и .static.
  2. При перезапуске демона на Primary скопировать .subnets и .static в .subnets.DONOTEDIT и .static.DONOTEDIT на Secondary.
  3. На всякий случай проводить операцию копирования и сохранения бекапов каждую ночь.

Итак, создаем пользователя на обоих серверах:

ls2# adduser
Username: dhcp-updater

Даем этому пользователю доступ на запись файлов на вторичном сервере:

chown dhcp-updater dhcpd.subnet.DONOTEDIT
chgrp dhcp-updater dhcpd.subnet.DONOTEDIT
chown dhcp-updater dhcpd.static.DONOTEDIT
chgrp dhcp-updater dhcpd.static.DONOTEDIT

Дальше нужно сгенерировать ключи RSA, чтобы можно было авторизоваться по протоколу SCP без ввода пароля.

Делаем так на первичном:

su dhcp-updater
ssh-keygen -t rsa

Нажать несколько раз Enter, пароль не вводить. В результате имеем 2 файла в каталоге /home/dhcp-updater/.ssh/ : id_rsa и id_rsa.pub. Это приватный и публичный ключи. Теперь нужно скопировать публичный ключ на вторичный сервер, для этого:

ls1# scp id_rsa.pub [email protected]:/home/dhcp-updater/authorized_keys

Вводим пароль пользователя dhcp-updater на вторичном сервере. Теперь мы сможем использовать SCP.

Еще нам понадобится sudo на оба сервера:

cd /usr/ports/security/sudo ; make install clean

Создаем каталоги для бекапов на обоих серверах:

mkdir /var/dhcp-backup
chown dhcp-updater /var/dhcp-backup

Создаем скрипт копирования на первичном сервере:

touch /usr/local/bin/dhcp-sync
chmod 755 /usr/local/bin/dhcp-sync

В скрипте пишем что-то подобное:

#!/bin/sh
# Генрация бекапов
date=`date -v-1d '+%Y%m%d-%H%M%s'`
month=`date '+%m%Y'`
sudo -u dhcp-updater cp -f /usr/local/etc/dhcpd/dhcpd.subnet /var/dhcp-backup/dhcpd.subnet.$date
sudo -u dhcp-updater bzip2 -f -k -z /var/dhcp-backup/dhcpd.subnet.$date
sudo -u dhcp-updater tar -r -f /var/dhcp-backup/dhcpd.subnet.$month.tar /var/dhcp-backup/dhcpd.subnet.$date.bz2

sudo -u dhcp-updater cp -f /usr/local/etc/dhcpd/dhcpd.static /var/dhcp-backup/dhcpd.static.$date
sudo -u dhcp-updater bzip2 -f -k -z /var/dhcp-backup/dhcpd.static.$date
sudo -u dhcp-updater tar -r -f /var/dhcp-backup/dhcpd.static.$month.tar /var/dhcp-backup/dhcpd.static.$date.bz2

sudo -u dhcp-updater scp -q /var/dhcp-backup/dhcpd.subnet.$date.bz2 [email protected]:/var/dhcp-backup
sudo -u dhcp-updater ssh ls2 tar -r -f /var/dhcp-backup/dhcpd.subnet.$month.tar /var/dhcp-backup/dhcpd.subnet.$date.bz2

sudo -u dhcp-updater scp -q /var/dhcp-backup/dhcpd.static.$date.bz2 [email protected]:/var/dhcp-backup
sudo -u dhcp-updater ssh ls2 tar -r -f /var/dhcp-backup/dhcpd.static.$month.tar /var/dhcp-backup/dhcpd.static.$date.bz2

sudo -u dhcp-updater ssh ls2 rm /var/dhcp-backup/dhcpd.subnet.$date.bz2
sudo -u dhcp-updater ssh ls2 rm /var/dhcp-backup/dhcpd.static.$date.bz2

sudo -u dhcp-updater rm /var/dhcp-backup/dhcpd.subnet.$date
sudo -u dhcp-updater rm /var/dhcp-backup/dhcpd.static.$date
sudo -u dhcp-updater rm /var/dhcp-backup/dhcpd.subnet.$date.bz2
sudo -u dhcp-updater rm /var/dhcp-backup/dhcpd.static.$date.bz2

# Собственно синхронизация и рестарт удаленного DHCP
sudo -u dhcp-updater scp -q /usr/local/etc/dhcpd/dhcpd.subnet [email protected]:/usr/local/etc/dhcpd/dhcpd.subnet.DONOTEDIT
sudo -u dhcp-updater scp -q /usr/local/etc/dhcpd/dhcpd.static [email protected]:/usr/local/etc/dhcpd/dhcpd.static.DONOTEDIT
sudo -u dhcp-updater ssh ls2 sudo /usr/local/etc/rc.d/isc-dhcpd restart

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

Теперь нам нужно запускать скрипт. Логично будет это делать при перезапуске демона dhcpd на первичном сервере. Для этого открываем файл /usr/local/etc/rc.d/isc-dhcpd, находим функцию dhcpd_checkconfig () и модифицируем ее таким образом, чтобы при удачном прохождении теста проводилось копирование конфигурации:

dhcpd_checkconfig ()
{
        local rc_flags_mod
        setup_flags
	rc_flags_mod="$rc_flags"
        # Eliminate '-q' flag if it is present
	case "$rc_flags" in
	*-q*)	rc_flags_mod=`echo "${rc_flags}" | sed -Ee 's/(^-q | -q | -q$)//'` ;;
	esac
        if ! ${command} -t -q ${rc_flags_mod}; 
        then
                err 1 "`${command} -t ${rc_flags_mod}` Configuration file sanity check failed"
        else sh /usr/local/bin/dhcp-sync
        fi
}

Теперь осталась одна проблема: чтобы перезапустить DHCP на удаленном сервере, нужен root, поэтому мы делаем sudo. Но чтобы sudo не просил пароль рута, нам нужно добавить запись в /usr/local/etc/sudoers:

dhcp-updater	ALL = NOPASSWD: /usr/local/etc/rc.d/isc-dhcpd restart

Таким образом разрешаем выполнять данную команды этому пользователю без ввода пароля.

Последний штрих — добавляем в кронтаб задание:

crontab -e
0 0 * * * root /usr/local/etc/rc.d/isc-dhcpd restart

Если все было сделано правильно, теперь DHCP-сервера будут синхронизироваться и работать в паре. Указываем по два ip helper address и радуемся.

 

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