DHCP server for Linux AD-DC Ubuntu 18.04. Integration with BIND9. The cherry on the cake. It’s great when we have a working Active Directory Domain Controller on Linux, but it’s not enough. Living in a world where in our local DNS zone only IP addresses of devices directly in the domain are automatically updated is not as rosy as it could be. We will try to solve this problem in this article. It is necessary to indicate in advance that our decision will not update the reverse zone (PTR recordings).
All articles in best to study order:
-
Configuring BIND9 for Linux AD-DC on Ubuntu 18.04 – Part 1
-
Linux AD-DC on Ubuntu 18.04 – Setting Samba4 – Part 1
-
Set up BIND9 for Linux AD-DC on Ubuntu 18.04 – Part 2
-
Linux Samba4 AD-DC on Ubuntu 18.04 – Settings – Part 2
-
DHCP server for Linux AD-DC Ubuntu 18.04. Integration with BIND9
-
Domain Controller on Ubuntu 18.04 – Time Synchronization – NTP
-
Administering the Linux domain controller
-
Ubuntu Domain Controller – Set up DHCP server
- To begin with, install a DHCP server
sudo apt-get install isc-dhcp-server
- Then we create a domain user to work with updates
sudo samba-tool user create dhcpduser --description="Unprivileged user for TSIG-GSSAPI DNS updates via ISC DHCP server" --random-password
- We also set a password expiration and add to the group DnsAdmins
sudo samba-tool user setexpiry dhcpduser --noexpiry sudo samba-tool group addmembers DnsAdmins dhcpduser
AdminGuide.Ru@ag-dc-1:~$ sudo samba-tool user setexpiry dhcpduser --noexpiry Expiry for user 'dhcpduser' disabled. AdminGuide.Ru@ag-dc-1:~$ sudo samba-tool group addmembers DnsAdmins dhcpduser Added members to group DnsAdmins
- Now we export the data
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
- Creating a script
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}
Set the rights to the script
sudo chmod 755 /usr/local/bin/dhcp-dyndns.sh
- Modifiable file with settings
Create a bekap and modify the configsudo cp /etc/dhcp/dhcpd.conf /etc/dhcp/dhcpd.conf_bak sudo nano /etc/dhcp/dhcpd.conf
Replacing content with the following:
authoritative; ddns-update-style none; 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; #ДНС сервера option netbios-name-servers 192.168.1.100; #NetBIOS сервера option ntp-servers 192.168.1.100; #NTP сервера pool { #Пулл адресов 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"); }
- Edit the rights to launch
sudo nano /etc/apparmor.d/usr.sbin.dhcpd
Add to the end of the file before the closing bracket
/usr/local/bin/dhcp-dyndns.sh rix, /dev/tty wr, /usr/sbin/samba rix, /usr/bin/gawk rix, /usr/bin/grep rix, /usr/bin/hostname rix, /usr/bin/logger rix, /usr/bin/wbinfo rix, /usr/bin/date rix, /usr/bin/klist rix, /usr/bin/host rix, /proc/** wr, /usr/bin/kinit rix, /etc/dhcpduser.keytab rk, /run/samba/winbindd/pipe wr, /etc/dhcpduser.keytab rk, /tmp/* wrk, /usr/bin/nsupdate rix,
Restart AppArmor
sudo /etc/init.d/apparmor stop && sudo /etc/init.d/apparmor start
Restart DHCP server
sudo service isc-dhcp-server stop && sudo service isc-dhcp-server start
-
DHCP server for Linux AD-DC – Check DHCP
In the network with ad-dc-1 I have another ubuntu server with the name client-lin-1. It’s not in the domain, it’s not configured, it’s just there and it gets network settings on dhcp.
Trying to kick this client by name, the result is zero.
I turn on this server, waiting for it to load. After the server is loaded, I try again, the ping goes.
Teams ping client-lin-1 and ping client-lin-1.adminguide.lan will now successfully ping this machine by name. Like your other devices that are not in the domain and receive addresses on DHCP from the domain controller.
I release and re-request an IP address fulfilling on the client command:sudo dhclient -r && sudo dhclient
Watching logs as a team
tail -n 250 /var/log/syslog
I see an answer similar to:
Aug 30 16:56:23 ag-dc-1 dhcpd[10880]: Release: IP: 192.168.1.114 Aug 30 16:56:23 ag-dc-1 dhcpd[10880]: execute_statement argv[0] = /usr/local/bin/dhcp-dyndns.sh Aug 30 16:56:23 ag-dc-1 dhcpd[10880]: execute_statement argv[1] = delete Aug 30 16:56:23 ag-dc-1 dhcpd[10880]: execute_statement argv[2] = 192.168.1.114 Aug 30 16:56:23 ag-dc-1 dhcpd[10880]: execute_statement argv[3] = 00:0c:29:20:ce:d2 Aug 30 16:56:23 ag-dc-1 named[675]: samba_dlz: starting transaction on zone adminguide.lan Aug 30 16:56:23 ag-dc-1 named[675]: samba_dlz: allowing update of signer=dhcpduser@ADMINGUIDE. LAN name=3(NXDOMAIN).adminguide.lan tcpaddr=127.0.0.1 type=A key=3167229815.sig-ag-dc-1.adminguide.lan/160/0 Aug 30 16:56:23 ag-dc-1 named[675]: client @0x7f1c180b8670 127.0.0.1#58645/key dhcpduser@ADMINGUIDE. LAN: updating zone 'adminguide.lan/NONE': deleting rrset at '3(NXDOMAIN).adminguide.lan' A Aug 30 16:56:23 ag-dc-1 name[675]d: samba_dlz: committed transaction on zone adminguide.lan Aug 30 16:56:23 ag-dc-1 dhcpd: DHCP-DNS Update succeeded Aug 30 16:56:23 ag-dc-1 dhcp[10880]d: DHCPRELEASE of 192.168.1.114 from 00:0c:29:20:ce:d2 (client-lin-1) via ens160 (found) Aug 30 16:56:23 ag-dc-1 dhcpd[10880]: DHCPDISCOVER from 00:0c:29:20:ce:d2 via ens160 Aug 30 16:56:24 ag-dc-1 dhcpd[10880]: DHCPOFFER on 192.168.1.114 to 00:0c:29:20:ce:d2 (client-lin-1) via ens160 Aug 30 16:56:24 ag-dc-1 dhcpd[10880]: adminguide.ru commit: IP: 192.168.1.114 DHCID: 00:0c:29:20:ce:d2 Name: client-lin-1 Aug 30 16:56:24 ag-dc-1 dhcpd[10880]: execute_statement argv[0] = /usr/local/bin/dhcp-dyndns.sh Aug 30 16:56:24 ag-dc-1 dhcpd[10880]: execute_statement argv[1] = add Aug 30 16:56:24 ag-dc-1 dhcpd:[10880] execute_statement argv [2]= 192.168.1.114 Aug 30 16:56:24 ag-dc-1 dhcpd:[10880] execute_statement argv [3]= 00:0c:29:20:ce:d2 Aug 30 16:56:24 ag-dc-1 dhcpd:[10880] execute_statement argv [4]= client-lin-1 Aug 30 16:56:24 ag-dc-1 named:[675] samba_dlz: starting transaction on zone adminguide.lan Aug 30 16:56:24 ag-dc-1 named:[675] samba_dlz: allowing update of signer=dhcpduser@ADMINGUIDE. LAN name=client-lin-1.adminguide.lan tcpaddr=127.0.0.1 type=A key=3775092585.sig-ag-dc-1.adminguide.lan/160/0 Aug 30 16:56:24 ag-dc-1 named[675]: samba_dlz: allowing update of signer=dhcpduser@ADMINGUIDE. LAN name=client-lin-1.adminguide.lan tcpaddr=127.0.0.1 type=A key=3775092585.sig-ag-dc-1.adminguide.lan/160/0 Aug 30 16:56:24 ag-dc-1 named[675]: client @0x7f1bf808a7f0 127.0.0.1#60997/key dhcpduser@ADMINGUIDE. LAN: updating zone 'adminguide.lan/NONE': deleting rrset at 'client-lin-1.adminguide.lan' A Aug 30 16:56:24 ag-dc-1 name[675]d: samba_dlz: subtracted rdataset client-lin-1.adminguide.lan 'client-lin-1.adminguide.lan.#0113600#011IN#011A#011192.168.1.112' Aug 30 16:56:24 ag-dc-1 named[675]: client @0x7f1bf808a7f0 127.0.0.1#60997/key dhcpduser@ADMINGUIDE. LAN: updating zone 'adminguide.lan/NONE': adding an RR at 'client-lin-1.adminguide.lan' A 192.168.1.114 Aug 30 16:56:24 ag-dc-1 name[675]d: samba_dlz: added rdataset client-lin-1.adminguide.lan 'client-lin-1.adminguide.lan.#0113600#011IN#011A#011192.168.1.114' Aug 30 16:56:24 ag-dc-1 name[675]d: samba_dlz: subtracted rdataset adminguide.lan 'adminguide.lan.#0113600#011IN#011SOA#011ag-dc-1.adminguide.lan. hostmaster.adminguide.lan. 48 900 600 86400 3600' Aug 30 16:56:24 ag-dc-1 named[675]: samba_dlz: added rdataset adminguide.lan 'adminguide.lan.#0113600#011IN#011SOA#011ag-dc-1.adminguide.lan. hostmaster.adminguide.lan. 49 900 600 86400 3600' Aug 30 16:56:24 ag-dc-1 named[675]: samba_dlz: committed transaction on zone adminguide.lan Aug 30 16:56:24 ag-dc-1 dhcpd: DHCP-DNS Update succeeded Aug 30 16:56:24 ag-dc-1 dhcpd[10880]: DHCPREQUEST for 192.168.1.114 (192.168.1.100) from 00:0c:29:20:ce:d2 (client-lin-1) via ens160 Aug 30 16:56:24 ag-dc-1 dhcpd[10880]: DHCPACK on 192.168.1.114 to 00:0c:29:20:ce:d2 (client-lin-1) via ens160
- To begin with, install a DHCP server
-
DHCP Server for Linux AD-DC – Conclusion
That’s how we came to an end. Never forget that there is still a lot of room for improvements, but this option will allow the admin to have in a small office licensed Active Directory Domain Controller with DNS and DHCP servers that are able to serve not only computers attached to the domain, but all machines in general. This will allow you to get away from setting up local services to static ip addresses, and switch to using dynamic ip addresses in conjunction with device names dns
P.S. If you have multiple network interfaces configured to receive network settings on DHCP, they’ll all get customized. But the host team <device_name> will return the address of the last interface of the server that appealed to THE DHCP.</device_name>