Active Directory mit DNS und DHCP Failover auf Debian

Vorbereitungen

Ich betreibe zu Hause ein reines IPv4 Netzwerk, weshalb ich im ersten Schritt IPv6 deaktiviere, in dem ich in der Datei…

nano /etc/sysctl.conf

…am Ende folgende Zeile hinzufüge:

net.ipv6.conf.all.disable_ipv6 = 1

Bevor es an die Einrichtung geht, kommt das obligatorische Aktualisieren des Systems, was ich eigentlich immer mit diesem Befehl ausführe:

apt update -y && apt upgrade -y && apt clean -y && apt autoremove -y && reboot

Bevor es gleich an das eigentlich Installieren geht, prüfe ich vorab, ob der Server seinen Namen und seine IP Adresse richtig kennt.

nano /etc/hosts

Das sollte jetzt so aussehen.

127.0.0.1       localhost
192.168.178.11  dc1.intern.domain.tld dc1Code-Sprache: CSS (css)

Samba installieren

apt-get install acl attr samba samba-dsdb-modules samba-vfs-modules smbclient winbind libpam-winbind libnss-winbind libpam-krb5 krb5-config krb5-user dnsutilsCode-Sprache: JavaScript (javascript)

Möglicherweise wird bei der Installation folgendes abgefragt:

  • WINS Server: nein
  • Default Kerberos Realm: INTERN.DOMAIN.TLD
  • Kerberos Server: dc1.intern.domain.tld
  • Admin Server: dc1.intern.domain.tld

Bind (DNS) installieren

apt install bind9

Samba für Erstellung der Domäne vorbereiten

Zu aller erst sicherstellen, dass keine Samba oder DNS Dienste laufen.

ps ax | egrep "samba|smbd|nmbd|winbindd"Code-Sprache: JavaScript (javascript)

Die Ausgabe könnte wie folgt aussehen…

root@dc1:~# ps ax | egrep "samba|smbd|nmbd|winbindd"
  158 ?        Ss     0:00 /usr/sbin/smbd -D --option=server role check:inhibit=yes --foreground
  162 ?        Ss     0:03 /usr/sbin/winbindd -D --option=server role check:inhibit=yes --foreground
  219 ?        S      0:00 /usr/sbin/smbd -D --option=server role check:inhibit=yes --foreground
  220 ?        S      0:00 /usr/sbin/smbd -D --option=server role check:inhibit=yes --foreground
  232 ?        S      0:00 /usr/sbin/smbd -D --option=server role check:inhibit=yes --foreground
30976 pts/2    S+     0:00 grep -E samba|smbd|nmbd|winbinddCode-Sprache: PHP (php)

Die letzte Zeile zeigt unseren soeben gestarteten Suchprozess und alle anderen gehören zu Samba und DNS. Die Nummern der ersten Spalte sind die Prozess IDs (kurz PID), welche allesamt beendet werden müssen (PID durch die jeweilige Nummer ersetzen). Bei o.g. Beispiel:

kill -9 158 162 219 220 232

Desweiteren muss die vorhandene Konfigurationsdatei von Samba entfernt werden. Mit smbd -b | grep "CONFIGFILE" zeigt das System an, wo sich diese befindet.

rm /etc/samba/smb.conf

Auch müssen eventuell bereits bestehende Datenbanken von Samba entfernt werden. Mit smbd -b | egrep "LOCKDIR|STATEDIR|CACHEDIR|PRIVATE_DIR" werden die entsprechenden Pfade angezeigt.

root@dc1:~# smbd -b | egrep "LOCKDIR|STATEDIR|CACHEDIR|PRIVATE_DIR"
   LOCKDIR: /var/run/samba
   STATEDIR: /var/lib/samba
   CACHEDIR: /var/cache/samba
   PRIVATE_DIR: /var/lib/samba/privateCode-Sprache: PHP (php)

Mit diesen Befehlen habe ich alle Datenbanken bei mir entfernt:

rm /var/run/samba/*.tdb
rm /var/run/samba/*.ldb
rm /var/lib/samba/*.tdb
rm /var/lib/samba/*.ldb
rm /var/cache/samba/*.tdb
rm /var/cache/samba/*.ldb
rm /var/lib/samba/private/*.tdb
rm /var/lib/samba/private/*.ldbCode-Sprache: JavaScript (javascript)

Und da wir gerade so schön mit Löschen beschäftigt sind, entfernen wir auch die Konfiguration von Kerberos.

rm /etc/krb5.conf

Domäne erstellen

Um den Server zum Domaincontroller zu provisionieren, gibt es bei Samba den Befehl samba-tool domain provision, welcher während der Ausführung einige Parameter abfragt, aber nicht alle, um eine Windows taugliche Active Directory Domäne erstellen zu können.

samba-tool domain provision --use-rfc2307 --interactive --option="interfaces=lo eth0" --option="bind interfaces only=yes"Code-Sprache: JavaScript (javascript)

Bei den Abfragen müssen die ersten beiden Angaben in Großbuchstaben erfolgen. Die Abfrage „Domain“ entspricht dem NetBIOS Domänennamen. Bei einer Benutzeranmeldung kommt dieser Name vor den User (DOMAIN\user) und kann beliebig benannt werden (max. 15 Zeichen). Realm entspricht übrigens in diesem Beispiel „user@intern.domain.tld“.

Wichtig ist die Eingabe „BIND9_DLZ“ anstelle dem vorausgewählten „SAMBA INTERNAL“.
Das Administrator Kennwort entspricht dem späteren gleichnamigen Domänen-Admin.

Abfrage Realm: INTERN.DOMAIN.TLD
Abfrage Domain: DOMAIN
Abfrage Rolle: dc
Abfrage DNS Backend: BIND9_DLZ
Abfrage Admin Kennwort: *******

Samba hat bei der Erstellung der Domäne eine Konfigurationsdatei erstellt, welche wir noch an die richtige Stelle kopieren müssen.

cp /var/lib/samba/private/krb5.conf /etc/krb5.confCode-Sprache: PHP (php)

BIND9_DLZ aktivieren und anpassen

In der Regel sollte der folgende Schritt bei einer Neuinstallation überflüssig sein, wird jedoch in vielen anderen Beiträgen im Internet immer wieder erwähnt. Hier wird festgelegt, welches Shared Object für Bind verwendet werden soll.
Wir müssen hierfür die Version von Bind ermitteln…

named -v

Die Ausgabe beinhaltet hinter dem Wort BIND die Versionsnummer. In der Datei…

nano /var/lib/samba/bind-dns/named.confCode-Sprache: JavaScript (javascript)

…muss nun entsprechend der Pfad unkommentiert sein, welcher der BIND Version entspricht.

Beispiel:

# For BIND 9.11.x
database "dlopen /usr/lib/x86_64-linux-gnu/samba/bind9/dlz_bind9_11.so";Code-Sprache: PHP (php)

Wir sagen BIND, dass es nur auf IPv4 hören darf…

nano /etc/default/bind9Code-Sprache: JavaScript (javascript)

…und ändern die Zeile OPTIONS= wie folgt:

OPTIONS="-u bind"Code-Sprache: JavaScript (javascript)

…ändern in…

OPTIONS="-4 -u bind"Code-Sprache: JavaScript (javascript)

In der eigentlichen Konfigurationsdatei legen wir fest, aus welchen Netzen DNS Abfragen erfolgen dürfen und wohin unbekannte Anfragen weitergeleitet werden sollen. Entweder die bestehende Datei abändern oder zuvor mit rm /etc/bind/named.conf.options löschen oder mit mv /etc/bind/named.conf.options /etc/bind/named.conf.options.org umbenennen.

nano /etc/bind/named.conf.options
// Netze, aus denen zugegriffen werden darf - die erste ist der Server selbst
acl internals { 127.0.0.0/8; 192.168.178.0/24; };
options {
      directory "/var/cache/bind";
      version "Go Away 0.0.7";
      notify no;
      empty-zones-enable no;
      auth-nxdomain yes;
      // Weiterleitungen (hier Router und die bekannten DNS Server)
	  forwarders { 192.168.178.1; 1.1.1.1; 8.8.8.8; };
      // Hier kommt die IP des 2. Domaincontrollers rein
      allow-transfer { 192.168.178.12; };
      dnssec-validation no;
      dnssec-enable no;
      dnssec-lookaside no;
      // Nur IPv4 
      listen-on-v6 { none; };
      // Adressen, auf denen der DNS hört und arbeitet. 
      listen-on port 53 { 192.168.178.11; 127.0.0.1; ::1; };
      minimal-responses yes;
      //  Hier wird auf die o.g. Netze verwiesen
      allow-query { "internals";  };
      allow-query-cache { "internals"; };
      recursion yes;
      allow-recursion {  "internals"; };
      tkey-gssapi-keytab "/var/lib/samba/bind-dns/dns.keytab";
  };Code-Sprache: PHP (php)

In die Datei…

nano /etc/bind/named.conf.local

…muss jetzt noch auf die Samba BIND Konfiguration verwiesen werden, in dem folgende Zeile am Ende eingefügt wird:

include "/var/lib/samba/bind-dns/named.conf";Code-Sprache: PHP (php)

Jetzt starten wir den DNS bzw. BIND Dienst mit…

systemctl start bind9

…und testen seine Funktion mit den Zeilen:

host -t A localhost 127.0.0.1
host -t PTR 127.0.0.1 127.0.0.1Code-Sprache: CSS (css)

Die Ausgabe sollte wie folgt aussehen:

root@dc1:~# host -t A localhost 127.0.0.1
Using domain server:
Name: 127.0.0.1
Address: 127.0.0.1#53
Aliases:
localhost has address 127.0.0.1
root@dc1:~# host -t PTR 127.0.0.1 127.0.0.1
Using domain server:
Name: 127.0.0.1
Address: 127.0.0.1#53
Aliases:
1.0.0.127.in-addr.arpa domain name pointer localhost.Code-Sprache: PHP (php)

Sollte das nicht der Fall sein, es gegebenenfalls nach einem Neustart (reboot) noch einmal versuchen.

Nun die Namensauflösung des Server so ändern, dass er sich zuerst selbst fragt und nicht den Router. Bei einem PVE Container geht das über die PVE GUI unter dem Menüpunkt „DNS“ und einem nachfolgenden Neustart.

nano /etc/resolv.conf

So sieht das bei PVE aus…

# --- BEGIN PVE ---
search intern.domain.tld
nameserver 192.168.178.11
# --- END PVE ---Code-Sprache: CSS (css)

Neustarten (reboot) und mit nslookup beoceka.de prüfen, ob der lokale Server ordnungsgemäß über die oben eingegebenen Forwarder den Namen auflösen kann. Das sollte jetzt so aussehen:

root@dc1:~# nslookup beoceka.de
Server:         192.168.178.11
Address:        192.168.178.11#53
Non-authoritative answer:
Name:   beoceka.de
Address: 85.13.151.105Code-Sprache: PHP (php)

Sollte ein connection timed out kommen, mit service bind9 status den Status prüfen und korrigieren.
In der Regel sollte es ein Fehler in der /etc/bind/named.conf.options sein.

Jetzt müssen wir Samba starten – ja, Samba startet im Domänen Modus nicht automatisch. Befehle wie systemctl start smbd.service oder samba-ad-dc funktionierten bei keinem meiner Tests. Zum automatischen Starten kommen wir gegen Ende des Artikels.

samba

Mit Samba können wir jetzt eine Reverse Lookup Zone erstellen.

samba-tool dns zonecreate -U Administrator dc1.intern.domain.tld 178.168.192.in-addr.arpaCode-Sprache: CSS (css)

Freigaben und DNS testen

Jetzt können wir die Freigaben testen, die zur Nutzung des Domänencontrollers notwendig sind.

Einmal anonym mit smbclient -L localhost -N sollte es so aussehen:

Anonymous login successful

        Sharename       Type      Comment
        ---------       ----      -------
        netlogon        Disk      
        sysvol          Disk      
        IPC$            IPC       IPC Service (Samba 4.9.5-Debian)
Reconnecting with SMB1 for workgroup listing.
Anonymous login successful

        Server               Comment
        ---------            -------
        Workgroup            Master
        ---------            -------
        WORKGROUP            DC1Code-Sprache: JavaScript (javascript)

…und als Domänen-Admin sollte der Netlogon-Ordner mit smbclient //localhost/netlogon -UAdministrator -c 'ls' sollte es so aussehen:

Enter INTERN\Administrator's password: 
  .                                   D        0  Tue May  4 10:45:52 2021
  ..                                  D        0  Tue May  4 10:45:54 2021
  

Die DNS Einträge für Active Directory testen wir mit folgenden Befehlen:

host -t SRV _ldap._tcp.intern.domain.tld.Code-Sprache: CSS (css)
host -t SRV _kerberos._udp.intern.domain.tld.
Code-Sprache: CSS (css)
host -t A dc1.intern.domain.tld.Code-Sprache: CSS (css)

Die Ausgaben sollten wie folgt aussehen:

_ldap._tcp.intern.domain.tld has SRV record 0 100 389 dc1.intern.domain.tld.


_kerberos._udp.intern.domain.tld has SRV record 0 100 88 dc1.intern.domain.tld.

root@dc1:~# host -t A dc1.intern.domain.tld.
dc1.intern.domain.tld has address 192.168.178.11Code-Sprache: PHP (php)

Nun testen wir Kerberos mit kinit Administrator. Hierbei sollte keine Fehlermeldung erscheinen, außer der Hinweis, dass das Kennwort in einigen Tagen ablaufen wird.
Mit klist wird das Ticket angezeit:

Ticket cache: FILE:/tmp/krb5cc_0
Default principal: Administrator@INTERN.DOMAIN.TLD

Valid starting     Expires            Service principal
05/04/21 12:37:38  05/04/21 22:37:38  krbtgt/INTERN.DOMAIN.TLD@INTERN.DOMAIN.TLD
        renew until 05/05/21 12:37:35Code-Sprache: JavaScript (javascript)

Damit das Kennwort vom Administrator nicht mehr ablaufen kann, setzen wir einen entsprechenden Wert mit samba-tool user setexpiry Administrator --noexpiry.

Samba automatisch bei Reboot starten

Sobald Samba als Domaincontroller arbeitet, heißen die Dienste anders. Mit folgenden Zeilen wird das System dafür vorbereitet:

systemctl stop smbd nmbd winbind
systemctl disable smbd nmbd winbind
systemctl unmask samba-ad-dc
systemctl start samba-ad-dc
systemctl status samba-ad-dc

Um sicher zu gehen, dass bisher alles richtig funktioniert, einfach neustarten und die Tests von oben noch einmal durchführen.

DHCP installieren

apt install isc-dhcp-server

Damit der DHCP Server dem DNS Server neu vergebene IP Adressen mit Client Name und Client MAC Adresse mitteilen darf, benötigt dieser entsprechende Rechte. Da es sich bei diesem DNS um einen Active Directory Dienst handelt, brauchen wir einen AD-User mit entsprechenden Rechten.

samba-tool user create dhcpduser --description="Benutzer für TSIG-GSSAPI DNS Updates via ISC DHCP Server" --random-password
samba-tool user setexpiry dhcpduser --noexpiry
samba-tool group addmembers DnsAdmins dhcpduserCode-Sprache: JavaScript (javascript)

Wir exportieren den Benutzerschlüssel für die spätere Verwendung.

samba-tool domain exportkeytab --principal=dhcpduser@INTERN.DOMAIN.TLD /etc/dhcpduser.keytab
chown root:root  /etc/dhcpduser.keytab
chmod 400  /etc/dhcpduser.keytab

Für die Aktualisierung der DNS Einträge brauchen wir ein Skript, welches bei neuen DHCP Einträgen automatisch ausgeführt wird:

nano /usr/local/bin/dhcp-dyndns.sh
#!/bin/bash
# On FreeBSD change the above line to #!/usr/local/bin/bash
#
# /usr/local/bin/dhcp-dyndns.sh
#
# This script is for secure DDNS updates on Samba,
# it can also add the 'macAddress' to the Computers object.
#
# Version: 0.9.3
#
# Copyright (C) Rowland Penny 2020-2021
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
# Make sure we have a useful path
export PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin
##########################################################################
#                                                                        #
#    You can optionally add the 'macAddress' to the Computers object.    #
#    Add 'dhcpduser' to the 'Domain Admins' group if used                #
#    Change the next line to 'yes' to make this happen                   #
Add_macAddress='yes'
#                                                                        #
##########################################################################
# On FreeBSD change this to /usr/local/etc/dhcpduser.keytab
keytab=/etc/dhcpduser.keytab
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
# Check for valid kerberos ticket
#logger "${test} [dyndns] : Running check for valid kerberos ticket"
klist -c "${KRB5CCNAME}" -s
if [ "$?" != "0" ]; then
logger "${test} [dyndns] : Getting new ticket, old one has expired"
kinit -F -k -t $keytab "${SETPRINCIPAL}"
# On FreeBSD change the -F to --no-forwardable     
if [ "$?" != "0" ]; then
logger "${test} [dyndns] : dhcpd kinit for dynamic DNS failed"
exit 1
fi
fi
}
rev_zone_info () {
local RevZone="$1"
local IP="$2"
local rzoneip
rzoneip=$(echo "$RevZone" | sed 's/\.in-addr.arpa//')
local rzonenum
rzonenum=$(echo "$rzoneip" |  tr '.' '\n')
declare -a words
for n in $rzonenum
do
words+=("$n")
done
local numwords="${#words[@]}"
unset ZoneIP
unset RZIP
unset IP2add
case "$numwords" in
1) # single ip rev zone '192'
ZoneIP=$(echo "${IP}" | awk -F '.' '{print $1}')
RZIP="${rzoneip}"
IP2add=$(echo "${IP}" | awk -F '.' '{print $4"."$3"."$2}')
;;
2) # double ip rev zone '168.192'
ZoneIP=$(echo "${IP}" | awk -F '.' '{print $1"."$2}')
RZIP=$(echo "${rzoneip}" | awk -F '.' '{print $2"."$1}')
IP2add=$(echo "${IP}" | awk -F '.' '{print $4"."$3}')
;;
3) # triple ip rev zone '0.168.192'
ZoneIP=$(echo "${IP}" | awk -F '.' '{print $1"."$2"."$3}')
RZIP=$(echo "${rzoneip}" | awk -F '.' '{print $3"."$2"."$1}')
IP2add=$(echo "${IP}" | awk -F '.' '{print $4}')
;;
*) # should never happen
exit 1
;;
esac
}
BINDIR=$(samba -b | grep 'BINDIR' | grep -v 'SBINDIR' | awk '{print $NF}')
WBINFO="$BINDIR/wbinfo"
# DHCP Server hostname
Server=$(hostname -s)
# 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 realm
REALM=$(echo ${domain^^})
# krbcc ticket cache
export KRB5CCNAME="/tmp/dhcp-dyndns.cc"
# Kerberos principal
SETPRINCIPAL="dhcpduser@${REALM}"
# Kerberos keytab as above
# 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
logger "Required keytab $keytab not found, it needs to be created."
logger "Use the following commands as root"
logger "samba-tool domain exportkeytab --principal=${SETPRINCIPAL} $keytab"
logger "chown XXXX:XXXX $keytab"
logger "Replace 'XXXX:XXXX' with the user & group that dhcpd runs as on your distro"
logger "chmod 400 $keytab"
exit 1
fi
# Variables supplied by dhcpd.conf
action="$1"
ip="$2"
DHCID="$3"
name="${4%%.*}"
# Exit if no ip address or mac-address
if [ -z "${ip}" ]; then
usage
exit 1
fi
# Exit if no computer name supplied, unless the action is 'delete'
if [ -z "${name}" ]; then
if [ "${action}" = "delete" ]; then
name=$(host -t PTR "${ip}" | awk '{print $NF}' | awk -F '.' '{print $1}')
else
usage
exit 1
fi
fi
# exit if name contains a space
case ${name} in
*\ * ) logger "Invalid hostname '${name}' ...Exiting"
exit
;;
esac
# exit if $name starts with 'dhcp'
# if you do not want computers without a hostname in AD
# uncomment the following block of code.
#if [[ $name == dhcp* ]]; then
#    logger "not updating DNS record in AD, invalid name"
#    exit 0
#fi
## update ##
case "${action}" in
add)
_KERBEROS
count=0
# does host have an existing 'A' record ?
A_REC=$(samba-tool dns query ${Server} ${domain} ${name} A -k yes 2>/dev/null | grep 'A:' | awk '{print $2}')
if [[ -z $A_REC ]]; then
# no A record to delete
result1=0
samba-tool dns add ${Server} ${domain} "${name}" A ${ip} -k yes
result2="$?"
elif [ "$A_REC" = "${ip}" ]; then
# Correct A record exists, do nothing
logger "Correct 'A' record exists, not updating."
result1=0
result2=0
count=$((count+1))
elif [ "$A_REC" != "${ip}" ]; then
# Wrong A record exists
logger "'A' record changed, updating record."
samba-tool dns delete ${Server} ${domain} "${name}" A ${A_REC} -k yes
result1="$?"
samba-tool dns add ${Server} ${domain} "${name}" A ${ip} -k yes
result2="$?"
fi
# get existing reverse zones (if any)
ReverseZones=$(samba-tool dns zonelist ${Server} -k yes --reverse | grep 'pszZoneName' | awk '{print $NF}')
if [ -z "$ReverseZones" ]; then
logger "No reverse zone found, not updating"
result3='0'
result4='0'
count=$((count+1))
else
for revzone in $ReverseZones
do
rev_zone_info "$revzone" "${ip}"
if [[ ${ip} = $ZoneIP* ]] && [ "$ZoneIP" = "$RZIP" ]; then
# does host have an existing 'PTR' record ?
PTR_REC=$(samba-tool dns query ${Server} ${revzone} ${IP2add} PTR -k yes 2>/dev/null | grep 'PTR:' | awk '{print $2}' | awk -F '.' '{print $1}')
if [[ -z $PTR_REC ]]; then
# no PTR record to delete
result3=0
samba-tool dns add ${Server} ${revzone} ${IP2add} PTR "${name}".${domain} -k yes
result4="$?"
break
elif [ "$PTR_REC" = "${name}" ]; then
# Correct PTR record exists, do nothing
logger "Correct 'PTR' record exists, not updating."
result3=0
result4=0
count=$((count+1))
break
elif [ "$PTR_REC" != "${name}" ]; then
# Wrong PTR record exists
# points to wrong host
logger "'PTR' record changed, updating record."
samba-tool dns delete ${Server} ${revzone} ${IP2add} PTR "${PTR_REC}".${domain} -k yes
result3="$?"
samba-tool dns add ${Server} ${revzone} ${IP2add} PTR "${name}".${domain} -k yes
result4="$?"
break
fi
else
continue
fi
done
fi
;;
delete)
_KERBEROS
count=0
samba-tool dns delete ${Server} ${domain} "${name}" A ${ip} -k yes
result1="$?"
# get existing reverse zones (if any)
ReverseZones=$(samba-tool dns zonelist ${Server} --reverse -k yes | grep 'pszZoneName' | awk '{print $NF}')
if [ -z "$ReverseZones" ]; then
logger "No reverse zone found, not updating"
result2='0'
count=$((count+1))
else
for revzone in $ReverseZones
do
rev_zone_info "$revzone" "${ip}"
if [[ ${ip} = $ZoneIP* ]] && [ "$ZoneIP" = "$RZIP" ]; then
host -t PTR ${ip} > /dev/null 2>&1
if [ "$?" -eq 0 ]; then
samba-tool dns delete ${Server} ${revzone} ${IP2add} PTR "${name}".${domain} -k yes
result2="$?"
else
result2='0'
count=$((count+1))
fi
break
else
continue
fi
done
fi
result3='0'
result4='0'
;;
*)
logger "Invalid action specified"
exit 103
;;
esac
result="${result1}:${result2}:${result3}:${result4}"
if [ "$count" -eq 0 ]; then
if [ "${result}" != "0:0:0:0" ]; then
logger "DHCP-DNS $action failed: ${result}"
exit 1
else
logger "DHCP-DNS $action succeeded"
fi
fi
if [ "$Add_macAddress" != 'no' ]; then
if [ -n "$DHCID" ]; then
Computer_Object=$(ldbsearch -k yes -H ldap://"$Server" "(&(objectclass=computer)(objectclass=ieee802Device)(cn=$name))" | grep -v '#' | grep -v 'ref:')
if [ -z "$Computer_Object" ]; then
# Computer object not found with the 'ieee802Device' objectclass, does the computer actually exist, it should.
Computer_Object=$(ldbsearch -k yes -H ldap://"$Server" "(&(objectclass=computer)(cn=$name))" | grep -v '#' | grep -v 'ref:')
if [ -z "$Computer_Object" ]; then
logger "Computer '$name' not found. Exiting."
exit 68
else
DN=$(echo "$Computer_Object" | grep 'dn:')
objldif="$DN
changetype: modify
add: objectclass
objectclass: ieee802Device"
attrldif="$DN
changetype: modify
add: macAddress
macAddress: $DHCID"
# add the ldif
echo "$objldif" | ldbmodify -k yes -H ldap://"$Server"
ret="$?"
if [ "$ret" -ne 0 ]; then
logger "Error modifying Computer objectclass $name in AD."
exit "${ret}"
fi
sleep 2
echo "$attrldif" | ldbmodify -k yes -H ldap://"$Server"
ret="$?"
if [ "$ret" -ne 0 ]; then
logger "Error modifying Computer attribute $name in AD."
exit "${ret}"
fi
unset objldif
unset attrldif
logger "Successfully modified Computer $name in AD"
fi
else
DN=$(echo "$Computer_Object" | grep 'dn:')
attrldif="$DN
changetype: modify
replace: macAddress
macAddress: $DHCID"
echo "$attrldif" | ldbmodify -k yes -H ldap://"$Server"
ret="$?"
if [ "$ret" -ne 0 ]; then
logger "Error modifying Computer attribute $name in AD."
exit "${ret}"
fi
unset attrldif
logger "Successfully modified Computer $name in AD"
fi
fi
fi
exit 0
Code-Sprache: PHP (php)

Mit chmod 755 /usr/local/bin/dhcp-dyndns.sh machen wir das Skript ausführbar.

Damit der DHCP Server nur auf IPv4 und nur auf dem Interface eth0 antwortet, brauchen wir mit nano /etc/default/isc-dhcp-server folgende Änderung:

INTERFACESv4="eth0"
#INTERFACESv6=""Code-Sprache: PHP (php)

Die originale Konfigurationsdatei des DHCP Servers sichern wir mit mv /etc/dhcp/dhcpd.conf /etc/dhcp/dhcpd.conf.orig weg und erstellen mit nano /etc/dhcp/dhcpd.conf eine neue mit folgendem Inhalt:

authoritative;
ddns-update-style none;
subnet 192.168.178.0 netmask 255.255.255.0 {
option subnet-mask 255.255.255.0;
option broadcast-address 192.168.178.255;
option time-offset 0;
option routers 192.168.178.1;
option domain-name "intern.domain.tld";
option domain-search "intern.domain.tld";
option domain-name-servers 192.168.178.11, 192.168.178.12;
option ntp-servers 192.168.178.11, 192.168.178.12;
pool {
max-lease-time 1800; # 30 minutes
range 192.168.178.50 192.168.178.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("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);
}
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
# however the dhcp update script will obtain the short hostname.
execute("/usr/local/bin/dhcp-dyndns.sh", "delete", ClientIP, "", "0");
}
Code-Sprache: PHP (php)

Jetzt starten wir den DHCP Server mit /etc/init.d/isc-dhcp-server restart und schauen, ob er startet oder Fehler auswirft. Bei meinen Tests kam es nur zu Fehlern, wenn ich ein Zeichen vergessen habe. Das wird mit journalctl -xe aber angezeigt.

Fehler wie: „May 05 10:33:05 dc1 dhcpd[3272]: execute: /usr/local/bin/dhcp-dyndns.sh exit status 17408“ bedeuten, dass sich ein Windows Client bereits selbst im DNS eingetragen hat. Dieser Fehler kann ignoriert werden.

Dienste automatisch starten

DHCP und BIND

systemctl enable bind9
systemctl enable isc-dhcp-server

Einen lauffähigen Domaincontroller mit DHCP und DNS haben wir jetzt. Zum Testen habe ich in meiner Proxmox Umgebung eine Debian- und eine Windows 10 Maschine angelegt.
Auf der nächsten Seite geht des mit dem zweiten DC weiter…

2 Kommentare zu „Active Directory mit DNS und DHCP Failover auf Debian“

  1. Hallo, vielen Dank für die super Anleitung. Leider bekomme ich beim Absetzen des Befehls
    database „dlopen /usr/lib/x86_64-linux-gnu/samba/bind9/dlz_bind9_11.so“;
    den Fehler: database: Kommando nicht gefunden.
    Finde auch leider keine Infos zu dem fehlenden Prog. database
    Wäre toll wenn Du mir dazu einen Tipp geben kannst.
    VG

Kommentar verfassen

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert