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

Резервный 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 полноценно отрабатывает каждую полученную запись.