Главная страница » Резервный DHCP сервер Linux AD-DC Ubuntu 18.04

Резервный DHCP сервер Linux AD-DC Ubuntu 18.04

by Belfigor
2273 views
Резервный DHCP сервер Linux AD-DC Ubuntu 18

Резервный DHCP сервер Linux AD-DC всегда должен быть готов в максимально короткие сроки принять на себя функционал основного DHCP сервера. Для этого резервный DHCP сервер должен быть проинтегрирован с DHCP сервером контроллера домена Linux AD-DC. После интеграции он будет находиться в режиме ожидания. Всегда готовый автоматически принять на себя роль DHCP сервера в случае падения олимпа. А в случае восстановления основного контроллера домена, резервный DHCP сервер вернёт главенствующую роль его DHCP серверу.

Резервный DHCP сервер Linux AD-DC

Настраиваем DHCP сервер

Начинаем конечно же с установки.

sudo apt-get install isc-dhcp-server

Экспортируем данные необходимые для интеграции dhcp и dns сервера

sudo samba-tool domain exportkeytab --principal=dhcpduser@ADMINGUIDE.LAN /etc/dhcpduser.keytab
sudo chown dhcpd:dhcpd  /etc/dhcpduser.keytab
sudo chmod 400  /etc/dhcpduser.keytab

Создаём скрипт ответственный за обновления локальной DNS зона

sudo nano /usr/local/bin/dhcp-dyndns.sh

Копируем в него следующее содержимое:

#!/bin/bash
# /usr/local/bin/dhcp-dyndns.sh
# This script is for secure DDNS updates on Samba 4
# Version: 0.8.9
# Uncomment the next line if using a self compiled Samba and adjust for your PREFIX
#PATH="/usr/local/samba/bin:/usr/local/samba/sbin:$PATH"
BINDIR=$(samba -b | grep 'BINDIR' | grep -v 'SBINDIR' | awk '{print $NF}')
WBINFO="$BINDIR/wbinfo"
# DNS domain
domain=$(hostname -d)
if [ -z ${domain} ]; then
logger "Cannot obtain domain name, is DNS set up correctly?"
logger "Cannot continue... Exiting."
exit 1
fi
# Samba 4 realm
REALM=$(echo ${domain^^})
# Additional nsupdate flags (-g already applied), e.g. "-d" for debug
#NSUPDFLAGS="-d"
# krbcc ticket cache
export KRB5CCNAME="/tmp/dhcp-dyndns.cc"
# Kerberos principal
SETPRINCIPAL="dhcpduser@${REALM}"
# Kerberos keytab
# /etc/dhcpduser.keytab
# krbcc ticket cache
# /tmp/dhcp-dyndns.cc
TESTUSER="$($WBINFO -u) | grep 'dhcpduser')"
if [ -z "${TESTUSER}" ]; then
logger "No AD dhcp user exists, need to create it first.. exiting."
logger "you can do this by typing the following commands"
logger "kinit Administrator@${REALM}"
logger "samba-tool user create dhcpduser --random-password --description=\"Unprivileged user for DNS updates via ISC DHCP server\""
logger "samba-tool user setexpiry dhcpduser --noexpiry"
logger "samba-tool group addmembers DnsAdmins dhcpduser"
exit 1
fi
# Check for Kerberos keytab
if [ ! -f /etc/dhcpduser.keytab ]; then
echo "Required keytab /etc/dhcpduser.keytab not found, it needs to be created."
echo "Use the following commands as root"
echo "samba-tool domain exportkeytab --principal=${SETPRINCIPAL} /etc/dhcpduser.keytab"
echo "chown XXXX:XXXX /etc/dhcpduser.keytab"
echo "Replace 'XXXX:XXXX' with the user & group that dhcpd runs as on your distro"
echo "chmod 400 /etc/dhcpduser.keytab"
exit 1
fi
# Variables supplied by dhcpd.conf
action=$1
ip=$2
DHCID=$3
name=${4%%.*}
usage()
{
echo "USAGE:"
echo "  $(basename $0) add ip-address dhcid|mac-address hostname"
echo "  $(basename $0) delete ip-address dhcid|mac-address"
}
_KERBEROS () {
# get current time as a number
test=$(date +%d'-'%m'-'%y' '%H':'%M':'%S)
# Note: there have been problems with this
# check that 'date' returns something like
# 04-09-15 09:38:14
# Check for valid kerberos ticket
#logger "${test} [dyndns] : Running check for valid kerberos ticket"
klist -c /tmp/dhcp-dyndns.cc -s
if [ "$?" != "0" ]; then
logger "${test} [dyndns] : Getting new ticket, old one has expired"
kinit -F -k -t /etc/dhcpduser.keytab -c /tmp/dhcp-dyndns.cc "${SETPRINCIPAL}"
if [ "$?" != "0" ]; then
logger "${test} [dyndns] : dhcpd kinit for dynamic DNS failed"
exit 1;
fi
fi
}
# Exit if no ip address or mac-address
if [ -z "${ip}" ] || [ -z "${DHCID}" ]; then
usage
exit 1
fi
# Exit if no computer name supplied, unless the action is 'delete'
if [ "${name}" = "" ]; then
if [ "${action}" = "delete" ]; then
name=$(host -t PTR "${ip}" | awk '{print $NF}' | awk -F '.' '{print $1}')
else
usage
exit 1;
fi
fi
# Set PTR address
ptr=$(echo ${ip} | awk -F '.' '{print $4"."$3"."$2"."$1".in-addr.arpa"}')
## nsupdate ##
case "${action}" in
add)
_KERBEROS
nsupdate -g ${NSUPDFLAGS} << UPDATE
server 127.0.0.1
realm ${REALM}
update delete ${name}.${domain} 3600 A
update add ${name}.${domain} 3600 A ${ip}
send
UPDATE
result1=$?
;;
delete)
_KERBEROS
nsupdate -g ${NSUPDFLAGS} << UPDATE
server 127.0.0.1
realm ${REALM}
update delete ${name}.${domain} 3600 A
send
UPDATE
result1=$?
;;
*)
echo "Invalid action specified"
exit 103
;;
esac
result="${result1}"
if [ "${result}" != "0" ]; then
logger "DHCP-DNS Update failed: ${result}"
else
logger "DHCP-DNS Update succeeded"
fi
exit ${result}

Устанавливаем права на скрипт:

sudo chmod 755 /usr/local/bin/dhcp-dyndns.sh

Настраиваем dhcp сервер, предварительно забекапив старый конфиг

sudo cp /etc/dhcp/dhcpd.conf /etc/dhcp/dhcpd.conf.original
sudo nano /etc/dhcp/dhcpd.conf

Вставляем следующее содержимое:

authoritative;
ddns-update-style none;
omapi-port 7911;
omapi-key omapi_key;
key omapi_key {
algorithm hmac-md5;
secret "ваш_OMAPI_ключ_для_интеграции"; #
}
failover peer "adminguide-dhcp-failover" {
secondary;
address ag-dc-2.adminguide.lan; #Полное DNS имя резервного DHCP сервера
peer address ag-dc-1.adminguide.lan; #Полное DNS имя основного DHCP сервера
max-response-delay 60;
max-unacked-updates 10;
load balance max seconds 3;
}
subnet 192.168.1.0 netmask 255.255.255.0 { #Подсеть и маска на которую будет вещать dhcp сервер
option subnet-mask 255.255.255.0; #Маска сети
option broadcast-address 192.168.1.255; #Адрес броадкаста
option time-offset 0;
option routers 192.168.1.1; #Шлюз
option domain-name "adminguide.lan"; #Имя домена
option domain-name-servers 192.168.1.101, 192.168.1.100; #ДНС сервера
option netbios-name-servers 192.168.1.101, 192.168.1.100; #NetBIOS сервера
option ntp-servers 192.168.1.101, 192.168.1.100; #NTP сервера
pool { #Пулл адресов
failover peer "adminguide-dhcp-failover";
max-lease-time 1800; # Максимальное время аренды в секундах
range 192.168.1.110 192.168.1.199; #Диапазон адресов
}
}
on commit {
set noname = concat("dhcp-", binary-to-ascii(10, 8, "-", leased-address));
set ClientIP = binary-to-ascii(10, 8, ".", leased-address);
set ClientDHCID = concat (
suffix (concat ("0", binary-to-ascii (16, 8, "", substring(hardware,1,1))),2), ":",
suffix (concat ("0", binary-to-ascii (16, 8, "", substring(hardware,2,1))),2), ":",
suffix (concat ("0", binary-to-ascii (16, 8, "", substring(hardware,3,1))),2), ":",
suffix (concat ("0", binary-to-ascii (16, 8, "", substring(hardware,4,1))),2), ":",
suffix (concat ("0", binary-to-ascii (16, 8, "", substring(hardware,5,1))),2), ":",
suffix (concat ("0", binary-to-ascii (16, 8, "", substring(hardware,6,1))),2)
);
set ClientName = pick-first-value(option host-name, config-option-host-name, client-name, noname);
log(concat("adminguide.ru commit: IP: ", ClientIP, " DHCID: ", ClientDHCID, " Name: ", ClientName));
execute("/usr/local/bin/dhcp-dyndns.sh", "add", ClientIP, ClientDHCID, ClientName);
}
on release {
set ClientIP = binary-to-ascii(10, 8, ".", leased-address);
set ClientDHCID = concat (
suffix (concat ("0", binary-to-ascii (16, 8, "", substring(hardware,1,1))),2), ":",
suffix (concat ("0", binary-to-ascii (16, 8, "", substring(hardware,2,1))),2), ":",
suffix (concat ("0", binary-to-ascii (16, 8, "", substring(hardware,3,1))),2), ":",
suffix (concat ("0", binary-to-ascii (16, 8, "", substring(hardware,4,1))),2), ":",
suffix (concat ("0", binary-to-ascii (16, 8, "", substring(hardware,5,1))),2), ":",
suffix (concat ("0", binary-to-ascii (16, 8, "", substring(hardware,6,1))),2)
);
log(concat("Release: IP: ", ClientIP));
execute("/usr/local/bin/dhcp-dyndns.sh", "delete", ClientIP, ClientDHCID, ClientName);
}
on expiry {
set ClientIP = binary-to-ascii(10, 8, ".", leased-address);
# cannot get a ClientMac here, apparently this only works when actually receiving a packet
log(concat("Expired: IP: ", ClientIP));
# cannot get a ClientName here, for some reason that always fails
execute("/usr/local/bin/dhcp-dyndns.sh", "delete", ClientIP, "", "0");
}

Настройка ag-dc-1.adminguide.lan

Теперь переходим к текущему владельцу FSMO ролей, серверу ag-dc-1

Открываем конфиг dhcpd

sudo nano /etc/dhcp/dhcpd.conf

Вставляем следующее содержимое:

authoritative;
ddns-update-style none;
omapi-port 7911;
omapi-key omapi_key;
key omapi_key {
algorithm hmac-md5;
secret "ваш_OMAPI_код_для_интеграции";
}
failover peer "adminguide-dhcp-failover" {
primary;
address ag-dc-1.adminguide.lan; #FQDN мастера (текущего сервера)
peer address ag-dc-2.adminguide.lan; #FQDN резервного DHCP сервера
max-response-delay 60;
max-unacked-updates 10;
mclt 3600;
split 255;
load balance max seconds 3;
}
subnet 192.168.1.0 netmask 255.255.255.0 { #Подсеть и маска на которую будет вещать dhcp сервер
option subnet-mask 255.255.255.0;
option broadcast-address 192.168.1.255;
option time-offset 0;
option routers 192.168.1.1; #Шлюз
option domain-name "adminguide.lan"; #Имя домена
option domain-name-servers 192.168.1.100, 192.168.1.101; #ДНС сервера
option netbios-name-servers 192.168.1.100, 192.168.1.101; #NetBIOS сервера
option ntp-servers 192.168.1.100, 192.168.1.101; #NTP сервера
pool { #Пулл адресов
failover peer "adminguide-dhcp-failover";
max-lease-time 1800; # Максимальное время аренды в секундах
range 192.168.1.110 192.168.1.199; #Диапазон адресов
}
}
on commit {
set noname = concat("dhcp-", binary-to-ascii(10, 8, "-", leased-address));
set ClientIP = binary-to-ascii(10, 8, ".", leased-address);
set ClientDHCID = concat (
suffix (concat ("0", binary-to-ascii (16, 8, "", substring(hardware,1,1))),2), ":",
suffix (concat ("0", binary-to-ascii (16, 8, "", substring(hardware,2,1))),2), ":",
suffix (concat ("0", binary-to-ascii (16, 8, "", substring(hardware,3,1))),2), ":",
suffix (concat ("0", binary-to-ascii (16, 8, "", substring(hardware,4,1))),2), ":",
suffix (concat ("0", binary-to-ascii (16, 8, "", substring(hardware,5,1))),2), ":",
suffix (concat ("0", binary-to-ascii (16, 8, "", substring(hardware,6,1))),2)
);
set ClientName = pick-first-value(option host-name, config-option-host-name, client-name, noname);
log(concat("adminguide.ru commit: IP: ", ClientIP, " DHCID: ", ClientDHCID, " Name: ", ClientName));
execute("/usr/local/bin/dhcp-dyndns.sh", "add", ClientIP, ClientDHCID, ClientName);
}
on release {
set ClientIP = binary-to-ascii(10, 8, ".", leased-address);
set ClientDHCID = concat (
suffix (concat ("0", binary-to-ascii (16, 8, "", substring(hardware,1,1))),2), ":",
suffix (concat ("0", binary-to-ascii (16, 8, "", substring(hardware,2,1))),2), ":",
suffix (concat ("0", binary-to-ascii (16, 8, "", substring(hardware,3,1))),2), ":",
suffix (concat ("0", binary-to-ascii (16, 8, "", substring(hardware,4,1))),2), ":",
suffix (concat ("0", binary-to-ascii (16, 8, "", substring(hardware,5,1))),2), ":",
suffix (concat ("0", binary-to-ascii (16, 8, "", substring(hardware,6,1))),2)
);
log(concat("Release: IP: ", ClientIP));
execute("/usr/local/bin/dhcp-dyndns.sh", "delete", ClientIP, ClientDHCID, ClientName);
}
on expiry {
set ClientIP = binary-to-ascii(10, 8, ".", leased-address);
# cannot get a ClientMac here, apparently this only works when actually receiving a packet
log(concat("Expired: IP: ", ClientIP));
# cannot get a ClientName here, for some reason that always fails
execute("/usr/local/bin/dhcp-dyndns.sh", "delete", ClientIP, "", "0");
}

Генерируем на ag-dc-1 случайный OMAPI ключ и отображаем его с помощью команды

sudo dnssec-keygen -a HMAC-MD5 -b 512 -n USER DHCP_OMAPI
sudo cat Kdhcp_omapi.+*.private |grep ^Key|cut -d ' ' -f2-

У меня ключ следующий:

08bfJKl4Sg++xTfRnDlTbCT9PhxOAf9QSyULgppI77Hv2Wc8iD4tvl4NU8BZhPU62WjQNVt08VBhEpeZa+0Mvw==

Его то мне и нужно подставить dhcpd.conf в блок key, чтобы в конфигах обоих серверов получилось вот так:

key omapi_key {
algorithm hmac-md5;
secret "08bfJKl4Sg++xTfRnDlTbCT9PhxOAf9QSyULgppI77Hv2Wc8iD4tvl4NU8BZhPU62WjQNVt08VBhEpeZa+0Mvw==";
}

Перезапускаем DHCP сервер на обоих серверах:

sudo service isc-dhcp-server stop && sudo service isc-dhcp-server start

Если всё сделано правильно, то за считанные секунды DHCP сервера придут к соответствию. DHCP сервер ag-dc-1 передаст на DHCP сервер ag-dc-2 всю имеющуюся у себя инфу по лизам и в сислогах обоих контроллеров доменов появится следующая строчка:

Jan  1 21:09:38 ag-dc-1 dhcpd[5071]: failover peer adminguide-dhcp-failover: Both servers normal

Пришла пора к донастройке DHCP сервера ag-dc-2. А именно к дообучению его аппармора (в случае если он у вас не отключен)

Резервный DHCP сервер Linux AD-DC – Проверяем

Выключаем ISC-DHCP на ag-dc-1 командой

sudo service isc-dhcp-server stop

В сислоге на ag-dc-2 вы увидите:

Jan  1 21:14:28 ag-dc-2 dhcpd[25150]: peer adminguide-dhcp-failover: disconnected
Jan  1 21:14:28 ag-dc-2 dhcpd[25150]: failover peer adminguide-dhcp-failover: I move from normal to communications-interrupted

И очень вскоре ISC-DHCP на ag-dc-2 начнёт переобуваться и приступать к выполнению роли основного DHCP сервера сети. Если не начнёт пишите, будем разбираться. Но в процессе ему в этом активно будет мешать apparmor. Потому нам необходимо дообучить apparmor, чтобы он перестал блокировать попытки обновить новым DHCP сервером локальную ДНС зону домена.

Наиболее простой способ это утилита aa-logprof и любой dhcp клиент имеющийся в сети и не являющийся членом домена. У меня для подобных тестов есть отдельная линуксовая машина. Если утилита aa-logprof не стоит, она входит в комплект утилит apparmor-utils. Потому установим весь комплект командой

sudo apt install apparmor-utils

На ag-dc-2 я запускаю aa-logprof

sudo aa-logprof

Если я вижу что-то кроме

Reading log entries from /var/log/syslog.
Updating AppArmor profiles in /etc/apparmor.d.
Enforce-mode changes:

Я всячески соглашаюсь с тем что запрашивает apparmor, нажимая (A)llow, (I)nherit, (S)ave Changes

Потом иду на линуксовую машину, сбрасываю текущий полученный от DHCP сервера адрес, и запрашиваю новый с помощью команды:

sudo dhclient -r && sudo dhclient

Ииии снова запускаю sudo aa-logprof на ag-dc-2. Занимаюсь я этим до тех пор, пока aa-logprof не выведет мне долгожданные 3 строки и ничего более:

Reading log entries from /var/log/syslog.
Updating AppArmor profiles in /etc/apparmor.d.
Enforce-mode changes:

После того как apparmor перестал реагировать на перезапрос клиентом информации у DHCP сервера, вы увидите в сислоге привычную картину валидно работающего автообновления DNS зоны.

Теперь обратно включаем isc-dhcp сервер на ag-dc-1. Для этого используем команду:

sudo service isc-dhcp-server start

Если сразу глянуть сислог на ag-dc-2 вы увидите

Jan  2 11:46:10 ag-dc-2 dhcpd[8344]: failover peer adminguide-dhcp-failover: I move from communications-interrupted to normal

dhcp сервер резервного контроллера домена начал разоружаться.

Подождав несколько секунд, и снова дёрнув dhcp клиент, вы увидите что ag-dc-2 – видит запросы, но не реагирует на них.

Jan  2 11:50:49 ag-dc-2 dhcpd[8344]: DHCPDISCOVER from 00:0c:29:20:ce:dc via ens160: load balance to peer adminguide-dhcp-failover
Jan  2 11:50:49 ag-dc-2 dhcpd[8344]: DHCPDISCOVER from 00:0c:29:20:ce:d2 via ens160: load balance to peer adminguide-dhcp-failover
Jan  2 11:50:50 ag-dc-2 dhcpd[8344]: DHCPREQUEST for 192.168.1.111 (192.168.1.100) from 00:0c:29:20:ce:dc via ens160: lease owned by peer
Jan  2 11:50:50 ag-dc-2 dhcpd[8344]: DHCPREQUEST for 192.168.1.112 (192.168.1.100) from 00:0c:29:20:ce:d2 via ens160: lease owned by peer

В то время как ag-dc-1 полноценно отрабатывает каждую полученную запись.

Заключение

Собственно на этом данная серия статей объявляется завершённой. Далее будут только правки и уточнения. Так же возможны небольшие статьи дополнения. Потому следите за обновлениями :). Освещение последующих вопросов, таких как кроссплатформенная интеграция контроллеров домена, всевозможные миграции и многое другое вы сможете прочитать в будущем (конец 2021 – начало 2022 года). Подписывайтесь чтобы быть в курсе событий :).

Ещё больше интересного контента в моём блоге в Zen и на моём канале YouTube. Особенно если вас интересуют видеоуроки. Подписывайтесь на каналы, ставьте лайки, делайте репосты, это поможет развитию контента проекта AdminGuide.Ru

Text.ru - 100.00%

You may also like

6 комментариев

AlexandrK 27.04.2021 - 00:07

Сделал по Вашим статьям свой первый Linux AD DC, класс! Прочитал материал по резервному DC cо всеми плюшками, огромный труд!!! это круто!!

Хотелось бы увидеть несколько кейсов, возможно в отдельно взятых статьях
“Передача FSMO ролей, при падении 1го DC, с заменой его новым”
“Возможные ошибки в репликации и решения проблем с ними”

С нетерпением жду статьи в которой будет рассматриваться как можно в начале к существующим 1(2-м) Windows AD DC, добавить контроллер Linux AD DC, затем ещё резервный Linux с синхронизацией всех DC, а затем и как передать роли одному из двух серверов на Linux и в завершение порядок разжалования ПК с Windows AD DC.

Reply
Belfigor 27.04.2021 - 20:21

Спасибо за обратную связь! 🙂
Материалы касающиеся трансферов FSMO ролей во всевозможных направлениях будут освещаться в 3й серии мануалов, надеюсь опубликовать её до конца года 🙂
В данный момент для неё уже рисуются обложки, но к самому содержимому я ещё не приступал 🙂

Reply
AlexandrK 03.05.2021 - 21:23

С нетерпением жду! Спасибо!!!
PS включите, пожалуйста, по возможности, в статьи оглавление с порядком действий (как на Яндекс Дзене) где нибудь в начале статьи, а то иногда путаюсь

Reply
ivpiv 11.08.2021 - 14:01

Как в DNS автоматически удалять устаревшие записи?

Reply
Belfigor 27.08.2021 - 17:40

Контроллер сам обновляет записи для машин у которых меняется IP но не меняется название. Если машина убивается (например удаляется виртмашина), то удалять придётся руками.

Reply
Максим 30.08.2021 - 12:32

Спасибо автору за огромный труд и отличные манулы. Все описано четко и понятно. Радует частичка юмора 😉 Теперь вопрос. В конфиге DHCP (/etc/dhcp/dhcpd.conf) вы указываете параметр ddns-update-style none; Не отключаем ли таким образом работу DDNS?

Reply

Leave a Comment