Authentifizierung über LDAP

Autoren 
Frank Prößdorf, Volker Grabsch

Contents

Voraussetzungen

Diese Howto geht von folgenden Gegebenheiten aus:

Das meiste gilt aber für andere Distributionen genauso.

Schritt für Schritt

Installation

Zuerst installieren wir alle benötigten Pakete:

yast -i openldap2 nss_ldap pam_ldap openssl openssl-devel 

Konfiguration

Nachdem wir über

slappasswd

ein Passwort erstellt haben tragen wir folgendes in die Konfigurationsdatei des LDAP Servers (/etc/openldap/slapd.conf) ein:

include         /etc/openldap/schema/core.schema
include         /etc/openldap/schema/cosine.schema
include         /etc/openldap/schema/nis.schema
include         /etc/openldap/schema/inetorgperson.schema

schemacheck on

pidfile         /var/run/slapd/slapd.pid
argsfile        /var/run/slapd/slapd.args

modulepath      /usr/lib/openldap/modules
loglevel  256

database        ldbm
cachesize       10000
suffix          "o=organisation,c=de"
rootdn          "cn=admin,o=organisation,c=de"
rootpw          {SSHA}xxxxxxxxxxxxxxxxxxxxxx
directory       /var/lib/ldap

access to attrs=userPassword
    by self write
    by * auth

access to *
    by * read

Sind wir hiermit fertig, können wir den Server schonmal starten. Wenn der Server läuft und wir ihn in der Prozessliste sehen, fügen wir nun in die LDAP Konfigurationsdatei des Clients (/etc/openldap/ldap.conf) die entsprechende URI und Base ein:

URI ldap://127.0.0.1:389/ ldap://server2:389/
BASE o=organisation,c=de
ldap_version 3
scope sub

#ssl start_tls
#TLS_REQCERT allow

Dabei müssen wir darauf achten das die Zeilen in denen es um SSL/TLS geht auskommentiert werden. Es ist möglich bei der URI mehrere Server anzugeben. Fällt der erste Server aus springt dann der zweite ein.

scope sub bedeutet dabei, dass immer der gesamte Unterbaum durchsucht wird. Ist scope hingegen base wird nur in der aktuellen Ebene gesucht und bei one genau eine Ebene tiefer. Eine veranschaulichende Grafik findet sich in der Microsoft LDAP Dokumentation.

Daten einfügen

Der Rest dieser Konfigurationsdatei braucht uns zunächst nicht zu interessieren. Nun wollen wir eine erste kleine Struktur entwerfen um sie in die LDAP Datenbank einzufügen. Die hier erstelle Struktur ist minimal und wird individuell verändert werden müssen. Um diese also nun zu kreieren, erstellen wir eine Datei basis.ldif:

dn: o=organisation,c=de
objectclass: organization
objectclass: top
o: organisation

dn: ou=users,o=organisation,c=de
objectclass: organizationalunit
ou: users

dn: uid=frank,ou=users,o=organisation,c=de
objectclass: posixAccount
objectclass: shadowAccount
objectclass: inetOrgPerson
sn: frank
cn: frank
uid: frank
uidNumber: 1000
gidNumber: 100
homeDirectory: /home/frank
loginShell: /bin/bash
userPassword: test
gecos: Frank Proessdorf

dn: ou=groups,o=organisation,c=de
objectclass: organizationalunit
ou: groups

dn: cn=mygroup,ou=groups,o=ibb,c=de
objectclass: posixGroup
cn: mygroup
gidNumber: 200
memberuid: frank

Nachdem wir die Datei nun gespeichert haben, gliedern wir sie in den LDAP ein:

ldapadd -x -D "cn=admin,o=organisation,c=de" -W -f basis.ldif

Erklärung:

-x 
einfache Authentifizierung
-D 
BindDN des Nutzers, mit dessen Autorisation man die Daten hinzufügt (hier: der RootDN)
-W 
frage nach dem Passwort an der Kommandozeile
-f 
die LDIF-Datei mit den Daten, die man in den LDAP hinzufügen möchte

Wurde dieses Kommando fehlerfrei ausgeführt, sind wir ein gutes Stück weiter und können uns die Datensätze über

ldapsearch -x 

anzeigen lassen. Wird dieses Kommando nicht fehlerfrei ausgeführt, liegt es unter Umständen daran, dass die BaseDN (BASE) in der /etc/ldap.conf nicht richtig gesetzt wurde, oder dass /etc/ldap.conf und /etc/openldap/ldap.conf nicht übereinstimmen. Erstere der beiden wird von pam und nss genutzt, und letztere wird von den OpenLDAP utilities verwendet. Hier hilft am besten ein Softlink:

rm /etc/ldap.conf
ln -s /etc/openldap/ldap.conf /etc/ldap.conf


Haben wir schon Daten im LDAP Baum und wollen diese lediglich verändern so hilft uns ein

ldapmodify -x -D "cn=admin,o=ibb,c=de" -W -f basis.ldif


Übrigens können wir später folgendermaßen das Passwort eines Benutzers (hier: frank) ändern:

ldappasswd -D "cn=admin,o=organisation,c=de" -x -W -S cn=frank,ou=users,o=organisation,c=de

PAM & NSS

Nun folgt das Einstellen von NSS und PAM (Pluggable Authentication Module), welche es erlauben, die Quelle zur Authentifizierung für System und verschiedene Dienste zu ändern. Der Login-Vorgang auf einem Linux-System gestaltet sich demzufolge in etwa fol­gendermaßen: Der Benutzer gibt seine Login-Kennung und sein persönliches Paß­wort auf der Konsole ein. Daraufhin überprüft das System über die im "Name Ser­vice Switch" konfigurierten Methoden, ob die Kennung dem System bekannt ist, zu welcher Gruppe sie gegebenenfalls gehört, usw. Anschließend wird der An­wender entsprechend der PAM-Konfiguration authentifiziert In der /etc/nsswitch.conf sollte der LDAP als Quelle hinzugefügt werden:

passwd: files ldap
group:  files ldap
shadow: files ldap

Im Verzeichnis /etc/pam.d liegen nun Dateien, die den einzelnen Services Module zuordnen, die verschiedene Loginmöglichkeiten erlauben. Für den sshd könnte die PAM Konfigurationsdatei zum Beispiel folgendermassen aussehen:

auth     sufficient     pam_ldap.so
auth     sufficient     pam_unix2.so    
auth     required       pam_nologin.so
auth     required       pam_env.so
account  sufficient     pam_ldap.so
account  sufficient     pam_unix2.so
account  required       pam_nologin.so
password sufficient     pam_unix2.so    use_authtok
password sufficient     pam_ldap.so     use_first_pass
password required       pam_pwcheck.so
session  required       pam_unix2.so    
session  required       pam_limits.so

Sollte hierbei ein "required" fehlschlagen, so werden die folgenden Module zwar getestet, aber ultimativ resultiert ein Fehler am Ende. Schlägt ein "sufficient" fehl, ein darauffolgendes "sufficient" ist jedoch erfolgreich, so wird die Aktion am Ende als erfolgreich gewertet. Die Änderungen werden unmittelbar übernommen und wir können uns nun erfolgreich über ssh einloggen. (Nach Entfernen dieser Änderungen, versucht das System sich dennoch über LDAP einzuloggen.. unload von Bibliotheken?)

Homeverzeichnis

Um nun bei erster Authentifizierung eines eingerichteten LDAP Benutzers an einem neuen Rechner das Homeverzeichnis automatisch erstellen zu lassen muss folgende Zeile in der /etc/pam.d/sshd hinzugefügt werden:

session  required       pam_mkhomedir.so skel=/etc/skel/ umask=0022

sudo

Zunächst sollten wir das SuSE sudo Paket löschen und den Sourcecode von Sudo von herunterladen und kompilieren. Dies ist nötig, da das Programmpaket von SuSE nicht mit LDAP-Unterstützung (--with-ldap) kompiliert wurde.

Nach dem Herunterladen wird das Archiv zunächst entpackt:

tar xvfz sudo-1.6.8p9.tar.gz

War dies erfolgreich, wechseln wir in das entsprechende Verzeichnis und kompilieren es mit den richtigen Optionen:

cd sudo-1.6.8p9
./configure --prefix=/usr/packages/sudo --with-ldap --with-pam
make
make install
ln -s /usr/packages/sudo/bin/sudo /usr/bin/
ln -s /usr/packages/sudo/bin/sudoedit /usr/bin/

Dies sollte ohne Probleme funktionieren, da die LDAP Bibliotheken im Standardpfad hinterlegt sind. Ist dies nicht der Fall müssen noch folgende Pakete per yast hinzugefügt werden

openldap2-devel pam-devel

Nun kopieren wir noch die entsprechende PAM-Konfigurationsdatei:

cp sample.pam /etc/pam.d/sudo

Und wir legen das folgende Schema in /etc/openldap/schema/sudo.schema an:

#
#  schema file for sudo
#
attributetype ( 1.3.6.1.4.1.15953.9.1.1
      NAME 'sudoUser'
      DESC 'User(s) who may  run sudo'
      EQUALITY caseExactIA5Match
      SUBSTR caseExactIA5SubstringsMatch
      SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )

attributetype ( 1.3.6.1.4.1.15953.9.1.2
      NAME 'sudoHost'
      DESC 'Host(s) who may run sudo'
      EQUALITY caseExactIA5Match
      SUBSTR caseExactIA5SubstringsMatch
      SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )

attributetype ( 1.3.6.1.4.1.15953.9.1.3
      NAME 'sudoCommand'
      DESC 'Command(s) to be executed by sudo'
      EQUALITY caseExactIA5Match
      SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )

attributetype ( 1.3.6.1.4.1.15953.9.1.4
      NAME 'sudoRunAs'
      DESC 'User(s) impersonated by sudo'
      EQUALITY caseExactIA5Match
      SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )

attributetype ( 1.3.6.1.4.1.15953.9.1.5
      NAME 'sudoOption'
      DESC 'Options(s) followed by sudo'
      EQUALITY caseExactIA5Match
      SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )

objectclass ( 1.3.6.1.4.1.15953.9.2.1 NAME 'sudoRole' SUP top STRUCTURAL
      DESC 'Sudoer Entries'
      MUST ( cn )
      MAY ( sudoUser $ sudoHost $ sudoCommand $ sudoRunAs $ sudoOption $
            description )
      )

Nun fügen wir das Schema noch in der /etc/openldap/slapd.conf hinzu:

include         /etc/openldap/schema/sudo.schema

... und starten den LDAP-Server neu:

rcldap restart

Schon können wir entsprechende Benutzer in den LDAP Verzeichnisbaum hinzufügen. Dies tun wir, indem wir vorher einen sudoers-Knoten erstellen und dann die Benutzer an diesen Knoten anhängen:

dn: ou=sudoers,o=organisation,c=de
objectclass: organizationalUnit
ou: sudoers

dn: cn=frank,ou=sudoers,o=organisation,c=de
objectClass: top
objectClass: sudoRole
cn: frank
sudoUser: frank
sudoHost: ALL
sudoCommand: ALL

Oder wir transformieren einfach gleich die vorher angelegte sudoers-Datei.

Damit sudo mit den im LDAP angelegten Benutzern funktioniert, müssen wir folgende Zeile in die /etc/openldap/ldap.conf eintragen:

sudoers_base   ou=sudoers,o=organisation,c=de

TODO: Aufgefallen ist mir, dass als sudoHost anscheinend nur ALL funktioniert. Jegliche Versuche, die ich unternommen habe mit IP, Host, localhost und dergleichen, liefen schief. Sudo hatte dann gesagt, dass der Benutzer in diesen Fällen nicht zu den sudoers gehört.

Replikation

Um auf den Clients für eine gewisse Redundanz zu sorgen, kann man in der ldap.conf mehrere LDAP Server angeben, sodass der Zugriff auch funktioniert, wenn einer der Server ausgefallen sein sollte:

URI ldap://server1:389 ldap://server2:389

Hierfür ist die Grundvorraussetzung, dass wir den ersten erstellten LDAP Server schon kopiert haben, d.h. auch alle Konfigurationsdateien und den Inhalt.

slapcat > ldap_inhalt.txt
tar cvfz ldap_copy.tgz /etc/openldap/slapd.conf /etc/openldap/ldap.conf /etc/pam.d/sshd ldap_inhalt.txt
scp ldap_copy.tgz server2:ldap_copy.tgz

Haben wir den zweiten Server nun fehlerfrei laufen können wir mit der Einrichtung der Replikation beginnen.

Wir konfigurieren zunächst den Master und fügen in dessen /etc/openldap/slapd.conf folgende Einträge hinzu.

replogfile /var/lib/slurpd/replica/slurpd.replog
replica uri=ldap://server2:389
        binddn="cn=admin,o=organisation,c=de"
        bindmethod=simple
        credentials=secret

Hierbei ist wichtig, dass die binddn und die credentials eigentlich nicht mit der rootdn übereinstimmen sollen. Dies ist hier nur zu Testzwecken. Eigentlich sollte ein Zugang genommen werden, der Nur-Lese-Rechte auf den gesamten LDAP hat.

Weiterhin müssen wir beachten, dass das slurpd.replog dem Benutzer ldap gehört und dieser auf die Rechte hat, in das Verzeichnis /var/lib/slurpd/replica/ zu wechseln.

Sollte der LDAP Replikationsdaemon slurpd noch nicht laufen, starten wir diesen. Vorher starten wir den slapd neu.

rcldap restart
rcslurpd start

Nun kommt der Slave dran. Auf diesem erweitern wir ebenfalls nur die /etc/openldap/slapd.conf um folgende Einträge:

updatedn   "cn=admin,o=organisation,c=de"
updateref  server1:389

Wir dürfen nicht vergessen, den slapd wiederum neuzustarten:

rcldap restart

Nun sollten sich die beim Master vollzogenen Änderungen auf den Slave automatisch replizieren.

Achtung: Wollen wir phpldapadmin verwenden, so muss in der Konfigurationsdatei der Master angegeben werden. Änderungen beim Slave produzieren beim PHPLdapAdmin folgende Fehlermeldung:

LDAP meldet: Internal (implementation specific) error
Fehlernummer: 0x50 (LDAP_OTHER)
Beschreibung:

und in /var/log/messages

Sep 22 12:14:06 test22 slapd[24419]: No structuralObjectClass for entry (uid=test,ou=users,o=organisation,c=de)
Sep 22 12:14:06 test22 slapd[24419]: conn=79 op=1 RESULT tag=105 err=80 text=no structuralObjectClass operational attribute 

LDAP-Dump sichern

Um nun als root per cron daemon einen LDAP-Dump zu sichern, können wir folgendes Script benutzen, in dem wir lediglich den von uns gewünschten Pfad angeben müssen. Niemand außer root hat dann Zugriff auf die Dateien und das Verzeichnis.

ldap_dump.sh

#!/bin/bash
 
######
# ldap_dump.sh
#  - create an LDAP dump and save it into a "secure" directory
#
# Autor: Frank Proessdorf
######
 
PFAD=/root/save
DATE=`date +%Y%m%d`
DUMPFILE=ldap_dump_$DATE.ldif

# create secure folder if it doesn't exist
if [ ! -d $PFAD ]
then
        echo "Sicherer Pfad wird angelegt.."
        mkdir $PFAD
        chmod 700 $PFAD
fi

cd $PFAD

# dump LDAP
slapcat -l $DUMPFILE

# gzip the dump
gzip $DUMPFILE

# set correct mode
chmod 600 $DUMPFILE.gz

echo "LDAP-Dump wurde gesichert."

#####
# The LDIF generated by this tool is suitable for use with slapadd(8).  
# As the entries are in database order, not superior first order,  they  cannot
# be loaded with ldapadd(1) without first being reordered.
#####

Beim Wiederherstellen ist zu beachten, dass die Daten per slapadd und nicht per ldapadd hinzugefügt werden müssen, da sie nicht in der Baumstrukturreihenfolge gedumpt wurden.

Verschlüsselung

Nun werden wir die Kommunikation zwischen LDAP Client und Server noch verschlüsseln, um den Authentifizierungsprozess abzusichern.

Bisherige Beobachtungen hierzu:

CA, Client und Serverzertifikate wurden erstellt. Nach Kopieren der Zertifikate ins /etc/openldap Verzeichnis wurden die Konfigurationsdateien wie folgt geändert:

Auf dem Server: slapd.conf

TLSCACertificateFile /etc/openldap/cacert.pem
TLSCertificateFile /etc/openldap/servercert.pem
TLSCertificateKeyFile /etc/openldap/serverkey.pem
TLSVerifyClient allow

Auf dem Client: ldap.conf

tls_cacert /etc/openldap/cacert.pem
tls_reqcert demand

Wird TLS genutzt kann über

ldapsearch -x -ZZ

getestet werden ob die verschlüsselte Kommunikation funktioniert.

Wird SSL genutzt, so muss die URI in der ldap.conf mit ldaps:// statt ldap:// beginnen und es kann eine ganz normale Abfrage über

ldapsearch -x

vorgenommen werden.

Sollten Fehler auftreten, kann folgender Befehl helfen, diesen auf die Spur zu kommen. SSL Test

openssl s_client -connect server01.notjusthosting.com:636 -state -CAfile cacert.pem -debug

Solaris als Client

Vorraussetzungen

Vorgehen sollte für andere Solaris Versionen ebenfalls funktionieren.

Vorgehen in Kurzfassung

Es gibt für allgemeine LDAP Anfragen zwei Dateien die zu erstellen sind: /var/ldap/ldap_client_cred und /var/ldap/ldap_client_file. In erstere kommen die Authentifizierungsdaten des ProxyBenutzers, in letztere die Zugriffskonfiguration.

NS_LDAP_BINDDN= cn=admin,o=organisation,c=de
NS_LDAP_BINDPASSWD= {NS1}xyz123
NS_LDAP_FILE_VERSION= 2.0
NS_LDAP_SERVERS= server1
NS_LDAP_SEARCH_BASEDN= o=organisation,c=de
NS_LDAP_AUTH= simple
NS_LDAP_CACHETTL= 3600
NS_LDAP_CREDENTIAL_LEVEL= proxy
NS_LDAP_SERVICE_SEARCH_DESC= passwd:ou=users,o=organisation,c=de?sub
NS_LDAP_SERVICE_SEARCH_DESC= group:ou=groups,o=organisation,c=de?sub

Es bleibt wie üblich PAM und NSS zu konfigurieren. Fangen wir mit NSS an und bearbeiten in der Datei /etc/nsswitch.conf drei Zeilen, so das sie LDAP berücksichtigen. Alle anderen Zeilen berücksichtigen LDAP nicht.

passwd:     files ldap
group:      files ldap

Nun folgt die PAM Konfiguration. Hierzu muss die Datei /etc/pam.conf geändert werden. Hierbei kann other natürlich auch gegen login, oder sonstige Dienste ersetzt werden.

other   auth requisite          pam_authtok_get.so.1
other   auth required           pam_dhkeys.so.1
other   auth required           pam_unix_cred.so.1
other   auth binding            pam_unix_auth.so.1 server_policy
other   auth required           pam_ldap.so.1

passwd  auth binding            pam_passwd_auth.so.1 server_policy
passwd  auth required           pam_ldap.so.1

other   account requisite       pam_roles.so.1
other   account binding         pam_unix_account.so.1 server_policy
other   account required        pam_ldap.so.1

Die Option other password brauchen wir bei Solaris10 nicht, weil das Passwortmanagement über pam_authtok_store.so.1 läuft.

Nach den Änderungen müssen der //nscd// und der //ldap_cachemgr// neu gestartet werden.

Um mitzuloggen was der LDAP Caching Manager ausführt können wir das Debugging erhöhen:

/usr/lib/ldap/ldap_cachemgr -l /var/adm/messages -d 6

Siehe auch

Dies sind Quellen und weiterführende Artikel für dieses Howto.