1. Index
  2. Debian
  3. Desktop
  4. Heimserver
  5. Webserver

Benutzer verwalten

OpenLDAP speichert Benutzerdaten in einer hierarchischen Datenbank, dem sogenannten Directory Information Tree (DIT). Pfade heißen Distinguished Name (DN) und werden vom Blatt zur Wurzel notiert, zum Beispiel uid=phrank,ou=users,dc=illusioni,dc=de. Jedem Eintrag sind eine oder mehrere Objektklassen zugeordnet, die festlegen, welche Name-Wert-Paare er enthalten kann oder muss. Das Schema definiert die verfügbaren Objektklassen.

Pakete installieren

Es werden sowohl Pakete für den Daemon und die zugehörigen Werkzeuge installiert als auch für die Authentifizierung gegen Kerberos und die NSS-Integration.

apt install slapd ldap-utils libsasl2-modules-gssapi-mit libnss-ldap nslcd

Der empfohlene Unterbau ist die LMDB, eine moderne Name-Wert-Datenbank. Die Rohdaten liegen unter /var/lib/ldap/data.mdb. Um in den folgenden Abschnitten Tipparbeit zu sparen, merken wir uns die Eckdaten in Umgebungsvariablen.

domain=illusioni.de
realm=ILLUSIONI.DE
base="dc=illusioni,dc=de"
bind="-D cn=admin,$base -w Password"
backend="olcDatabase={1}mdb,cn=config"

Clients konfigurieren

Damit Clients den LDAP-Server finden, hinterlegen wir die Daten in einer Konfigurationsdatei. Da alle Dienste auf dem selben Rechner laufen, kommunizieren sie per ldapi über den lokalen Sockel /var/run/slapd/ldapi.

/etc/ldap/ldap.conf
BASE dc=illusioni,dc=de URI ldapi:/// SASL_MECH GSSAPI
ANONYMOUS Ohne Authentifizierung
EXTERNAL Systembenutzer
PLAIN Passwort im Klartext
GSSAPI Kerberos
CRAM-MD5 Aufforderung-Antwort-Verfahren
DIGEST-MD5 …mit Integritätssicherung
SCRAM …in Modern
OTP Einmalkennwort

Da der Daemon Benutzer auch via Kerberos authentifizieren soll, braucht er eine Schlüsseldatei. Diese kann allerdings erst später erzeugt werden, sobald Kerberos fertig eingerichtet ist.

echo "export KRB5_KTNAME=/etc/krb5/slapd.keytab" >> /etc/default/slapd

Zugriff gewähren

Der Verzeichnisdienst hat einen eigenen Administrator mit uneingeschränkten Rechten, der meistens admin heißt. Dieses Konto sollte man ausschließlich für die Verwaltung verwenden, aber niemals als Zugang für Daemonen.

ldapmodify -Y EXTERNAL << .
dn: $backend
replace: olcRootDn
olcRootDn: cn=admin,$base
-
replace: olcRootPW
olcRootPW: $(slappasswd -h {SSHA} -s Password)
.

Die Zugriffskontrollliste wird von oben nach ausgewertet. Standardmäßig dürfen authentifizierte Benutzer alles lesen (außer Passwörter) und ihr eigenes Passwort ändern.

ldapsearch -Y EXTERNAL -b "$backend" olcAccess
dn: $backend
olcAccess: {0}to attrs=userPassword
  by self write
  by anonymous auth
  by * none
olcAccess: {1}to attrs=shadowLastChange
  by self write
  by * read
olcAccess: {2}to *
  by * read

Wir geben nun dem Replikationsdienst vollen Schreibzugriff. Der KDC-Server bekommt Lesezugriff und der Admin-Server Schreibzugriff auf den Kerberos-Teilbaum. Angemeldete Benutzer dürfen ihr Passwort, ihre Shell und ihren Namen selbst ändern.

ldapmodify -Y EXTERNAL << .
dn: $backend
changetype: modify
replace: olcAccess
olcAccess: {0}to *
  by dn="cn=repl,ou=daemons,$base" write
  by * break
olcAccess: {1}to dn.subtree="cn=kerberos,$base"
  by dn="cn=adm,ou=daemons,$base" write
  by dn="cn=kdc,ou=daemons,$base" read
  by * none
olcAccess: {2}to attrs=userPassword
  by self write
  by anonymous auth
  by * none
olcAccess: {3}to attrs=shadowLastChange,loginShell,gecos
  by self write
  by * read
olcAccess: {4}to *
  by * read
.

Die GSSAPI bildet aus den Anmeldaten etwas eigenwillige DNs in der Form

uid=User,cn=www.illusioni.de,cn=gssapi,cn=auth

die man mit Hilfe eines regulären Ausdrucks an die eigene Systematik anpassen kann.

ldapadd -Y EXTERNAL << .
dn: cn=config
olcAuthzRegexp:
  uid=([^,]+),cn=[a-z.]*$domain,cn=gssapi,cn=auth
  uid=\$1,ou=users,$base
olcSaslRealm: $realm
.

Ein kleiner Test zeigt, ob die Umwandlung klappt.

slapauth -v -M GSSAPI -R $realm User
ID: <phrank> check succeeded
authcID:     <uid=phrank,ou=users,dc=illusioni,dc=de>

Kommunikation verschlüsseln

Damit Passwörter und Daten nicht im Klartext das Netzwerk passieren, verschlüsseln wir die Verbindung mit TLS. Dazu erzeugen wir einen Satz Diffie-Hellmann-Parameter für den Schlüsselaustausch und unsere Zertifizierungsstelle stellt ein Serverzertifikat aus. Das kopieren wir nach /etc/ssl und schränken den Lesezugriff auf Mitglieder der Gruppe ssl-cert ein.

openssl dhparam -out /etc/ssl/dh1024.pem 1024
chmod g+r            /etc/ssl/$HOSTNAME.{key,crt}
chgrp ssl-cert       /etc/ssl/$HOSTNAME.{key,crt}
adduser openldap ssl-cert

Als nächstes teilen wir dem Server die Pfade zu Schlüsseln und Zertifikaten mit.

ldapmodify -Y EXTERNAL << .
dn: cn=config
changetype: modify
add: olcTLSDHParamFile
olcTLSDHParamFile: /etc/ssl/dh1024.pem
-
add: olcTLSCACertificateFile
olcTLSCACertificateFile: /etc/ssl/ca.crt
-
add: olcTLSCertificateFile
olcTLSCertificateFile: /etc/ssl/$HOSTNAME.crt
-
add: olcTLSCertificateKeyFile
olcTLSCertificateKeyFile: /etc/ssl/$HOSTNAME.key
.

Verschlüsselung erzwingen.

ldapmodify -Y EXTERNAL << .
dn: olcDatabase={1}mdb,cn=config
changeType: modify
add: olcSecurity
olcSecurity: tls=1

Standardmäßig sind nur die Objektklassen indiziert.

ldapsearch -Y EXTERNAL -b "$backend" olcDbIndex
olcDbIndex: objectClass eq

Um spätere Anfragen zu beschleunigen und lästige Meldungen im Systemprotokoll zu vermeiden, erzeugen wir Suchbäume für viele weitere Attribute.

ldapmodify -Y EXTERNAL << .
dn: $backend
changetype: modify
replace: olcDbIndex
olcDbIndex: objectClass eq
olcDbIndex: entryCSN eq
olcDbIndex: cn  pres,sub,eq
olcDbIndex: uid pres,sub,eq
olcDbIndex: sn  pres,sub,eq
olcDbIndex: displayName pres,sub,eq
olcDbIndex: default sub
olcDbIndex: uidNumber eq
olcDbIndex: gidNumber eq
olcDbIndex: dc eq
olcDbIndex: member eq
olcDbIndex: mail,givenName eq,subinitial
olcDbIndex: rfc822MailMember,memberUid eq
olcDbIndex: krbPrincipalName eq,pres,sub
olcDbIndex: krbPwdPolicyReference eq
.

Datenbank replizieren

LDAP erlaubt ausgefeilte Architekturen zur Lastverteilung und Ausfallsicherheit. Dieser Abschnitt beschreibt nur die Spiegelung des Datenbestandes zwischen zwei gleichberechtigten Servern, den sogenannten Mirror Mode. Alle beteiligten Knoten werden gleich konfiguriert, außer einer eindeutige Identifikation und der jeweiligen Adresse des Partner-Knotens.

id=1;host=durmstrang;peer=ilvermorny;replpw=Password \
	ldapmodify -Y EXTERNAL << .
# Replikations-Modul laden
dn: cn=module{0},cn=config
changetype: modify
add: olcModuleLoad
olcModuleLoad: syncprov

# Replikations-Anbieter konfigurieren
dn: olcOverlay=syncprov,$backend
changeType: add
objectClass: olcOverlayConfig
objectClass: olcSyncProvConfig
olcOverlay: syncprov
olcSpCheckpoint: 100 10
olcSpSessionLog: 100

# Server-ID eindeutig festlegen
dn: cn=config
changeType: modify
add: olcServerID
olcServerID: $id

# Replikation aktivieren
dn: $backend
changeType: modify
add: olcSyncrepl
olcSyncrepl: rid=001
  provider=ldap://$peer
  bindmethod=simple
  binddn=cn=repl,ou=daemons,$base
  credentials=$replpw
  searchbase=$base
  schemachecking=on
  type=refreshAndPersist
  retry="60 +"
  starttls=yes
  tls_cacert=/etc/ssl/ca.crt
  tls_cert=/etc/ssl/$HOSTNAME.crt
  tls_key=/etc/ssl/$HOSTNAME.key
-
add: olcMirrorMode
olcMirrorMode: TRUE
.

Alle Schritte bis hierher muss man auf allen an der Replikation beteiligten Rechnern durchführen. Die Konfiguration selbst wird nicht repliziert, nur die Daten, die wir in den folgenden Abschnitten füllen.

Datenstruktur anlegen

Zuerst ergänzen wir das Schema um Objektklassen für Domänen-Namen und Elektropost-Adressen.

ldapadd -Y EXTERNAL -f /etc/ldap/schema/misc.ldif

Dann legen wir Organisationseinheiten für virtuelle Domänen, Elektropost-Verteiler, Daemonen, Gruppen und Benutzer an.

for orga in domains aliases groups users daemons; do ldapadd $bind << .
dn: ou=$orga,$base
objectClass: organizationalUnit
ou: daemons
.
done

Es ist heute üblich, auf einem Host die Webseiten und Postfächer für mehrere Domänen zu betreiben.

vdomain=Domain
ldapadd $bind << .
dn: dc=$vdomain,ou=domains,dc=illusioni,dc=de
objectClass: dNSDomain
dc: $vdomain
.
done

Weiterleitungen werden statt in /etc/aliases im Verzeichnis gespeichert.

for name in hostmaster postmaster webmaster; do ldapadd $bind << .
dn: cn=$name,ou=aliases,$base
objectClass: nisMailAlias
cn: $name
cn: $name@$domain
rfc822MailMember: root@$domain
.
done

Als nächstes erzeugen wir die Konten für den Replikationsdienst, den NSS, das KDC, den Kerberos Admin-Server, Dovecot, Postfix und weitere Dienste, die Daten aus dem Verzeichnis lesen müssen.

for daemon in repl nss kdc adm dovecot postfix apache; do ldapadd $bind << .
dn: cn=$daemon,ou=daemons,$base
objectClass: simpleSecurityObject
objectClass: organizationalRole
cn: $daemon
description: Syncrepl user for mirrormode operation
userPassword: $(slappasswd -h {SSHA} -s Password)
.
done

Benutzer anlegen

Schließlich legen wir Gruppen und Benutzer an.

uid=Uid;user=User;first=FirstName;last=LastName ldapadd $bind << .
dn: cn=$user,ou=groups,$base
objectClass: posixGroup
cn: $user
gidNumber: $uid

dn: uid=$user,ou=users,$base
objectClass: person
objectClass: inetOrgPerson
objectClass: organizationalPerson
objectClass: posixAccount
objectClass: shadowAccount
uid: $user
cn: $first $last
sn: $last
givenName: $first
mail: $user@$domain
uidNumber: $uid
gidNumber: $uid
homeDirectory: /home/$user
loginShell: /bin/bash
.

Jeder Benutzer kann sein Passwort selbst ändern:

ldappasswd $bind "uid=User,ou=users,$base" -S

Und um einen Benutzer zu löschen:

ldapdelete $bind uid=User,ou=users,$base

Eine Liste aller Benutzer erhält man mit folgender Suche:

ldapsearch $bind "uid=*" cn

Benutzer im System verwenden

Zum Abschluss wird der NSS so konfiguriert, dass er Benutzer und Gruppen zuerst in der Passwortdatei und dann per Verzeichnisdienst sucht.

/etc/nsswitch.conf
passwd: files ldap group: files ldap shadow: files ldap

Der NSLCD greift mit einem eigenen Konto lesend auf Benutzer und Gruppen hat. Er reduziert die Last, indem er Antworten zwischenspeichert.

/etc/nslcd.conf
uid nslcd gid nslcd uri ldapi:/// base dc=illusioni,dc=de ldap_version 3 binddn cn=nss,ou=daemons,dc=illusioni,dc=de bindpw Password ssl start_tls tls_cacertfile /etc/ssl/ca.crt

Nun findet das System auch Benutzer, die nicht in /etc/passwd angelegt sind.

getent passwd phrank
phrank:x:1001:1001:Frank Hoffmann:/home/phrank:/bin/bash

Identität

ldapwhoami
SASL/GSSAPI authentication started
SASL username: phrank@ILLUSIONI.DE
SASL SSF: 56
SASL data security layer installed.
dn:uid=phrank,ou=users,dc=illusioni,dc=de

Literatur

  1. Gerald Carter: LDAP System Administration, O'Reilly, 2003
  2. Debian Wiki: Setting up an LDAP server with OpenLDAP
  3. OpenLDAP Administrator's Guide: Replication
  4. Justin Ellingwood: How To Encrypt OpenLDAP Connections Using STARTTLS, 2015
  5. RFC 2307: An Approach for Using LDAP as a Network Information Service, 1998