Das Problem

Montag morgen. Nextcloud zeigt 502 Bad Gateway. Die Ursache ist schnell gefunden: Der CIFS-Mount auf srv-r2d2 hängt. Kein Wunder – der Fileserver versucht noch, sich bei dc-vader zu authentifizieren. Der wurde aber am Wochenende demoted.

root@srv-r2d2:~# mount | grep cifs
//srv-r2d2.darkside.local/daten on /mnt/daten type cifs (sec=krb5)

root@srv-r2d2:~# ls /mnt/daten
ls: cannot access '/mnt/daten': Host is down

Der Nextcloud-Container auf srv-chewbacca (192.168.66.48) bindet diesen Mount via NFS ein. Kein Mount, kein Dateizugriff, 502.

Erste Reaktion: “Klar, der Memberserver muss nur den neuen DC kennen.” Klingt simpel. Ist es nicht.

Die Diagnose

UCR zeigt auf den Toten

root@srv-r2d2:~# ucr get ldap/server/name
dc-vader.darkside.local

root@srv-r2d2:~# ucr get kerberos/kdc
dc-vader.darkside.local

root@srv-r2d2:~# ucr get nameserver1
192.168.66.1

Alle UCR-Variablen zeigen noch auf dc-vader. Der ist aber nicht mehr da.

AD-Join OK, SMB trotzdem kaputt

root@srv-r2d2:~# net ads testjoin
Join is OK

root@srv-r2d2:~# net ads info
LDAP server: 192.168.66.2
LDAP server name: dc-yoda.darkside.local
Realm: DARKSIDE.LOCAL

Kerberos und AD sehen dc-yoda korrekt – DNS liefert den neuen DC. Aber:

root@srv-r2d2:~# smbclient -k //srv-r2d2.darkside.local/daten -c ls
session setup failed: NT_STATUS_ACCESS_DENIED

LDAP-Machine-Bind schlaegt fehl

root@srv-r2d2:~# ldapsearch -x -H ldaps://dc-yoda.darkside.local:7636 \
  -D "$(ucr get ldap/hostdn)" -y /etc/machine.secret \
  -b "dc=darkside,dc=local" "(cn=srv-r2d2)"
ldap_bind: Invalid credentials (49)
Additional info: 80090308: LdapErr: DSID-0C09044E, comment: AcceptSecurityContext error, data 52e

Error 52e = falsches Passwort. Aber /etc/machine.secret wurde nicht geaendert. Das Problem: Der Memberserver hat ein Machine-Passwort im UCS-LDAP, und nach dem Demote des alten DCs stimmt das nicht mehr mit dem ueberein, was dc-yoda kennt.

Der erste Versuch: net ads join

Die offensichtliche Loesung:

root@srv-r2d2:~# net ads join -U luke.skywalker
Enter luke.skywalker's password:
Using short domain name -- DARKSIDE
Joined 'SRV-R2D2' to dns domain 'darkside.local'

Sieht gut aus. Kerberos funktioniert jetzt:

root@srv-r2d2:~# kinit luke.skywalker@DARKSIDE.LOCAL
Password for luke.skywalker@DARKSIDE.LOCAL:
root@srv-r2d2:~# klist
Ticket cache: FILE:/tmp/krb5cc_0
Default principal: luke.skywalker@DARKSIDE.LOCAL

Aber SMB?

root@srv-r2d2:~# smbclient -k //srv-r2d2.darkside.local/daten -c ls
session setup failed: NT_STATUS_ACCESS_DENIED

Immer noch ACCESS_DENIED. Der LDAP-Machine-Bind scheitert weiterhin mit 52e.

Warum? net ads join repariert nur die Samba AD-Seite (Kerberos, Machine-Account im AD). Es aktualisiert aber nicht den UCS-LDAP – und dort haengt die Machine-Authentifizierung des Memberservers.

Das ist dasselbe Dual-Directory-Problem wie beim DC-Shadow-Context: Samba AD und UCS-LDAP sind zwei getrennte Welten.

Die echte Loesung: univention-join

Der Memberserver muss sich komplett neu bei dc-yoda registrieren – mit univention-join. Aber der Weg dahin ist gepflastert mit TLS-Fallen, besonders wenn du Step-CA statt der UCS-CA nutzt.

Vorbereitung: UCR auf neuen DC umstellen

root@srv-r2d2:~# ucr set \
  ldap/server/name=dc-yoda.darkside.local \
  ldap/server/ip=192.168.66.2 \
  ldap/master=dc-yoda.darkside.local \
  kerberos/kdc=dc-yoda.darkside.local \
  nameserver1=192.168.66.2

Huerden auf dem Weg

1. SSH Host Key Mismatch

root@srv-r2d2:~# univention-join -dcname dc-yoda.darkside.local \
  -dcaccount luke.skywalker
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@    WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!     @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

univention-join verbindet sich per SSH zum DC. Wenn du den DC neu aufgesetzt hast (nicht promoted, sondern frisch installiert), hat er einen neuen Host Key. Der alte Key von dc-vader liegt noch in known_hosts – aber auch wenn dc-yoda den gleichen Hostnamen-Eintrag hat, kann der Key von einer frueheren Installation stammen.

ssh-keygen -R dc-yoda.darkside.local
ssh-keygen -R 192.168.66.2

2. SSL Certificate Verify Failed

root@srv-r2d2:~# univention-join -dcname dc-yoda.darkside.local \
  -dcaccount luke.skywalker
...
SSL error: certificate verify failed (self signed certificate in certificate chain)

Du nutzt Step-CA (“Rebellion Internal CA”) statt der UCS-eigenen CA. UCS erwartet aber seine eigene CA unter /etc/univention/ssl/ucsCA/CAcert.pem.

# Step-CA Root-Zertifikat als UCS-CA hinterlegen
cp /usr/local/share/ca-certificates/rebellion-internal-ca.crt \
   /etc/univention/ssl/ucsCA/CAcert.pem

# System-Trust aktualisieren
update-ca-certificates

3. ldap.conf TLS_CACERT hardcoded auf UCS-CA

Selbst nach dem Austausch der CA-Datei kann es scheitern. Der Grund:

root@srv-r2d2:~# grep TLS_CACERT /etc/ldap/ldap.conf
TLS_CACERT /etc/univention/ssl/ucsCA/CAcert.pem

Das ist OK wenn du Schritt 2 gemacht hast. Aber Achtung: Manche UCS-Versionen haben zusaetzlich eine TLS_REQCERT demand Zeile. Falls dein Step-CA-Zertifikat einen anderen CN/SAN hat als der DC-Hostname:

# Temporaer zum Testen (NICHT dauerhaft!)
# TLS_REQCERT allow

# Besser: Sicherstellen dass das Zertifikat den FQDN enthaelt
openssl s_client -connect dc-yoda.darkside.local:7636 </dev/null 2>/dev/null | \
  openssl x509 -noout -text | grep -A1 "Subject Alternative Name"

4. Passwort-Datei: echo vs printf

univention-join kann ein Passwort aus einer Datei lesen. Klassischer Fehler:

# FALSCH - echo haengt ein \n an
echo "geheim123" > /tmp/join-pw.txt

# RICHTIG - printf ohne Newline
printf '%s' "geheim123" > /tmp/join-pw.txt

# Kontrolle
xxd /tmp/join-pw.txt | tail -1
# Darf NICHT mit 0a enden

Das trailing Newline fuehrt zu Invalid credentials und du suchst stundenlang am falschen Ende.

5. Join Script 20: AddressSanitizer Bug

root@srv-r2d2:~# univention-join -dcname dc-yoda.darkside.local \
  -dcaccount luke.skywalker
...
Running join script 20univention-directory-listener...
==12345==ERROR: AddressSanitizer: heap-buffer-overflow
SUMMARY: AddressSanitizer: heap-buffer-overflow ...

Das ist ein bekannter UCS-Bug in bestimmten Versionen. Der AddressSanitizer in univention-directory-listener crasht, aber der eigentliche Join ist zu dem Zeitpunkt bereits abgeschlossen.

# Pruefen ob der Join trotzdem erfolgreich war
univention-check-join-status
# "Joined successfully"

# Falls das Script haengt: Ctrl+C und pruefen
cat /var/univention-join/status

Du kannst das fehlgeschlagene Join-Script spaeter einzeln nachholen:

univention-run-join-scripts --force --run-scripts 20univention-directory-listener

Der eigentliche Join

Wenn alle TLS-Huerden beseitigt sind:

root@srv-r2d2:~# univention-join -dcname dc-yoda.darkside.local \
  -dcaccount luke.skywalker -dcpwd /tmp/join-pw.txt

* Running pre-join hooks ...
* Checking connection to dc-yoda.darkside.local ...
* Setting up SSL ...
* Running join scripts ...
   20univention-directory-listener ...     [AddressSanitizer, ignoriert]
   26univention-samba ...                  done
   30univention-pam ...                    done
   ...
* Join successful

Die Ueberraschung: UCR-Revert

Du hast vor dem Join brav die UCR-Variablen auf dc-yoda umgestellt. Du hast den Join erfolgreich durchgefuehrt. Du lehnst dich zurueck. Und dann:

root@srv-r2d2:~# ucr get ldap/server/name
dc-vader.darkside.local

Was?! univention-join hat die UCR-Variablen zurueckgesetzt! Der Join-Prozess liest die “offizielle” Konfiguration aus dem LDAP und schreibt sie in die UCR. Wenn der alte DC noch als ldap/server/name im LDAP-Directory steht (weil niemand ihn dort entfernt hat), wird er froehlich wieder eingetragen.

# Nach dem Join: UCR ERNEUT korrigieren
ucr set \
  ldap/server/name=dc-yoda.darkside.local \
  ldap/server/ip=192.168.66.2 \
  ldap/master=dc-yoda.darkside.local \
  kerberos/kdc=dc-yoda.darkside.local \
  nameserver1=192.168.66.2

# Und diesmal auch im LDAP aufraeumen
# Auf dc-yoda: alten DC-Eintrag entfernen/aktualisieren
ssh root@dc-yoda.darkside.local 'udm computers/domaincontroller_master list'

Trockener Kommentar: univention-join ist wie ein Azubi, der die Akten alphabetisch umsortiert, nachdem du sie chronologisch geordnet hast. Technisch korrekt, praktisch kontraproduktiv.

Bonus: slapd TLS-Chain mit Step-CA

Falls dein dc-yoda ein TLS-Zertifikat von der “Rebellion Internal CA” (Step-CA) hat, stolperst du ueber ein weiteres Problem: slapd sendet nur das Leaf-Zertifikat, nicht die Intermediate-Chain.

# Was der Client sieht
openssl s_client -connect dc-yoda.darkside.local:7636 -showcerts </dev/null 2>/dev/null
# Nur 1 Zertifikat in der Ausgabe -- das Leaf

# Was der Client braucht
# Root-CA -> Intermediate -> Leaf (vollstaendige Chain)

Der Client kann das Leaf-Zertifikat nicht verifizieren, weil ihm die Intermediate fehlt. Die Loesung: CAcert.pem muss die gesamte Chain enthalten.

# Auf dc-yoda: Combined Bundle erstellen
cat /etc/step-ca/certs/intermediate_ca.crt \
    /etc/step-ca/certs/root_ca.crt \
    > /etc/univention/ssl/ucsCA/CAcert.pem

# slapd-Zertifikat muss ebenfalls die Chain enthalten
cat /etc/univention/ssl/dc-yoda.darkside.local/cert.pem \
    /etc/step-ca/certs/intermediate_ca.crt \
    > /etc/univention/ssl/dc-yoda.darkside.local/cert-chain.pem

# slapd-Konfiguration anpassen
ucr set ldap/server/tls/certificate=/etc/univention/ssl/dc-yoda.darkside.local/cert-chain.pem

systemctl restart slapd

Danach pruefen:

openssl s_client -connect dc-yoda.darkside.local:7636 -showcerts </dev/null 2>/dev/null | \
  grep -c "BEGIN CERTIFICATE"
# Erwartet: 2 oder 3 (Leaf + Intermediate, optional Root)

Lessons Learned

1. net ads join != univention-join

net ads join repariert nur die Samba AD/Kerberos-Seite. Fuer UCS-Memberserver brauchst du univention-join, weil dieser auch den UCS-LDAP (Machine-Account, Policies, UCR) aktualisiert.

2. Step-CA mit UCS ist ein Minenfeld

UCS erwartet seine eigene CA ueberall. Wenn du Step-CA nutzt, musst du an mindestens drei Stellen eingreifen: CAcert.pem, ldap.conf und die slapd-Chain.

3. univention-join revertiert UCR-Variablen

Nach dem Join IMMER pruefen ob die UCR-Variablen noch korrekt sind. Der Join liest die “offizielle” Konfiguration aus dem LDAP – und die kann veraltet sein.

4. printf, nicht echo

Passwort-Dateien mit echo erzeugen ist ein Klassiker-Fehler. Das trailing Newline ist unsichtbar und fuehrt zu stundenlangem Debugging.

5. AddressSanitizer in Join-Scripts ist ein UCS-Bug

Nicht dein Problem. Pruefen ob der Join trotzdem erfolgreich war, fehlgeschlagene Scripts spaeter einzeln nachholen.

6. CIFS-Mounts nach DC-Wechsel neu aufbauen

umount -l /mnt/daten
mount -a
# oder spezifisch
mount -t cifs //srv-r2d2.darkside.local/daten /mnt/daten -o sec=krb5

Checkliste: Nach DC-Demote auf Memberservern

- [ ] DNS pruefen: `dig dc-yoda.darkside.local` loest korrekt auf?
- [ ] UCR umstellen: ldap/server/name, ldap/master, kerberos/kdc, nameserver1
- [ ] SSH known_hosts bereinigen: `ssh-keygen -R <alter-dc>`
- [ ] TLS pruefen: Step-CA in /etc/univention/ssl/ucsCA/CAcert.pem?
- [ ] TLS_CACERT in /etc/ldap/ldap.conf korrekt?
- [ ] Passwort-Datei: `xxd` pruefen, kein trailing 0x0a?
- [ ] `univention-join -dcname <neuer-dc> -dcaccount <admin>`
- [ ] Nach Join: UCR-Variablen ERNEUT pruefen (Revert!)
- [ ] `univention-check-join-status` -> "Joined successfully"
- [ ] Fehlgeschlagene Join-Scripts nachholen
- [ ] LDAP-Machine-Bind testen: `ldapsearch -D "$(ucr get ldap/hostdn)" -y /etc/machine.secret`
- [ ] SMB-Zugriff testen: `smbclient -k //srv-r2d2.darkside.local/daten -c ls`
- [ ] CIFS-Mounts neu einhaengen: `umount -l && mount -a`
- [ ] Abhaengige Dienste pruefen (Nextcloud, Docker-Container auf srv-chewbacca)
- [ ] Im LDAP alten DC-Eintrag entfernen (sonst revertiert der naechste Join wieder)

Getestet mit UCS 5.0 Memberserver, Samba 4.18, Step-CA 0.27. Die Kombination aus DC-Demote + Step-CA + Memberserver-Rejoin ist in der Univention-Dokumentation nicht beschrieben. Jetzt schon.