Резервный 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
Проснувшись однажды утром после беспокойного сна, Грегор Замза обнаружил, что он у себя в постели превратился в страшное насекомое.
6 комментариев
Сделал по Вашим статьям свой первый Linux AD DC, класс! Прочитал материал по резервному DC cо всеми плюшками, огромный труд!!! это круто!!
Хотелось бы увидеть несколько кейсов, возможно в отдельно взятых статьях
«Передача FSMO ролей, при падении 1го DC, с заменой его новым»
«Возможные ошибки в репликации и решения проблем с ними»
С нетерпением жду статьи в которой будет рассматриваться как можно в начале к существующим 1(2-м) Windows AD DC, добавить контроллер Linux AD DC, затем ещё резервный Linux с синхронизацией всех DC, а затем и как передать роли одному из двух серверов на Linux и в завершение порядок разжалования ПК с Windows AD DC.
Спасибо за обратную связь! 🙂
Материалы касающиеся трансферов FSMO ролей во всевозможных направлениях будут освещаться в 3й серии мануалов, надеюсь опубликовать её до конца года 🙂
В данный момент для неё уже рисуются обложки, но к самому содержимому я ещё не приступал 🙂
С нетерпением жду! Спасибо!!!
PS включите, пожалуйста, по возможности, в статьи оглавление с порядком действий (как на Яндекс Дзене) где нибудь в начале статьи, а то иногда путаюсь
Как в DNS автоматически удалять устаревшие записи?
Контроллер сам обновляет записи для машин у которых меняется IP но не меняется название. Если машина убивается (например удаляется виртмашина), то удалять придётся руками.
Спасибо автору за огромный труд и отличные манулы. Все описано четко и понятно. Радует частичка юмора 😉 Теперь вопрос. В конфиге DHCP (/etc/dhcp/dhcpd.conf) вы указываете параметр ddns-update-style none; Не отключаем ли таким образом работу DDNS?