9 | Berechtigungen mit ACLs |
Bis zu diesem Zeitpunkt kann noch jeder anonym auf alle Informationen des LDAP-Baums zugreifen, und ein ldapsearch -x listet Ihnen noch alle Objekte auf. Zwar können bei anonymen Zugriffen und bei Zugriffen von authentifizierten Benutzern alle Werte nur gelesen werden, aber auch sicherheitsrelevante Informationen wie zum Beispiel die Passwörter der Benutzer werden angezeigt. Nur der rootDN hat immer Schreibzugriff. Die Rechte des rootDN können Sie auch nicht einschränken. Aus diesem Grund sollten Sie den rootDN auch nie für authentifizierte Zugriffe von Diensten nutzen, denn in manchen Konfigurationsdateien von Diensten müssen Sie das Passwort in Klartext eintragen.
Wie Sie schon bei der Einrichtung des sssd gesehen haben, werden für Dienste immer eigene Objekte vom Typ simpleSecurityObject erstellt, die für die Authentifizierung von Diensten genutzt werden.
Bei der Einrichtung des LDAP-Servers über die dynamische Konfiguration werden Sie mit einen anonymen Zugriff auf die dynamische Konfiguration mit ldapsearch -x -b "cn=config" keine Ergebnisse sehen. Hier ist der Schutz vor einem anonymen Zugriff bereits aktiv. In der Konfiguration gibt es eine Access Control List (ACL) , die den Zugriff für den Benutzer root über den SASL-Mechanismus EXTERNAL zulässt, aber den Zugriff für alle anderen Benutzer unterbindet. Ohne diese Grund-ACL hätten Sie die Konfiguration so gar nicht einrichten können.
Dadurch, dass wir in unserer Grundkonfiguration einen rootDN mit einem rootpw im Backend cn=config eingerichtet haben, könnten Sie diese ACL entfernen. Dann hätte nur noch der rootDN Zugriff auf die Konfiguration. Aber eins nach dem anderen.
9.1 | Grundlegendes zu ACLs |
Um den Einsatz von ACLs werden Sie nicht herumkommen. Daher ist es sehr wichtig, dass Sie ACLs genau planen und verstehen. Auch die Reihenfolge, wie die ACLs abgearbeitet werden, ist von enormer Wichtigkeit. Dabei ist es nicht nur wichtig, wie eine ACL aufgebaut ist, sondern auch, in welcher Reihenfolge die ACLs abgearbeitet werden. Bei der Einrichtung des ersten LDAP-Servers haben wir Ihnen gezeigt, dass die Konfiguration in verschiedene Bereiche unterteilt ist. Jeder der Bereiche kann mit eigenen ACLs ausgestattet werden. Eine Besonderheit ist dabei der Abschnitt {-1}frontend: Hier handelte es sich um den globalen Bereich der Konfiguration. Alle ACLs, die Sie hier eintragen, sind immer für alle Datenbanken gültig.
ACLs werden Sie immer mittels LDIF-Dateien in die dynamische Konfiguration einspielen. Wir werden Ihnen für alle Beispiele im Buch die benötigte LDIF-Datei bereitstellen, sodass Sie alle ACL-Beispiele sofort nachvollziehen können, wenn Sie unsere Beispielumgebung eingerichtet haben.
9.1.1 | Aufbau einer ACL |
Jede ACL wird vom Server immer als eine Zeile interpretiert. Wenn Sie die dynamische Konfiguration nutzen, werden Sie sehen, dass die ACLs auch immer als eine Zeile angezeigt werden, wenn Sie sich die ACLs auflisten lassen oder über ein grafisches Werkzeug auf die Konfiguration zugreifen. Beim Anlegen der ACLs können Sie die Zeilen auch umbrechen, um die ACLs besser lesen zu können.
Wichtig: Wenn Sie ACLs in LDIF-Dateien umbrechen, sorgen Sie dafür, dass am Ende einer Zeile kein Leerzeichen vorhanden sind, da diese Zeile sonst Fehler erzeugt oder die ACLs im BASE64-Format in der Konfiguration abgelegt werden. Eine neue Zeile nach dem Umbruch muss immer mit mindestens zwei Leerzeichen beginnen. Das erste Leerzeichen ist der Zeilenumbruch und das zweite Leerzeichen das Trennzeichen zum letzten Wort in der vorherigen Zeile. Setzen Sie nur ein Leerzeichen am Zeilenanfang, dann wird das letzte Wort der vorherigen Zeile und das erste Wort der neuen Zeile direkt zusammengeschrieben in der Konfiguration abgelegt. Das führt unweigerlich zu Fehlern.
ACLs sind so wie in Listing 9.1 aufgebaut:
olcAccess{0} to <what> by <who> <perm> by <who> <perm> [access] [control] olcAccess{1} to <what> by <who> <perm> by <who> <perm> [access] [control] olcAccess{2} to <what> by <who> <perm> by <who> <perm> [access] [control]
Hier legen Sie fest, auf welches Objekt oder welches Attribut sich diese ACL bezieht. Tragen Sie hier den DN des Objekts oder einfach den Name des Attribut ein, das Sie über eine ACL schützen wollen. Wollen Sie eine ACL für mehrere Objekte nutzen, können Sie an dieser Stelle auch reguläre Ausdrücke für den DN nutzen.
Auf die Verwendung von regulären Ausdrücken werden wir später in Abschnitt 9.2.4 zurückkommen. Bei der Definition des DNs legen Sie fest, ob die Regel nur für genau dieses Objekt oder weitere untergeordnete Objekte gültig sein soll. Gültige Beschreibungen für die Tiefe sehen Sie in Tabelle 9.2:
Muster |
greift für |
Aliase |
dn |
genau das hier aufgeführte Objekt |
dn.exact, dn.base |
dn.one |
dieses Objekt und eine Ebene darunter |
dn.onelevel |
sn.sub |
dieses Objekt und alle untergeordneten Objekte |
dn.subtree |
dn.children |
für alle Objekte unterhalb dieses Objekts, nicht aber das hier angegebene Objekt |
na |
Hier legen Sie fest, wer Rechte an den Objekten bekommt, die Sie im what-Feld definiert haben. Auch hier verwenden Sie wieder einen DN. Verwenden Sie hier die in Tabelle 9.2 aufgeführten Optionen, können Sie die Liste der Berechtigten erweitern. So würde eine Zeile to dn.children=ou=users,dc=example,dc=net allen Objekten unterhalb dieser OU die vergebenen Rechte erteilen. Selbstverständlich können Sie auch hier reguläre Ausdrücke verwenden.
Neben den Möglichkeiten, Rechte an bestimmten Objekten zu vergeben, gibt es noch vordefinierte Einträge, die Rechte erhalten können. In Tabelle 9.2 sehen Sie eine Liste der speziellen Objekte.
Objekt |
Bedeutung |
anonymous |
Ein nicht angemeldeter Benutzer |
users |
Jeder angemeldete Benutzer |
self |
Rechte, die ein Objekt an sich selbst hat (z. B. Benutzer kann sein Passwort ändern) |
group |
Rechte für Gruppen |
peername |
Das Objekt bezieht sich auf Hosts |
* |
Alle Objekte |
9.1.2 | Die Berechtigungen |
Immer, wenn Sie eine Zeile mit dem who-Feld erstellen, vergeben Sie eine der folgenden Berechtigungen:
none
Das angegebene Objekt erhält keine Rechte an dem im what-Feld angegebenen Objekt, auch Zugriffsfehler werden nicht angezeigt.
disclose
Auch diese Berechtigung erlaubt keinen Zugriff auf das entsprechende Objekt, aber eventuelle Fehlermeldungen beim Zugriff werden angezeigt.
auth
Diese Berechtigung erlaubt lediglich den Zugriff auf ein Objekt oder Attribut, wenn der Bind-Prozess, sprich eine Authentifizierung, durchgeführt wird. Das ist die Berechtigung, die ein anonymer Benutzer an einem Passwort haben muss, da sonst eine Anmeldung nicht möglich ist. Ein Auslesen oder Vergleichen der Passwörter ist mit dieser Berechtigung nicht möglich.
compare
Bei der Abfrage eines Objekts oder eines Attributs mit dieser Berechtigung kann der Inhalt mit einem Muster verglichen werden; das Ergebnis ist entweder ein TRUE (der Inhalt des Attributs ist identisch mit dem angegebenen Muster) oder ein FALSE (der Inhalt des Attributs ist nicht identisch mit dem angegebenen Muster). Der eigentliche Inhalt wird nicht angezeigt.
Hinweis: Denken Sie daran: Es gibt Attribute, bei denen wird auf Groß- und Kleinschreibung geachtet, und bei anderen Attributen nicht. Auch bei einem compare wird auf eine eventuelle Groß- und Kleinschreibung des Attributs geachtet, wenn das Attribut entsprechend konfiguriert ist.
search
Hat ein Benutzer die Berechtigung search, kann er nach Werten dieses Attributs suchen und bekommt die Objekte angezeigt, bei denen das Attribut den Wert besitzt, aber das Attribut selbst wird nicht angezeigt.
read
Diese Berechtigung erlaubt es, das Attribut oder das Objekt zu lesen, sprich der Inhalt kann angezeigt werden.
write
Diese Berechtigung erlaubt die Veränderung eines Objekts oder Attributs. Hat ein Benutzer diese Berechtigung an einer OU, ist er in der Lage, neue Objekte in der OU anzulegen. Die Berechtigung write setzt sich aus den beiden Berechtigungen add und delete zusammen. Beide Berechtigungen können Sie auch einzeln vergeben. Vergeben Sie an einer OU die Berechtigung add an einen Benutzer, kann er zwar neue Objekte in der OU anlegen, aber bestehende Objekte nicht mehr löschen.
manage
Mit dieser Berechtigung vergeben Sie die Möglichkeit, ein Objekt oder Attribut komplett zu administrieren. Seien Sie sehr vorsichtig mit der Vergabe dieser Berechtigung, denn ein Benutzer, der an Objekten das Recht manage besitzt, kann auch die internen Attribute verändern. Wir werden später einen ldap-admin anlegen, der alle Schreibvorgänge am DIT durchführen können soll. Auch dieser Benutzer wird nur das Recht write erhalten. Nur der rootdn kann die internen Attribute ändern.
Eine höhere Berechtigung schließt alle geringeren Berechtigungen ein. Das Recht read schließt also die Rechte search, compare, auth und disclose ein.
9.1.3 | Die Privilegien |
Neben den Berechtigungen gibt es noch die Privilegien:Mithilfe der Privilegien können Sie die Rechte an den Objekten noch feiner vergeben. In Tabelle 9.3 sehen Sie die Gegenüberstellung der Berechtigungen und der Privilegien.
Berechtigung |
Privileg |
none |
0 |
disclose |
d |
auth |
x |
compare |
c |
search |
s |
read |
r |
write |
w |
add |
a |
delete |
z |
manage |
m |
Der Unterschied zwischen Berechtigungen und Privilegien ist der, dass bei einer höheren Berechtigung wie write alle schwächeren Berechtigungen eingeschlossen sind. Bei den Privilegien müssen Sie immer alle Privilegien vergeben, die ein Benutzer oder eine Gruppe an einem Objekt besitzt. Schwächere Privilegien sind nicht automatisch eingeschlossen.
Die Privilegien können Sie über ein + vergeben, durch ein - entziehen, und ein = setzt alle Rechte auf das angegebene Muster. In Listing 9.2 sehen Sie zwei Beispiele:
olcAccess{0} to attrs=userPassword by self +rw by * +x olcAccess{1} to dn.children="ou=users,dc=example,dc=net" by self +rw by cn.children="ou,users,dc=example,dc=net" +sr
Im ersten Beispiel kann jeder Benutzer sein Passwort lesen und schreiben, aber nicht vergleichen. Alle anderen Benutzer können das Attribut nur für die Authentifizierung nutzen.
Im zweiten Beispiel kann jeder Benutzer aus der OU ou=users,dc=example,dc=net auf alle Objekte der OU suchend und lesend zugreifen, ein Vergleichen ist nicht möglich. An dem eigenen Objekt kann jeder Benutzer aus der OU lesend und schreibend zugreifen, hier ist dann ein Vergleichen und ein Suchen nicht möglich.
Bei der Verwendung der Privilegien müssen Sie noch genauer darauf achten, dass Sie alle benötigten Rechte vergeben haben.
ACLs werden immer von oben nach unten in der Liste abgearbeitet. Trifft eine ACL auf den gewünschten Zugriff zu, wird die Abarbeitung der ACLs sofort unterbrochen. Selbst wenn es eine weitere ACL gäbe, die die Rechte für den Zugriff restriktiver oder umfangreicher vergeben würde, wird diese ACL nie geprüft, da der Prüfprozess vorher beendet wird. Sie sehen, dass es wichtig ist, dass Sie die Reihenfolge der ACLs immer genau prüfen.
Nicht nur die Reihenfolge der ACLs ist wichtig, sondern auch die Reihenfolge der einzelnen who-Zeilen. Bekommt ein Benutzer Rechte an einem Objekt (zum Beispiel read) über eine Gruppe und zusätzlich tragen Sie den Benutzer, weiter unten in der ACL, mit dem Recht write ein, wird das Recht write nie wirksam werden, da die Prüfung der ACL bereits bei der Gruppe, in der der Benutzer Mitglied ist und die das Recht read besitzt, endet. Achten Sie daher auch immer auf die Reihenfolge innerhalb einer ACL.
Beim Einrichten des OpenLDAP-Servers haben Sie bereits gesehen, dass die Konfiguration in mehrere Datenbanken unterteilt ist. Auch gibt es noch die Bereiche dn: cn=config und dn: olcDatabase={-1}frontend,cn=config, in denen der globale Bereich der Konfiguration eingetragen wird. Für alle Datenbanken und die beiden globalen Bereiche lassen sich getrennte ACLs hinzufügen. So können Sie bestimmte Bereiche nur für bestimmte Benutzer verwaltbar machen.
Wenn Sie die folgenden Regeln einhalten, ist die Gefahr gering, dass ACLs aufgrund ihrer Position nicht wirksam sind:
1. ACLs, mit denen Sie Rechte an Attributen vergeben wollen, schreiben Sie immer an den Anfang der Liste.
2. Je tiefer ein Objekt im Baum angesiedelt ist, desto weiter oben in der Liste muss die ACL stehen.
3. Verzichten Sie, wenn möglich, auf den übermäßigen Einsatz von regulären Ausdrücken bei der Einrichtung von ACLs. Reguläre Ausdrücke in ACLs führen zu erheblich mehr Überprüfungen beim Zugriff auf den LDAP-Baumund werden oft unübersichtlich.
Wichtig ist beim Erstellen der LDIF-Dateien für ACLs, dass Sie die by-Zeilen immer mindestens mit zwei Leerzeichen einrücken. Das erste Leerzeichen ist der Zeilenumbruch und das zweite Leerzeichen die Trennung zum vorherigen Wort.
Innerhalb einer ACL darf es keine Kommentarzeilen geben (eine ACL wird vom LDAP-Server immer als eine Zeile interpretiert). Bei der Beschreibung des Datensatzes, für den diese ACL relevant ist, können Sie Objekte mit ihrem DN oder die Namen von Attributen angeben.
Bei der Reihenfolge achten Sie darauf, dass Sie Berechtigungen auf Attribute als Erstes schreiben und erst dann die ACLs auf Objekte und Container definieren. Sonst kann es dazu kommen, dass ein Benutzer über den Container ein Recht bekommt, das Sie später über ein Attribut einschränken wollen.
Da die Überprüfung der ACLs bei einem Zugriff durch einen Benutzer an der Stelle endet, an der ein match gefunden wurde, würde eine spätere Beschränkung auf ein Attribut nicht mehr greifen. Um die folgenden Regeln besser zu verstehen, sehen Sie sich Bild 9.1 an:
Bild 9.1
Die oberste Ebene ist also nicht Ihr eingerichteter Suffix, sondern ””. Darunter auf gleicher Ebene liegen die Datenbanken für die dynamische Konfiguration: cn=config, die Datenbank, in der alle Schemata beim Starten des LDAP-Servers abgelegt werden, cn=subschema, Ihre eigene Datenbank mit den Objekten. Später kommt noch die Datenbank cn=monitor dazu, in der alle Zugriffe und Aktionen gespeichert werden. Die meisten Monitorsysteme sind in der Lage, diese Information auszuwerten. In Kapitel 17, «Monitoring mit Munin», werden wir auf das Backend cn=monitor näher eingehen.
Wir haben jetzt schon oft auf die Wichtigkeit der Reihenfolge der ACLs hingewiesen und dass die Prüfung bei einem match sofort beendet wird, aber das Verhalten können Sie beeinflussen. Die Abarbeitung der Regeln kann über verschiedene control fields manipuliert werden. Folgende control fields stehen Ihnen zur Verfügung:
stop
Dabei handelt es sich um die Standardmethode; immer wenn ein Zugriff einen Match erzeugt, wird die Abarbeitung der weiteren Regeln gestoppt.
continue
Wenn Sie in einer Regel verschiedene who-Zeilen eingetragen haben und eine der Zeilen auf einen Zugriff passt, wird normalerweise der Vergleich mit den weiteren who-Zeilen dieser Regel abgebrochen. Verwenden Sie aber zusätzlich das continue, werden auch die weiteren Zeilen der Regeln noch abgearbeitet.
break
Würde aufgrund einer Berechtigung die Abarbeitung der weiteren Regeln abbrechen, so können Sie mit einem break dafür sorgen, dass auch die weiteren Regeln bis zum nächsten Match abgearbeitet werden.
Hinweis: Eine umfassende Hilfe und weitere Erklärungen zur Verwendung von ACLs finden Sie in der Manpage slapd.access.
9.1.4 | Erste Schritte mit ACLs |
Wenn Sie sich die Konfiguration Ihres LDAP-Servers anzeigen lassen, werden Sie feststellen, dass bei der Einrichtung der dynamischen Konfiguration bereits ACLs eingerichtet wurden, und zwar in verschiedenen Bereichen. In Listing 9.3 sehen Sie die bereits eingetragenen ACLs für die Objektdatenbank:
root@provider01:~# ldapsearch -Y EXTERNAL -H ldapi:/// -b cn=config ... dn: olcDatabase={-1}frontend,cn=config ... olcAccess: {1}to dn="" by * read olcAccess: {2}to dn.base="cn=subschema" by * read ... dn: olcDatabase={2}mdb,cn=config ... olcAccess: {0} to attrs=userPassword by anonymous auth by self write by * none olcAccess: {1} to attrs=shadowLastChange by anonymous auth by self write by * none olcAccess: {2} to * by dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth manage by * read ...
Im Bereich dn: olcDatabase={-1}frontend,cn=config der Konfiguration finden Sie zwei ACLs, die für alle Objektdatenbanken benötigt werden. Damit Sie die ACLs nicht in jeder Objektdatenbank einzeln einspielen müssen, werden diese beiden ACLs im globalen Bereich der Konfiguration gesetzt und sind somit für alle Objektdatenbanken gültig:
olcAccess: {0}to dn.exact=””by * read
Durch diese ACL werden alle Zugriffe auf die oberste Ebene des LDAP-Baums freigegeben, durch die Verwendung von dn.exact aber nur genau auf dieses Element. Ihr Suffix liegt direkt unter dieser Ebene.
olcAccess: {1}to dn.exact=cn=subschema by * read
Bei jedem Zugriff wird immer geprüft, ob die Attribute und Objektklassen, die aufgelistet werden sollen, auch in den Schemata vorhanden sind. Aus dem Grund muss bei jeder Abfrage des LDAP der Zugriff auf cn=subschema möglich sein.
Über die Nummerierung der ACLs wird die Reihenfolge der Abarbeitung festgelegt. Über die Nummerierung können Sie auch neue ACLs an bestimmten Stellen hinzufügen oder entfernen.
Jeder anonyme Zugriff auf die Datenbank soll verhindert werden. Aus dem Grund benötigen Sie jetzt eine ACL, die nur angemeldeten Benutzer das Recht gibt, auf den Suffix Ihres Baums zugreifen zu können. In Listing 9.4 sehen Sie die entsprechende LDIF-Datei, in der die ersten Berechtigungen für die obersten Ebenen vergeben werden:
dn: olcDatabase={2}mdb,cn=config changetype: modify add: olcAccess olcAccess: {2}to dn.exact=dc=example,dc=net by users read
Wichtig: Achten Sie beim Erstellen der LDIF-Datei darauf, dass Sie keine zusätzlichen Leerzeichen in der Zeile oder am Ende der Zeilen haben. Sollten Sie nach dem Einspielen der ACLs eine der ACLs im BASE64-Format angezeigt bekommen, so haben Sie in der LDIF-Datei bei diesem Eintrag ein zusätzliches Leerzeichen am Ende der Zeile gesetzt. Wenn Sie die ACLs mit dem vi schreiben, gibt es dort die Option :set list. Damit werden unter anderem die Leerzeichen am Ende einer Zeile angezeigt.
Ab jetzt ist es wichtig, dass Sie auf die richtige Datenbanknummer achten. Wenn Sie unsere Beispielkonfiguration verwendet haben, ist die Datenbank mit der Nummer 1 die Monitoring-Datenbank und die Nummer 2 die Objektdatenbank.
Diese ACLs steuern den Zugang auf Ihren LDAP-Baum, ohne diese ACLs können keine Objekte aufgelistet werden.
Spielen Sie die ACLs mittels ldapadd wie in Listing 9.5 ein:
root@provider01:~# ldapadd -x -D "cn=admin,dc=example,dc=net"\ -W -f basic-acl.ldif Enter LDAP Password: modifying entry "olcDatabase={2}mdb,cn=config"
Wenn Sie die LDIF-Datei nicht mit dem rootDN einspielen wollen, können Sie auch den EXTERNAL-Principal nutzen – aber nur, wenn Sie sich als root am LDAP-Server angemeldet haben. Ein Passwort wird an dieser Stelle nicht benötigt. In Listing 9.6 sehen Sie das entsprechende Kommando:
root@provider01:~# ldapmodify -Q -Y EXTERNAL -H ldapi:/// -f basic-acl.ldif modifying entry "olcDatabase={2}mdb,cn=config"
Lassen Sie sich jetzt einmal die Konfiguration anzeigen und suchen Sie nach den ACLs für für Ihre Objektdatenbank. Dort werden Sie die ACLs aus Listing 9.7 sehen:
olcAccess: {0}to dn.exact=dc=example,dc=net by users read olcAccess: {1} to attrs=userPassword by anonymous auth by self write by * none olcAccess: {2} to attrs=shadowLastChange by anonymous auth by self write by * none olcAccess: {3} to * by dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth manage by * read
Nachdem Sie die ACL eingespielt haben, führen Sie einmal eine anonyme Suche durch und einmal eine Suche mit dem Benutzer uid=sssd-user,ou=users,dc=example,dc=net. Sie werden die Ergebnisse aus Listing 9.8 sehen:
root@provider01:~# ldapsearch -x -LLL No such object (32) root@provider01:~# ldapsearch -x -D uid=sssd-user,ou=users,\ dc=example,dc=net -W -LLL Enter LDAP Password: dn: dc=example,dc=net objectClass: domain objectClass: dcObject dc: example dn: ou=users,dc=example,dc=net ou: users objectClass: organizationalUnit dn: ou=groups,dc=example,dc=net ou: groups objectClass: organizationalUnit dn: cn=benutzer,ou=groups,dc=example,dc=net objectClass: posixGroup gidNumber: 10000 cn: benutzer ...
Obwohl in der ACL mit der Nummer 3 noch für alle (somit auch für anonyme Zugriffe) das Recht read eingetragen ist, werden bei einem anonymen Zugriff keine Objekte mehr angezeigt. Nur wenn Sie sich mit einem Benutzer anmelden, werden Ihnen alle Objekte angezeigt. Aber warum ist das so?
Beim anonymen Zugriff wird als Erstes versucht, auf den Suffix zuzugreifen. Da der Zugriff auf den Suffix aber durch die gerade eingespielte ACL nur noch für authentifizierte Benutzer möglich ist, können auch alle darunter liegenden Objekte nicht mehr angezeigt werden.
Stellen Sie sich Ihren LDAP-Baum wie ein Haus vor, der Suffix ist dabei die Haustür. Wenn Sie keinen Zugang zum Haus mehr bekommen, weil Sie keinen Schlüssel für die Haustür besitzen, können Sie auch die Räume im Haus nicht mehr betreten. Selbst dann nicht, wenn Sie einen Schlüssel für die Tür für einen der Räume im Haus besitzen.
Durch dieses Beispiel haben wir Ihnen noch einen wichtigen Punkt in Bezug auf die ACLs gezeigt: Es reicht nicht, dass ein Benutzer Rechte an einem Objekt hat, um darauf zugreifen zu können. Sein Weg zum Objekt muss auch durch ACLs ermöglicht werden.
Bei der ersten Einrichtung der Konfiguration wurde auch eine ACL eingerichtet, die den lesenden Zugriff auf alle Objekte für jeden erlaubt. In Listing 9.9 sehen Sie diese ACL:
olcAccess: {3} to * by dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth manage by * read
Wenn diese ACLs so bleiben würden, hätten alle Benutzer später alle Rechte auf allen Objekten, bei denen die Zugriffe nicht durch andere ACLs verboten sind. Es gilt somit: „Alles, was nicht verboten ist, ist erlaubt.“ Das ist nicht gerade die beste Policy, um den LDAP-Baum abzusichern. Besser ist: „Alles, was nicht ausdrücklich durch eine ACL erlaubt ist, ist verboten.“ Aus diesem Grund soll jetzt der Teil der ACL entfernt werden, der das Leserecht auf alle vergibt.
Leider können Sie ACLs nicht einfach modifizieren, sondern für eine Änderung wird die ACL als Erstes gelöscht und dann mit dem neuen Inhalt an derselben Stelle wie vorher wieder eingespielt. Um nun die zweite by-Zeile zu entfernen, benötigen Sie die LDIF-Datei aus Listing 9.10:
dn: olcDatabase={2}mdb,cn=config changetype: modify delete: olcAccess olcAccess: {3} - add: olcAccess olcAccess: {3} to * by dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth manage
Aber halt, was ist jetzt passiert? Jetzt hat nur noch der root Zugriff auf den LDAP-Baum. Wenn Sie jetzt versuchen, als sssd-user auf den Baum zuzugreifen, sehen Sie nur noch die oberste Ebene dc=example,dc=net, alles andere wird ausgeblendet. Aber warum? Durch das Entfernen der ACL haben Sie dem Benutzer auch das Recht genommen, auf alle Objekte zuzugreifen. Lediglich den Suffix kann er sich noch anzeigen lassen, da alle authentifizierten Benutzer auf den Suffix Zugriff haben.
Bei dem sssd-user handelt sich um ein Objekt, das allen Benutzern die Anmeldung ermöglichen soll. Somit ist es wichtig, dass der sssd-user auch alle Objekte lesen kann. Deshalb erweitern wir im nächsten Schritt die ACL mit der Nummer 3, so wie Sie es in Listing 9.11 sehen:
dn: olcDatabase={2}mdb,cn=config changetype: modify delete: olcAccess olcAccess: {3} - add: olcAccess olcAccess: {3} to * by dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth manage by dn.exact=uid=sssd-user,ou=users,dc=example,dc=net read
Eine erneute Suche mit dem sssd-user zeigt jetzt wieder alle Objekte.
9.1.4.1 | Neue Position für eine ACL |
Wenn Sie eine bestehende ACL an eine andere Stelle verschieben möchten, werden immer zwei Schritte benötigt. Im ersten Schritt entfernen Sie die ACLs an der derzeitigen Stelle, und im zweiten Schritt legen Sie die ACL erneut an, wobei Sie jetzt die neue Position angeben. In Listing 9.12 sehen Sie die entsprechende LDIF-Datei für die Änderung der Position:
dn: olcDatabase={2}mdb,cn=config changetype: modify delete: olcAccess olcAccess: {1} dn: olcDatabase={1}mdb,cn=config changetype: modify add: olcAccess olcAccess: {2}to attrs=userPassword by anonymous auth by self write by * none
Da hier zwei einzelne Änderungen an der Konfiguration vorgenommen werden, sehen Sie auch zwei Ergebnisanzeigen. In Listing 9.13 sehen Sie die Ausführung des Kommandos und die Anzeige der Ergebnisse:
root@provider01:~# ldapmodify -Y EXTERNAL -H ldapi:/// \ -f reihenfolge-zwei-mod.ldif SASL/EXTERNAL authentication started SASL username: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth SASL SSF: 0 modifying entry "olcDatabase={2}mdb,cn=config" modifying entry "olcDatabase={2}mdb,cn=config"
Sie können die Änderung der Reihenfolge auch mit einer Änderung vornehmen. Im Beispiel aus Listing 9.14 sehen Sie die LDIF-Datei, um alles in einer Änderung durchzuführen. Mit dieser Änderung wird die ursprüngliche Reihenfolge wieder hergestellt:
dn: olcDatabase={2}mdb,cn=config changetype: modify delete: olcAccess olcAccess: {2} - add: olcAccess olcAccess: {1}to attrs=userPassword by anonymous auth by self write by * none
Dazu dann in Listing 9.15 das Kommando und die Ausgabe:
root@provider01:~# ldapmodify -Y EXTERNAL -H ldapi:/// \ -f acl/reihenfolge-ein-mod.ldif SASL/EXTERNAL authentication started SASL username: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth SASL SSF: 0 modifying entry "olcDatabase={2}mdb,cn=config"
Welche Version Sie wählen, spielt hier keine Rolle, das Ergebnis ist immer dasselbe – die Position der ACL verschiebt sich.
9.1.4.2 | Löschen von ACLs |
Um eine ACL endgültig zu löschen, benötigen Sie auch hier eine LDIF-Datei. Wenn Sie nur eine ACL löschen wollen, brauchen Sie lediglich die Nummer der ACL und die Nummer der Datenbank, aus der Sie die ACL löschen wollen. In Listing 9.16 sehen Sie die LDIF-Datei:
dn: olcDatabase={2}mdb,cn=config changetype: modify delete: olcAccess olcAccess: {10}
Löschen Sie die ACL mit der Nummer 10, rutschen alle darunter liegenden ACLs auf, sprich aus 11 wird 10, aus 12 wird 11 usw. Es entstehen niemals Lücken in der Liste der ACLs.
Wollen Sie mehrere ACLs aus einer Datenbank löschen, ist die Reihenfolge der ACLs in der LDIF-Datei absolut wichtig. Die Reihenfolge muss absteigend sein, so wie Sie es in Listing 9.17 sehen:
dn: olcDatabase={2}mdb,cn=config changetype: modify delete: olcAccess olcAccess: {14} - delete: olcAccess olcAccess: {10} - delete: olcAccess olcAccess: {8} - delete: olcAccess olcAccess: {7}
Würden Sie die ACLs in aufsteigender Reihenfolge löschen, wäre das Ergebnis ein komplett anderes und nicht das, was Sie wollen. Beim Löschen wird jetzt als Erstes die ACL mit der Nummer 14 gelöscht, und alle darunter liegenden ACLs rutschen um eine Stelle hoch. Die ACLs mit den Nummern 10, 8 und 7 verändern ihre Position aber nicht. Dann geht es weiter mit dem Löschen der anderen ACLs. Es werden also genau die gewünschten ACLs gelöscht.
Würden Sie das Löschen in umgekehrter Reihenfolge in die LDIF-Datei schreiben, würde als Erstes die ACL mit der Nummer 7 gelöscht. Dadurch rücken alle ACLs um eine Stelle weiter vor. Aus der ACL mit der Nummer 8 wird 7 und aus 9 wird die Nummer 8. Wenn dann im nächsten Schritt die ACL mit der Nummer 8 gelöscht wird, handelt es sich dabei um die eigentliche Nummer 9. Das Ergebnis ist ein komplett anderes.
9.1.4.3 | ACLs mit Filtern |
Bis zu diesem Zeitpunkt haben wir ACLs auf Objekte angewendet oder Objekten im LDAP Rechte gegeben. Es gibt aber noch eine weitere Möglichkeit, wie Sie ACLs nutzen können – die Filter. Bei Filtern können Sie beliebige Filter für Attribute oder Objektklassen einrichten und dann Benutzern Rechte an den Attributen und Objektklassen geben oder nehmen, die dem Filter entsprechen. So können Sie zum Beispiel den Zugriff für bestimmte Benutzer auf eine Objektklasse einschränken. Eine mögliche ACL sehen Sie in Listing 9.18:
dn: olcDatabase={2}mdb,cn=config changetype: modify add: olcAccess olcAccess: {16} to filter=(objectClass=posixAccount) by dn.sub ou=users,dc=example,dc=net read - add: olcAccess olcAccess: {17} to dn.sub=ou=users,dc=example,dc=net filter=(sn) by dn.exact=uid=sn-changer,ou=users,dc=example,dc=net write by * break
Im ersten Beispiel wird nur ein Filter genutzt, um das what-Feld zu beschreiben. Dieser Filter trifft damit auf alle Objekte zu, die die Objektklasse posixAccount beinhalten. Selbst wenn die Benutzer aus der OU ou=users,dc=example,dc=net das Schreibrecht an allen Eigenschaften der Objekte in einer OU besitzen, könnten Sie alle Attribute der Objektklasse posixAccount nicht verändern.
Als Filter können Sie alle in Kapitel 8, «LDAP-Filter», besprochenen Filter nutzen.
9.1.5 | ACL und grafische Werkzeuge |
Selbstverständlich können Sie die ACLs auch mit grafischen Werkzeugen bearbeiten und sind nicht einzig und alleine auf die Kommandozeile angewiesen. Mittlerweile kann auch der LDAP Account Manager (LAM) auf die dynamische Konfiguration zugreifen, sodass Sie damit auch ein Web-basiertes Werkzeug haben, um die Konfiguration zu bearbeiten. Die Einrichtung und erste Schritte mit dem LAM finden Sie in Kapitel 7, «Grafische Werkzeuge».
Weiterhin stehen Ihnen die Werkzeuge Eclipse und Apache Directory Studio zur Verfügung. Alle drei Werkzeuge sind Open Source und können von Ihnen kostenfrei genutzt werden. Das Apache Directory Studio und Eclipse basieren auf Java und werden lokal auf einem PC installiert. Die Java-basierten Werkzeuge gibt es sowohl für Windows als auch für MacOS und Linux. Wir werden hier das Apache Directory Studio und den LAM für die Bearbeitung der ACLs nutzen. Unter https://directory.apache.org/studio/ können Sie die zu Ihrem Betriebssystem passende Version herunterladen. Nach dem Download entpacken Sie die Datei und wechseln in das Verzeichnis, das beim Entpacken entstanden ist. Dort starten Sie das Programm mit dem Kommando ApacheDirectoryStudio.
Verbinden Sie sich mit der Konfiguration cn=config, verwenden Sie dafür den rootdn der Konfiguration, denn im Moment gibt es keinen anderen Benutzer, der schreibend auf die Konfiguration zugreifen darf.
Wenn Sie nach der Anmeldung den DIT öffnen, sehen Sie darunter den Eintrag cn=config. Ein Klick auf den Eintrag zeigt Ihnen alle Untereinträge. Wenn Sie jetzt auf den Eintrag für Ihre Objektdatenbank klicken, sehen Sie im mittleren Feld die Konfiguration Ihres LDAP-Servers. Auch die ACLs tauchen hier auf, wie Sie es in Bild 9.2 sehen.
Bild 9.2
Doppelklicken Sie auf eine der ACLs, können Sie die ACL bearbeiten. Auch die Anpassung der Position können Sie hier durchführen. Ändern Sie einfach die Zahl in den geschweiften Klammern, und die ACL wird an die neue Position verschoben. Über einen Rechtsklick in dem Fenster kommen Sie in ein Kontextmenü, und dort können Sie den Punkt NEUES ATTRIBUT auswählen, um eine neue ACL zu erstellen.
Seit der Version 7.9 des LAM können Sie auch die Konfiguration im cn=config bearbeiten, also auch die ACLs. Verbinden Sie sich mit cn=config Ihres LDAP-Servers und suchen sich auf der linken Seite die Datenbank, bei der Sie die ACLs ändern, löschen oder hinzufügen wollen. Mit einem Klick auf die Datenbank sehen Sie dann auf der rechten Seite die Konfiguration. Bild 9.3 zeigt die momentan eingetragenen ACLs.
Bild 9.3
Über die doppelten Pfeile vor der ACL können Sie die ACL in der Reihenfolge verschieben. Indem Sie die Maus auf dem Symbol platzieren, dann die linke Maustaste drücken und gedrückt halten, können Sie die ACL an die gewünschte Stelle verschieben. Die Nummerierung der ACL ändert sich dann automatisch.
Wenn Sie auf das grüne Plus direkt hinter der ACL klicken, können Sie unterhalb der ACL eine neue ACL einfügen.
Durch einen Klick auf das rote Kreuz können Sie die entsprechende ACL löschen.
Klicken Sie irgendwo in den Text der ACL, können Sie eine bestehende ACL ändern.
In Zukunft können Sie alle ACLs auch grafisch verwalten. Im weiteren Verlauf des Buches werden wir die ACLs aber immer über LDIF-Dateien einpflegen, so können Sie unsere Beispiele direkt per LDIF übernehmen.
9.1.6 | Rechte für den LDAP-Admin |
In Kapitel 6, «Erste Schritte in der Objektverwaltung», haben wir einen Benutzer angelegt, der in Zukunft den LDAP-Baum verwalten können soll. Dazu benötigt der Benutzer die entsprechenden Berechtigungen. In Listing 9.19 sehen Sie noch einmal die Übersicht über alle ACLs, die momentan in der Konfiguration angelegt sind:
olcAccess: {0}to dn.exact=dc=example,dc=net by users read olcAccess: {1}to attrs=userPassword by anonymous auth by self write by * none olcAccess: {2} to attrs=shadowLastChange by anonymous auth by self write by * none olcAccess: {3} to * by dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth manage by dn.exact=uid=sssd-user,ou=users,dc=example,dc=net read
Durch weitere ACLs wird dafür gesorgt, dass der ldap-admin die benötigten Rechte bekommt. Denn der ldap-admin ist der Benutzer, mit dem später alle Änderungen am LDAP-Baum durchgeführt werden sollen. Der rootDN wird nur noch dafür benötigt, um neue Administratoren einzurichten oder den LDAP-Baum in verschiedene administrative Bereiche zu unterteilen. Der neue Administrator benötigt die folgenden Rechte, umalle Änderungen durchführen zu können:
1. Am DN dc=example,dc=net benötigt er das Schreibrecht.
2. An allen Objekten und Attributen unterhalb von dc=example,dc=net benötigt er ebenfalls das Schreibrecht.
Warum nicht einfach dem ldap-admin ab dc=example,dc=net alle Rechte geben? Wenn Sie die Berechtigung aufteilen (einmal exakt für dc=example,dc=net und dann noch für die untergeordneten Objekte, also die children), können Sie dafür sorgen, dass der ldap-admin zwar in allen bestehenden OUs unterhalb von dc=example,dc=net Änderungen vornehmen kann. Aber wenn Sie das Recht auf dc=example,dc=net für den ldap-admin auf read setzen, kann der ldap-admin auf der Ebene keine neuen Objekte anlegen. Das würde zum Beispiel Sinn machen, wenn Ihr Unternehmen mehrere Standorte hat und der ldap-admin nicht in der Lage sein soll, einen neuen Standort anzulegen.
Damit der ldap-admin die Rechte an dc=example,dc=net bekommt, brauchen Sie keine weiteren Änderungen, denn das Leserecht erhält er bereits durch die bestehende ACL mit der Nummer 0. Nur wenn Ihr ldap-admin an der Stelle auch schreiben können soll, brauchen Sie eine LDIF-Datei, um die ACL zu ändern. Ergänzen Sie die ACL für dc=example,dc=net, um eine neue by-Zeile, wie Sie sie in Listing 9.20 sehen:
dn: olcDatabase={2}mdb,cn=config changetype: modify delete: olcAccess olcAccess: {0} - add: olcAccess olcAccess: {0} to dn.exact=dc=example,dc=net by dn.exact=cn=ldap-admin,ou=users,dc=example,dc=net write by users read by * none
Wichtig: Achten Sie darauf, dass die by-Zeile auf jeden Fall vor der Zeile für users steht, da der ldap-admin auch ein Benutzer ist und somit für ihn auch die Rechte für users gelten würde, wenn die eigene ACL nicht vor der Regel für users stände.
Aber wie regeln Sie den Schreibzugriff auf alle anderen Einträge, die sich unterhalb von dc=example,dc=net befinden? Dafür kann die ACL mit der Nummer 3 geändert werden, denn über diese ACL werden Rechte auf alle Objekte vergeben. Aber was ist mit den Passwörtern? Die sind in zwei eigenen ACLs 1 und 2 abgesichert. Jetzt besteht die Möglichkeit, einfach die ACL mit der Nummer 3 in der Reihenfolge auf die Nummer 1 zu verschieben, dann hätte aber der sssd-user Rechte an den Passwörtern, die er nicht braucht.
Im Moment bliebe nur die Lösung, den ldap-admin zusätzlich, mit Schreibrecht, in die beiden ACLs einzutragen. Später werden wir Ihnen eine elegantere Lösung durch den Einsatz von Gruppen und/oder dynamischen Gruppen zeigen.
Um den ldap-admin mit den benötigten Rechten auszustatten, sehen Sie in Listing 9.21 die ACLs für die Änderung der Rechte:
dn: olcDatabase={2}mdb,cn=config changetype: modify delete: olcAccess olcAccess: {3} - delete: olcAccess olcAccess: {2} - delete: olcAccess olcAccess: {1} - add: olcAccess olcAccess: {1}to attrs=userPassword by anonymous auth by self write by dn.exact=uid=ldap-admin,ou=users,dc=example,dc=net write by dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth manage by * none - add: olcAccess olcAccess: {2} to attrs=shadowLastChange by anonymous auth by self write by dn.exact=uid=ldap-admin,ou=users,dc=example,dc=net write by dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth manage by * none - add: olcAccess olcAccess: {3} to * by dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth manage by dn.exact=uid=sssd-user,ou=users,dc=example,dc=net read by dn.exact=uid=ldap-admin,ou=users,dc=example,dc=net write by * break
Hier sehen Sie dann noch einmal, dass erst die bestehenden ACLs gelöscht werden, und zwar in umgekehrter Reihenfolge, und dann die neuen, geänderten ACLs hinzugefügt werden.
Hinweis: Wenn Sie auch weiterhin über die SASL-Methode EXTERNAL die Passwörter ändern und auslesen wollen, ist es notwendig, dass Sie die ACL um die entsprechenden Zeilen erweitern.
Doch warum die Zeile by * break in der ACL mit der Nummer 3? Der * trifft auf alle Objekte und Attribute zu, die bis zu diesem Zeitpunkt noch über eine vorherige ACL abgedeckt sind. Der Standardwert, wenn kein Eintrag einer ACL auf einen Zugriff passt, ist stop, sprich die Überprüfung wird abgebrochen. Alle folgenden ACLs, sofern es noch welche gibt, würden nicht mehr geprüft. Da wir im Anschluss weiter ACLs für tiefer im Baum liegende Objekte vergeben wollen, ist es notwendig, dass an dieser Stelle auf jeden Fall die Liste der ACLs weiter geprüft wird, und das geht mit einem by * break.
Jetzt haben Sie einen Benutzer, mit dem Sie sämtliche Änderungen an Objekten vornehmen können. Soll der Benutzer auch in der Lage sein, die Konfiguration zu ändern, dann brauchen Sie eine weitere ACL. Dieses Mal werden mit der ACL aber die Rechte an der Konfigurationsdatenbank angepasst. Listing 9.22 zeigt die LDIF-Datei für die Änderungen:
dn: olcDatabase={0}config,cn=config changetype: modify delete: olcAccess olcAccess: {0} - add: olcAccess olcAccess: {0}to * by dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth manage by dn.exact=uid=ldap-admin,ou=users,dc=example,dc=net write
9.2 | ACLs in der Praxis |
Im vorherigen Abschnitt ging es darum, wie Sie mit ACLs arbeiten und worauf Sie besonderes Augenmerk haben sollten. Um Ihnen ein Bild der Umsetzung in der Praxis zu geben, werden wir in diesem Abschnitt Regeln erstellen, mit denen Sie gezielt Zugriffsrechte vergeben können. Alle Beispiele werden wir anhand der Struktur aus Abschnitt 3.2 des Kapitels «Installation des ersten OpenLDAP» erläutern. Als Grundlage werden die ACLs des vorherigen Abschnitts genutzt.
9.2.1 | Rechte an der eigenen Abteilung |
Im Moment ist es noch so, dass ein Benutzer, der Mitarbeiter einer bestimmten Abteilung ist, nur Rechte auf die oberste Ebene des LDAP-Baums hat. In Listing 9.23 sehen Sie den Zugriff eines Benutzers auf den LDAP-Baum mit dem Kommando ldapsearch:
root@provider01:~# ldapsearch -x -D "cn=u1-prod,ou=users,ou=produktion,\ ou=firma,dc=example,dc=net" -W -LLL Enter LDAP Password: dn: dc=example,dc=net objectClass: domain objectClass: dcObject dc: example
Dadurch, dass die ACLs jedem angemeldeten Benutzer das Leserecht auf dc=example, dc=net geben, bekommt der Benutzer dieses Element angezeigt; an allen anderen darunter liegenden Objekten hat der Benutzer aber keine Rechte. Durch die ACL in Listing 9.24 können Sie den Benutzern aus den einzelnen Abteilungen jetzt das Leserecht an allen Objekten der eigenen Abteilung geben:
dn: olcDatabase={2}mdb,cn=config changetype: modify add: olcAccess olcAccess: {4}to "dn.sub=ou=verwaltung,ou=firma,dc=example,dc=net" by "dn.children=ou=users,ou=verwaltung,ou=firma,dc=example,dc=net" read by * none - add: olcAccess olcAccess: {5}to "dn.sub=ou=produktion,ou=firma,dc=example,dc=net" by dn.children="ou=users,ou=produktion,ou=firma,dc=example,dc=net" read by * none - add: olcAccess olcAccess: {6}to "dn.exact=ou=firma,dc=example,dc=net" by users read by * none
Hinweis: Hätten wir bei der ACL 3 nicht am Ende die Zeile by * break eingetragen, würden diese ACLs niemals durchlaufen, und die Benutzer würden die benötigten Berechtigungen nicht erhalten.
Spielen Sie die LDIF-Datei wie in den vorherigen Beispielen ein und prüfen Sie anschließend mit einem Benutzer aus jeder Abteilung, ob die entsprechenden Objekte angezeigt werden. In Listing 9.25 sehen Sie den Zugriff eines Benutzers aus der Abteilung Produktion:
root@provider01:~# ldapsearch -x -D cn="u1-prod,ou=users,\ ou=produktion,ou=firma,dc=example,dc=net" -W -LLL dn Enter LDAP Password: dn: dc=example,dc=net dn: ou=firma,dc=example,dc=net dn: ou=Produktion,ou=firma,dc=example,dc=net dn: ou=users,ou=Produktion,ou=firma,dc=example,dc=net dn: ou=groups,ou=Produktion,ou=firma,dc=example,dc=net dn: cn=Produktion,ou=groups,ou=Produktion,ou=firma,dc=example,dc=net dn: cn=u1-Prod,ou=users,ou=Produktion,ou=firma,dc=example,dc=net dn: cn=u2-prod,ou=users,ou=Produktion,ou=firma,dc=example,dc=net dn: cn=Al-Prod,ou=users,ou=Produktion,ou=firma,dc=example,dc=net
Es passiert genau das Erwartete: Die Benutzer aus den einzelnen Abteilungen können alle Objekte der eigenen Abteilung sehen. Im Gegensatz dazu werden die Mitarbeiter der anderen Abteilungen und die OUs ou=user,dc=example,dc=net und ou=groups,dc=example, dc=net nicht angezeigt. In diesen OUs sollen abteilungsübergreifende Benutzer und Gruppen wie der Benutzer uid=ldap-admin und die Gruppe cn=benutzer verwaltet werden. Auch die Benutzer, die für Dienste benötigt werden (wie der uid=sssd-user), werden dort verwaltet.
Lassen Sie sich die Konfiguration anzeigen, um die ACLs zu prüfen. Listing 9.26 zeigt die geänderten Berechtigungen:
olcAccess: {0}to dn.exact=dc=example,dc=net by users read olcAccess: {1}to attrs=userPassword by anonymous auth by self write by dn.exact=uid=ldap-admin,ou=users,dc=example,dc=net write by dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth manage by * none olcAccess: {2} to attrs=shadowLastChange by anonymous auth by self write by dn.exact=uid=ldap-admin,ou=users,dc=example,dc=net write by dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth manage by * none olcAccess: {3} to * by dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth manage by dn.exact=uid=sssd-user,ou=users,dc=example,dc=net read by dn.exact=uid=ldap-admin,ou=users,dc=example,dc=net write by * break olcAccess: {4}to "dn.sub=ou=verwaltung,ou=firma,dc=example,dc=net" by "dn.children=ou=users,ou=verwaltung,ou=firma,dc=example,dc=net" read by * none olcAccess: {5}to "dn.sub=ou=produktion,ou=firma,dc=example,dc=net" by dn.children="ou=users,ou=produktion,ou=firma,dc=example,dc=net" read by * none olcAccess: {6}to "dn.exact=ou=firma,dc=example,dc=net" by users read by * none
Aber warum haben wir hier noch eine extra ACL für die Ebene ou=firma,dc=example,dc=net eingefügt?
Ohne diese Berechtigung wäre es für keinen Benutzer möglich, auf die Objekte der eigenen Abteilung zuzugreifen. Da kommt wieder der Vergleich Open LDAP mit einem Gebäude. Sie haben einen Schlüssel für Ihr Büro, aber keinen Schlüssel für die Eingangstür des Gebäudes: Wenn Sie draußen vor der Tür stehen, hilft Ihnen Ihr Büroschlüssel auch nicht weiter. Für jede Tür zwischen dem Haupteingang und Ihrer Bürotür müssen Sie einen Schlüssel besitzen, um in Ihr Büro zu gelangen. Genauso ist es auch im LDAP-Baum: Soll ein Benutzer auf bestimmte Objekte zugreifen können, benötigt er Berechtigungen auf dem gesamten Pfad zu diesem Objekt.
In Bild 9.4 sehen Sie das Ergebnis des Zugriffs im LAM.
Bild 9.4
Tipp: Es ist immer eine gute Idee, nach der Veränderung der ACLs eine grafische Anmeldung mit einem betroffenen Benutzer durchzuführen. So können Sie testen, ob das Ergebnis auch das ist, was Sie erwartet haben.
9.2.2 | Rechte für Gruppen |
Sie können Rechte nicht nur direkt an Benutzer vergeben, sondern auch eine Gruppe im LDAP-Baum kann Rechte bekommen. Aber Achtung, das Verschachteln von Gruppen klappt so noch nicht, dazu kommen wir später. Die Mitglieder der Gruppe erhalten dann die entsprechenden Rechte an den LDAP-Objekten. Der Weg über Gruppen ist in den meisten Fällen besser, als einzelnen Benutzer Rechte zu geben. Wer kennt das nicht? Sie geben einem Benutzer Rechte, und wenig später benötigt ein zweiter Benutzer die identischen Berechtigungen und dann ein dritter und so weiter und so weiter. Wenn Sie bei der Vergabe von Berechtigungen nicht mit Gruppen arbeiten würden, müssten Sie jeden Benutzer einzeln in die ACL eintragen, und Ihre ACL-Liste würde immer länger. Je länger eine ACL-Liste ist, desto länger dauert die Überprüfung bei jedem Zugriff.
Jetzt können Sie nicht einfach eine Posix-Gruppe nehmen und dieser Gruppe Rechte im LDAP-Baum geben, denn in einer Posix-Gruppe steht immer nur der Anmeldename des Benutzers und nicht sein vollständiger DN. Aber genau den benötigt LDAP zur Überprüfung der Berechtigungen. Denn im LDAP – im Gegensatz zum Active Directory – kann es Objekte in verschiedenen OUs geben, die denselben RDN haben. Die Eindeutigkeit eines Objekts im OpenLDAP kann nur über den DN ermittelt werden.
Um Berechtigungen an Gruppen zu vergeben, stehen Ihnen drei verschiedene Gruppenobjekte zur Verfügung:
GroupOfNames
Bei dem Objekt GroupOfNames werden die Mitglieder über das Attribut Members verwaltet. Für jeden Benutzer wird der DN als Mitgliedsname verwaltet. Sie können eine Gruppe dieses Typs anlegen, ohne sofort ein Mitglied zuzuordnen.
GroupOfUniqueNames
Bei der GroupOfUniqueNames werden die Benutzer mit dem Attribut UniqueMember verwaltet. Hier ist es notwendig, dass Sie beim Anlegen der Gruppe unbedingt einen Benutzer über das entsprechende Attribut eintragen. Ohne einen Benutzer lässt sich die Gruppe nicht anlegen.
groupOfURLs
Im Gruppenobjekt groupOfURLs werden die Mitglieder über das Attribut Member verwaltet. In dem Attribut Member wird dort auch der DN eingetragen. Dieser Gruppentyp wird dafür verwendet, die Gruppenmitgliedschaften dynamisch zu verwalten. In Kapitel 10, «Erweiterte Funktionen durch Overlays», werden wir später auf die Verwaltung von dynamischen Gruppen eingehen.
Hinweis: Wollen Sie an dieser Stelle eine groupOfURLs anlegen, benötigen Sie das Schema dynlist.schema in Ihrer Konfiguration, da das Objekt Bestandteil dieses Schemas ist.
Zur Vergabe von Berechtigungen soll jetzt die Objektklasse GroupOfUniqueNames verwendet werden. Die folgende Aufgabe soll mithilfe der GroupOfUniqueNames gelöst werden:
Alle Abteilungsleiter sollen in der Lage sein, die Passwörter ihrer Mitarbeiter der eigenen Abteilung zu ändern. Dazu wird einer Gruppe das Recht verliehen, und die Abteilungsleiter werden zur Gruppe hinzugefügt.
Im ersten Schritt legen Sie die neue Gruppe an, das können Sie entweder über eine LDIF-Datei oder ein grafisches Werkzeug realisieren. In Listing 9.27 sehen Sie die LDIF-Datei für diese Gruppe:
dn: cn=pw-set,ou=groups,dc=example,dc=net objectClass: groupOfNames objectClass: top cn: pw-set Member: cn=prod-al,ou=users,ou=produktion,ou=firma,dc=example,dc=net Member: cn=verw-al,ou=users,ou=verwaltung,ou=firma,dc=example,dc=net
Wie Sie in dem Listing sehen, haben wir hier sofort beide Abteilungsleiter als Mitglied eingetragen. Um das Objekt anlegen zu können, benötigen Sie an dieser Stelle mindestens einen Benutzer, den Sie als Mitglied eintragen wollen. Ohne dass Sie ein Mitglied mit dem vollständigen DN angegeben haben, lässt sich das Objekt nicht anlegen.
Jetzt geben Sie der Gruppe noch das Schreibrecht an den Passwörtern. Dazu erweitern Sie die ACLs für die Attribute userPassword und shadowLastChange, wie Sie sie in Listing 9.28 sehen:
dn: olcDatabase={2}mdb,cn=config changetype: modify delete: olcAccess olcAccess: {1} - add: olcAccess olcAccess: {1}to attrs=userPassword by dn.exact=uid=ldap-admin,ou=users,dc=example,dc=net write by group/GroupOfNames/member=cn=pw-set,ou=users,dc=example,dc=net write by self write by anonymous auth by * none - delete: olcAccess olcAccess: {2} - add: olcAccess olcAccess: {2}to attrs=shadowLastChange by dn.exact=uid=ldap-admin,ou=users,dc=example,dc=net write by group/GroupOfNames/member=cn=pw-set,ou=users,dc=example,dc=net write by self write by * none
Jeder der beiden Abteilungsleiter könnte, wenn man diese Regeln alleine betrachtet, alle Passwörter aller Benutzer aus beiden Abteilungen ändern. Dadurch, dass jeder der beiden Abteilungsleiter nur Zugriff auf die Objekte in seiner Abteilung hat, kann jeder Abteilungsleiter letztendlich nur die Passwörter der eigenen Mitarbeiter ändern. Das zeigt auch, je mehr ACLs in Ihrem Baum zum Einsatz kommen, um so wichtiger ist es, dass Sie neue ACLs genau planen und möglichst erst in einer Testumgebung prüfen. Die Abhängigkeiten einer neuen ACL zu bereits bestehenden ACLs sind nicht zu unterschätzen.
Immer wenn Sie einer Gruppe von Personen Rechte an bestimmten Objekten im LDAP-Baum geben wollen, können Sie dieses Beispiel nutzen.
9.2.3 | Rechte für ein simpleSecurityObject |
Wenn Sie ein simpleSecurityObject für einen Dienst anlegen und der Dienst auf bestimmte Objekte zugreifen können muss, ist es notwendig, dass Sie für dieses Objekt noch die entsprechenden ACLs einrichten.
Ein simpleSecurityObject wurde bereits für den sssd und den ldap-admin angelegt. Der sssd-user muss in der Lage sein, die Benutzerobjekte im LDAP-Baum zu finden und für die Authentifizierung bereitstellen zu können. Damit muss der sssd-Benutzer lesenden Zugriff auf alle Benutzer im LDAP-Baum erhalten. Ein Zugriff auf die Passwörter ist für den Benutzer nicht notwendig, denn die eigentliche Authentifizierung wird vom Benutzer ausgeführt.
Prüfen Sie mit ldapsearch und dem sssd-user, welche Informationen für den sssd-user im Moment angezeigt werden. Neben dem Auflisten der Objekte können Sie auch noch prüfen, ob Sie mit dem Kommando getent passwd <ein-ldap-benutzer> den Benutzer angezeigt bekommen.
Tipp: Wenn Sie für jede Abteilung einen eigenen sssd-Benutzer anlegen würden, könnten Sie auf den entsprechenden Clients in der Abteilung den passenden sssd-Benutzer eintragen, der dann nur auf die Benutzer der Abteilung Rechte besitzt. So könnten Sie verhindern, dass sich Benutzer aus einer Abteilung an PCs in einer anderen Abteilung anmelden können.
9.2.4 | ACLs mit regulären Ausdrücken |
Bis zu diesem Punkt beziehen sich alle ACLs immer genau auf ein Objekt, und Sie vergeben Rechte über die by-Zeile auch an einzelne Objekte. Was aber, wenn Sie an sehr vielen verschiedenen Objekten, eventuell sogar an unterschiedlichen Stellen, unterschiedlichen Benutzern Rechte vergeben wollen? Dann wird es Zeit für ACLs mit regulären Ausdrücken.
Bevor Sie sich jetzt überlegen, wie Sie möglichst viele ACLs über reguläre Ausdrücke erstellen können, folgen hier einige zu berücksichtigende Punkte:
Planen Sie Ihren LDAP-Baum so, dass die Struktur übersichtlich ist und die regulären Ausdrücke nicht zu kompliziert werden. Theoretisch unterstützt OpenLDAP die erweiterten regulären Ausdrücke, aber nicht alles, was möglich ist, ist auch sinnvoll.
Ein Namensstandard beim Aufbau der Struktur vereinfacht reguläre Ausdrücke.
Je mehr reguläre Ausdrücke Sie bei den ACLs verwenden, desto aufwendiger ist die Überprüfung der Berechtigungen.
Überlegen Sie genau, ob es nicht auch ohne reguläre Ausdrücke geht, indem Sie vielleicht zwei oder drei einfache ACLs erstellen.
Wohl einer der wichtigsten Punkte: Dokumentieren Sie Ihre regulären Ausdrücke genau, sodass Sie oder jemand anders auch später in der Lage ist, die Ausdrücke auszuwerten und zu verstehen.
Um Ihnen die regulären Ausdrücke anhand eines Beispiels zu erklären, wollen wir an dieser Stelle jedem Benutzer eine OU zuordnen, die direkt unterhalb seines Objekts liegt und die er als Adressbuch nutzen kann. Kein anderer Benutzer soll Zugriff auf die OU und die darin liegenden Objekte haben. Auch die Benutzer derselben Abteilung sollen keinen Zugriff erhalten.
Als Beispiel sehen Sie hier den DN für das Adressbuch des Benutzers cn=u1-prod aus der Produktion ou=adressen,cn=u1-prod,ou=users,ou=produktion,ou=firma,dc=example, dc=net. In unserem Beispielbaum sind die benötigten OUs für die Benutzer bereits angelegt. Auf diese Objekte werden wir hier auch das Beispiel einrichten.
Wenn Sie sich die Struktur der Abteilungen und Benutzer noch einmal anschauen, werden Sie sehen, dass wir in beiden Abteilungen eine einheitliche Namensgebung für die OUs gewählt haben. Auch die Struktur der Abteilungen ist identisch aufgebaut. Hier zeigt sich jetzt der Vorteil, dass die Struktur für alle Abteilungen einheitlich aufgebaut wurde und nicht jede Abteilung andere Ebenen und Namen verwendet. So ist es möglich, mit nur einer ACL den Zugriff für alle Benutzer zu regeln. In Listing 9.29 sehen Sie die ACL:
dn: olcDatabase={2}mdb,cn=config changetype: modify add: olcAccess olcAccess: {4}to dn.regex="ou=adressen,cn=(.+),ou=users,ou=(.+),ou=firma,\ dc=example,dc=net$" by dn.regex="$1,ou=users,ou=$2,ou=firma,dc=example,dc=net$" write by * none
Platzieren Sie die ACL direkt unterhalb der ACL für das Attribut shadowLastChange. Die ACL liegt dann oberhalb der ACL für die Abteilungen. Die Regel wird dann vor der Regel für die Abteilung geprüft, und somit erhält der Benutzer die Rechte. Würden Sie die Adressbuch-ACL unterhalb der ACL für die Abteilung legen, würde die ACL nie zutreffen. Denn die Regel trifft auf alle Objekte unterhalb von ou=<Abteilung> zu.
Jetzt können die Benutzer aus den Abteilungen in der eigenen Adressen-OU neue Objekte anlegen, aber sie sind damit auch in der Lage, zum Beispiel über den LAM, Benutzer anzulegen, die sich anschließend an einem System via ssh anmelden können. Das ist keine gute Idee.
Hinweis: Nachdem Sie die ACLs eingetragen haben, kommt nur noch der Benutzer an seine Adressen. Auch der vorher eingerichtete ldap-admin kann auf die privaten Adressen der Benutzer nicht mehr zugreifen. Die einzige Möglichkeit ist, dass Sie mit der rootdn-Kennung auf den LDAP zugreifen. Denn den rootdn können Sie nie in seinen Rechten beschneiden.
9.2.5 | ACLs mit Filtern in der Praxis |
Jetzt kommen die Filter für ACLs zum Einsatz. Damit sich ein Benutzer an einem System anmelden kann, wird die objectClass posixAccount benötigt. Über eine ACL mit Filter können Sie den Zugriff auf eine objectClass einschränken. Für alle Benutzer aus den Abteilungen soll der Zugriff auf die Objektklasse, in ihrem Adressbuch, nur lesend erlaubt sein. In Listing 9.30 sehen Sie die ACL:
dn: olcDatabase={2}mdb,cn=config changetype: modify add: olcAccess olcAccess: {4} to dn.regex=ou=adressen,cn=(.+),ou=users,ou=(.+)\ ,ou=firma,dc=example,dc=net filter=(objectClass=posixAccount) by dn.regex=cn=(.+),ou=users,ou=(.+),ou=firma,dc=example,dc=net read by * break
Nachdem wir jetzt die Benutzer der Abteilungen weiter eingeschränkt haben, sehen Sie in Bild 9.5 noch einmal eine Übersicht über alle Einträge, die der Benutzer jetzt sehen kann.
Bild 9.5
Nachdem Sie das Beispiel gesehen haben, kommen jetzt noch ein paar Gesichtspunkte, die Sie nicht aus den Augen verlieren dürfen:
Verwenden Sie, wenn der reguläre Ausdruck mindestens ein Zeichen enthalten soll, immer (.+) anstelle von (.*), da der Stern auch für kein Vorkommen des letzten Zeichens steht. Der Ausdruck dn.regex=.*dc=example,dc=net würde alle Objekte und alle Attribute einschließen, inklusive des Objekts dc=example,dc=net.
Beginnen Sie möglichst jeden Ausdruck mit dem Zeichen ^ (Ausdruck steht am Anfang der Zeile). Beenden Sie jeden Ausdruck mit einem $. Denn wenn Sie zum Beispiel einen regulären Ausdruck dn.redex=.*,dc=example,dc=net erstellen, trifft der Ausdruck auch dann zu, wenn das Muster mitten in eine manderen String steht. So würde der Ausdruck auch auf uid=u1,ou=users,ou=abt1,dc=example,dc=net,dc=home zutreffen.
Verwenden Sie keine regulären Ausdrücke, um die speziellen Objekte users, anonymous oder self zu ersetzen.
Um zu sehen, wie der slapd die ACLs abarbeitet, setzen Sie das Loglevel acl. Anschließend greifen Sie auf eines der Adressbücher zu und schauen sich parallel mit journalctl -f die Meldungen an. Sie werden sehen, dass für jeden Buchstaben aus dem regulären Ausdruck ein eigener Eintrag im Log zu sehen ist.
9.2.6 | Filtern aufgrund von Hostinformationen |
Oft reicht es nicht aus, den Zugriff auf Objekte für bestimmte Benutzergruppen zu sperren. Manchmal ist es auch sinnvoll, wenn Benutzer sich nur an bestimmten Systemen anmelden dürfen. So macht es zum Beispiel Sinn, dass Mitarbeiter, die Zugriff auf personenbezogene Daten haben, sich nur an bestimmten Systemen anmelden dürfen. Oder Sie wollen verhindern, dass sich Administratoren, die nur für Mailserver verantwortlich sind, an Webservern anmelden können. Alle diese Beispiele lassen sich gut über die ACL-Möglichkeit peername abbilden.
Im folgenden Beispiel wollen wir dafür sorgen, dass sich der Benutzer u1-verw nur an dem Host mit der IP-Adresse 192.168.56.201 anmelden kann. Bevor Sie die ACL erstellen, ist es wichtig zu wissen, an welcher Stelle der bestehenden ACLs die neue Regel eingetragen werden muss. Dazu benötigen Sie die ACL aus Listing 9.31:
dn: olcDatabase={2}mdb,cn=config changetype: modify add: olcAccess olcAccess: {4} to dn.exact="cn=u1-verw,ou=users,ou=verwaltung,ou=firma,\ dc=example,dc=net" by peername.regex=192\.168\.56\.201 read by peername.regex=192\.168\.56\.12 read by * none
Beim Einsatz von peername können Sie auch wieder unterschiedliche style verwenden. Wir nutzen hier peername.regex, da mit diesem style jede IP-Konfiguration abgebildet werden kann. So können Sie ganze Subnetze oder wie in unserem Beispiel den Zugriff auf einzelne IP-Adressen beschränken.
Hinweis: Über die IP-Adresse 192.168.56.12 ist der Webserver mit dem LAM erreichbar, so kann der Benutzer dann noch sein Adressbuch bearbeiten. Ohne diesen Eintrag ginge das nicht.
9.2.7 | ACLs auf Grund von ssf |
Immer wenn ein Client auf einen LDAP-Server zugreift, wird eine bestimmte Stufe der Sicherheit verwendet. Das kann von „keiner Sicherheit“, also unverschlüsselt und mit Klartextpasswort, bis „hohe Sicherheit“ mit dem Einsatz von Kerberos und TLS gehen. Die Sicherheit wird dabei als security strength factor (ssf) bezeichnet. Immer wenn ein Client auf den LDAP-Server zugreift, sehen Sie diesen ssf im Log, so wie in den Beispielen in Listing 9.32:
--------Beispiel 01------------- root@client01:~# ldapsearch -x -D uid=ldap-admin,ou=users,dc=example,dc=net \ -W -LLL -H ldap://provider01.example.net:389 uid=ldap-admin dn Enter LDAP Password: dn: uid=ldap-admin,ou=users,dc=example,dc=net --------Journal auf provider01---------- ACCEPT from IP=192.168.56.71:44802 (IP=0.0.0.0:389) ... BIND dn="uid=ldap-admin,ou=users,dc=example,dc=net" \ mech=SIMPLE bind_ssf=0 ssf=0 --------Beispiel 02--------------------- root@client01:~# ldapsearch -x -D uid=ldap-admin,ou=users,dc=example,dc=net \ -W -LLL -H ldaps://provider01.example.net uid=ldap-admin dn Enter LDAP Password: dn: uid=ldap-admin,ou=users,dc=example,dc=net --------Journal auf provider01---------- ACCEPT from IP=192.168.56.71:57318 (IP=0.0.0.0:636) TLS established tls_ssf=256 ssf=256 tls_proto=TLSv1.3 \ tls_cipher=TLS_AES_256_GCM_SHA384 ... BIND dn="uid=ldap-admin,ou=users,dc=example,dc=net" mech=SIMPLE \ bind_ssf=0 ssf=256 --------Beispiel 03--------------------- root@client01:~# kinit ldap-admin Passwort für ldap-admin@EXAMPLE.NET: root@consumer01:~# ldapsearch -LLL -H ldaps://provider01.example.net \ uid=ldap-admin dn SASL/GSSAPI authentication started SASL username: ldap-admin@EXAMPLE.NET SASL SSF: 56 SASL data security layer installed. dn: uid=ldap-admin,ou=users,dc=example,dc=net --------Journal auf provider01---------- ACCEPT from IP=192.168.56.71:58234 (IP=0.0.0.0:636) TLS established tls_ssf=256 ssf=256 tls_proto=TLSv1.3 \ tls_cipher=TLS_AES_256_GCM_SHA384 ... Apr 11 20:17:19 provider01 slapd[1078]: conn=1004 op=3 BIND \ dn="uid=ldap-admin,ou=users,dc=example,dc=net" mech=GSSAPI \ bind_ssf=56 ssf=256
Hinweis: Die Zahl hinter dem ssf-Parameter gibt die Länge des verwendeten Schlüssels an.
Alle drei Beispiele zeigen den Zugriff von einem LDAP-Client auf einen LDAP-Server, nur mit verschiedenen Verbindungs- und Anmeldesicherheitsstufen. Bei den Auszügen aus dem Log haben wir uns auf die relevanten Zeilen und Informationen beschränkt. Die Beispiele im Einzelnen:
Hier wird eine Anfrage eines Clients über eine ungesicherter ldap://-Verbindung angefordert, das Passwort wird unverschlüsselt und ohne den Einsatz von Kerberos übertragen. Im Log des LDAP-Servers fehlt auch der Aufbau der TLS-Verbindung. Bei der Übermittlung der Anmeldedaten sehen Sie auch: bind_ssf und ssf haben beide den Wert 0. Es wird also ein simple-bind ohne die Verwendung von TLS durchgeführt.
Anstelle von ldap:// wird hier ldaps:// verwendet. Daher ändert sich der Wert für ssf auf 256. Das zeigt, dass TLS genutzt wird. Aber auch hier wird immer noch ein simple-bind verwendet. Das Passwort wird aber bei der Übertragung durch TLS verschlüsselt.
Erst hier wird sowohl mit einem verschlüsselten Passwort, dem Kerberos-Credential, und einer TLS-gesicherten Verbindung die Verbindung zum LDAP-Server hergestellt. Um diesen Stand der Sicherheit zu erreichen, benötigen Sie eine vollständig eingerichtete Kerberos-Infrastruktur. Wie Sie Kerberos einrichten, finden Sie in Kapitel 15, «OpenLDAP mit Kerberos».
Wichtig: Obwohl wir bei der Einrichtung vom Kerberos ausschließlich 256-Bit-Schlüssel verwenden, wird hier immer ein bind_ssf=56 angezeigt. Das ist kein Fehler in der Kerberos-Konfiguration, sondern ein „Fehler“ im Kerberos. Immer wenn GSSA-PI verwendet wird, ist der Wert von 56 fest eingebunden. Es wird also NIE die echte Schlüssellänge angezeigt. Aus dem Grund können Sie in den ACLs keinen anderen Wert als 56 für sasl_ssf nutzen. Erst wenn das Problem im Kerberos behoben wird, kann auch OpenLDAP den richtigen Wert erkennen.
Wollen Sie jetzt verhindern, dass auf bestimmte Objekte oder Attribute nur zugegriffen werden kann, wenn ein vorbestimmter ssf beim Zugriff erkannt wird, können Sie die entsprechenden ACLs erweitern. In Listing 9.33 sehen Sie eine ACL, die die oberste Ebene des Verzeichnisbaums schützt:
olcAccess: {1}to dn.exact=dc=example,dc=net by dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth manage by users ssf=256 sasl_ssf=56 read
Hier sehen Sie, dass sowohl für die Verbindung (ssf=256) als auch für die bind-Methode (sasl_ssf=56) eine höhere Sicherheitsstufe benötigt wird. Alle drei Einträge in der by-Zeile sind dabei UND-verknüpft. Beide Sicherheitsstufen müssen also beim Zugriff eingehalten werden.
So können Sie über ACLs für Teilbereiche eine höhere Sicherheit vorgeben.
Wollen Sie alle Zugriffe auf den gesamten LDAP immer mit einem minimalen ssf belegen, können Sie in der Konfiguration das Attribut olcSecurity setzen. Ein Beispiel für eine LDIF-Datei sehen Sie in Listing 9.34:
dn: cn=config changetype: modify replace: olcSecurity olcSecurity: ssf=1 tls=256
Wobei ssf=1 die zusätzliche Sicherheit aktiviert und tls=265 einen ssf von 256 für TLS verlangt. Denken Sie daran, dass wenn Sie -Y EXTERNAL für den Zugriff auf den LDAP-Server nutzen wollen, kein TLS zum Einsatz kommt und damit diese Art des Zugriffs dann nicht mehr möglich ist.
Wichtig: Seien Sie vorsichtig mit der Verwendung von sasl_ssf. Wenn Sie den Wert größer 0 setzen, kann kein Dienst oder Benutzer, ohne in Besitz eines Kerberos-Tickets zu sein, auf den LDAP-Server zugreifen..
9.2.8 | ACLs mit set |
Kommen wir jetzt zu einem Thema, das nicht so bekannt ist, aber doch nützlich sein kann: der Einsatz von set. Beim Einsatz von set geht es um die Vergabe von Berechtigungen in Relation zu anderen Objekten. Worum geht es genau?
1. Vergabe von Berechtigungen über ACLs an verschachtelte Gruppen: Wenn Sie die Gruppe (A) in der Gruppe (B) als Mitglied eintragen und anschließend der Gruppe (B) Rechte im LDAP-Baum geben, heißt das nicht, dass auch die Mitglieder der Gruppe (A) die Rechte erhalten, denn normalerweise verfolgt der OpenLDAP verschachtelte Gruppen nicht. Mit einer Regel über set ist das aber möglich.
2. Verfolgung von Referenzen: Stellen Sie sich vor, Sie haben bei allen Benutzern einer Abteilung im Attribut manager den Abteilungsleiter eingetragen, und der soll über eine ACL Rechte an bestimmten Attributen seiner Mitarbeiter erhalten. Dann können Sie diese Aufgabe sehr einfach über set realisieren.
Hier soll als Beispiel bei allen Benutzerobjekten der ou=Verwaltung der Abteilungsleiter cn=Verw-al,ou=users,ou=Verwaltung,ou=firma,dc=example,dc=net im Attribut manager eingetragen werden.
3. Stellvertreterregeln: Nehmen wir den Fall aus Punkt 2 und gehen mal davon aus, dass der Abteilungsleiter einen Stellvertreter hat, der ebenfalls diese Rechte erhalten soll. Der Stellvertreter ist über das Attribut secretary beim Abteilungsleiter eingetragen und erhält die benötigten Rechte über eine entsprechend angepasste ACL. Gehen wir auch hier noch mal einen Schritt weiter. Der Abteilungsleiter hat mehrere Stellvertreter, von denen aber nur eine bestimmte Gruppe die Rechte erhalten soll. Dann können Sie hier auch noch über eine Gruppenmitgliedschaft die Rechte nur diesen Stellvertretern geben.
Wir haben ja bereits eine Gruppe, die als Mitglieder die Abteilungsleiter beinhaltet und die in der Lage ist, Passwörter zu ändern.
Jeder Abteilungsleiter soll jetzt noch einen Stellvertreter bekommen, der dann auch die Passwörter ändern können soll. Dafür legen wir die neue Gruppe aus Listing 9.35 an:
dn: cn=stellvertreter,ou=groups,dc=example,dc=net objectClass: groupOfNames objectClass: top cn: stellvertreter Member: cn=u1-prod,ou=users,ou=produktion,ou=firma,dc=example,dc=net Member: cn=u1-verw,ou=users,ou=verwaltung,ou=firma,dc=example,dc=net
Zur Gruppe pw-set wird jetzt die neue Gruppe stellvertreter als Mitglied hinzugefügt. Das können Sie entweder über eines der grafischen Werkzeuge oder über die LDIF-Datei aus Listing 9.36 erledigen:
dn: cn=pw-set,ou=groups,dc=example,dc=net changetype: modify add: Member Member: cn=stellvertreter,ou=groups,dc=example,dc=net
Jetzt fehlt nur noch die ACL, die die Berechtigungen an die Benutzer der verschachtelten Gruppe weiterleitet. Die ACL sehen Sie in Listing 9.37:
dn: olcDatabase={2}mdb,cn=config changetype: modify delete: olcAccess olcAccess: {2} - delete: olcAccess olcAccess: {1} - add: olcAccess olcAccess: {1} to attrs=userPassword by anonymous auth by self write by dn.exact=uid=ldap-admin,ou=users,dc=example,dc=net write by dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth manage by set="[cn=pw-set,ou=groups,dc=example,dc=net]/Member* & user" write by * none - add: olcAccess olcAccess: {2} to attrs=shadowLastChange by anonymous auth by self write by dn.exact=uid=ldap-admin,ou=users,dc=example,dc=net write by dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth manage by set="[cn=pw-set,ou=groups,dc=example,dc=net]/Member* & user" write by * none
Als neuen Eintrag für die Attribute userPassword und shadowLastChange sehen Sie das set. Die einzelnen Elemente der Regel haben die folgende Bedeutung:
cn=pw-set,ou=groups,dc=example,dc=net]
Das ist die Gruppe, aus der die Mitglieder ausgewertet werden sollen.
Member*
In der angegebenen Gruppe werden die Mitglieder über das Attribut Member verwaltet. Der * dahinter bedeutet, dass die Gruppe rekursiv durchsucht wird. Wenn also in der Gruppe weitere Gruppen eingetragen sind, werden diese auch noch durchsucht. Sollte es dort auch noch Mitglieder geben, die über das Attribut Member verwaltet werden, erhalten diese auch die Rechte der gerade geänderten ACL. Wollen Sie nur den Mitgliedern dieser einen Gruppe Rechte geben, dann können Sie den * auch weglassen.
& user
Vergleicht den Benutzer, der die Anfrage gestellt hat, mit den Einträgen in den Gruppen.
Bei dem Beispiel gehen wir davon aus, dass im Attribut Member immer der vollständige DN eingetragen ist. Was, wenn in der Gruppe aber nur der Name (uid) eingetragen ist? Ja, auch dann kann die Gruppe verwendet werden. Dann können Sie die ACLs wie in Listing 9.38 verwenden:
changetype: modify delete: olcAccess olcAccess: {1} - add: olcAccess olcAccess: {1}to attrs=userPassword by self write by set="[cn=posix,ou=groups,dc=example,dc=net]/memberUID & user/uid" write by anonymous auth by * none
Bei der Verfolgung von Referenzen wird der Inhalt eines Attributs ausgewertet. Bei dem entsprechenden Attribut handelt es sich um ein Attribut, in dem der vollständige DN eines Benutzers eingetragen ist. Im Beispiel verwenden wir das Attribut Manager. Alle Benutzerobjekte in den Abteilungen ou=verwaltung und ou=produktion, an denen der Abteilungsleiter das Recht zu ändern haben soll, erhalten den Abteilungsleiter als manager eingetragen.
Damit Sie das Beispiel nachvollziehen können, setzen Sie das Attribut bei den gewünschten Benutzern. Das können Sie entweder über eines der grafischen Werkzeuge oder über LDIF-Dateien realisieren. In Listing 9.39 sehen Sie die LDIF-Datei für die Änderung eines Benutzers:
dn: cn=u1 verw,ou=users,ou=verwaltung,ou=firma,dc=example,dc=net changetype: modify add: Manager Manager: cn=verw al,ou=users,ou=verwaltung,ou=firma,dc=example,dc=net
Bei der Vergabe der Berechtigungen wird später geschaut, ob bei einem Benutzer der Manager eingetragen ist. Ist das der Fall, kann der Abteilungsleiter die Attribute dieses Mitarbeiters ändern.
Jetzt fehlt nur noch die Anpassung der ACLs für die Abteilungen. Dazu sehen Sie in Listing 9.40 die benötigten Änderungen der ACLs für den Zugriff auf die Abteilungen:
dn: olcDatabase={2}mdb,cn=config changetype: modify delete: olcAccess olcAccess: {8} - delete: olcAccess olcAccess: {7} - add: olcAccess olcAccess: {7}to dn.sub=ou=verwaltung,ou=firma,dc=example,dc=net by set="this/manager & user" write by "dn.children=ou=users,ou=verwaltung,ou=firma,dc=example,dc=net" read by * none - add: olcAccess olcAccess: {8}to dn.sub=ou=produktion,ou=firma,dc=example,dc=net by set="this/manager & user" write by "dn.children=ou=users,ou=produktion,ou=firma,dc=example,dc=net" read by * none
Hier verweist das set jetzt auf das Attribut manager. Nur der Benutzer (user), der dort eingetragen ist, bekommt das Schreibrecht. Das Attribut manager ist damit die Referenz für die Berechtigung.
Bei der Stellvertreterregelung gibt es zwei Möglichkeiten. Beginnen möchten wir mit der einfacheren:
Bei den Abteilungsleitern werden bestimmte Benutzerobjekte als secretary eingetragen. Diese sollen dann später der Stellvertreter werden. In Listing 9.41 sehen Sie die LDIF-Datei für die Anpassung eines Abteilungsleiters:
dn: cn=verw-al,ou=users,ou=verwaltung,ou=firma,dc=example,dc=net changetype: modify add: secretary secretary: cn=u1-verw,ou=users,ou=verwaltung,ou=firma,dc=example,dc=net
Diese hier eingetragenen Benutzerobjekte sollen jetzt in der Lage sein, die Eigenschaften der Benutzer in der eigenen Abteilung zu ändern. Sie sollen aber nicht in der Lage sein, die Passwörter der Benutzer anzupassen. Deshalb wird nur die ACL für den Zugriff auf die eigene OU geändert. Da die ACL für die Passwörter vor der ACL für die Abteilungen steht, betrifft die Änderung nicht die Passwörter. Achten Sie daher stets auf die richtige Reihenfolge der ACLs. In Listing 9.42 sehen Sie die geänderten ACLs für den Zugriff auf die Objekte in den jeweiligen Abteilungen:
dn: olcDatabase={2}mdb,cn=config changetype: modify delete: olcAccess olcAccess: {8} - delete: olcAccess olcAccess: {7} - add: olcAccess olcAccess: {7}to dn.sub=ou=verwaltung,ou=firma,dc=example,dc=net by set="this/manager & user" write by set="this/manager/secretary & user" write by "dn.children=ou=users,ou=verwaltung,ou=firma,dc=example,dc=net" read by * none - add: olcAccess olcAccess: {8}to dn.sub=ou=produktion,ou=firma,dc=example,dc=net by set="this/manager & user" write by set="this/manager/secretary & user" write by "dn.children=ou=users,ou=produktion,ou=firma,dc=example,dc=net" read by * none
Jetzt hat der Abteilungsleiter genau einen Stellvertreter. Da es sich bei dem Attribut secretary um ein multi-value-Attribut handelt, können Sie hier auch mehrere Benutzer eintragen.
Sie können hier die Abhängigkeit der Attribute voneinander sehen. Ein Benutzer, der bei Objekten als manager eingetragen wurde und selbst über secretary-Einträge verfügt, gewährt diesen den Zugriff.
Jetzt soll in der zweiten Möglichkeit der Stellvertreterregelung eine Gruppe, in der bestimmte Stellvertreter eingetragen sind, das Recht erhalten, die Passwörter der Abteilung zu ändern. Dazu benötigen Sie in jeder Abteilung eine Gruppe, in der alle Stellvertreter eingetragen sind. In Listing 9.43 sehen Sie die dazu benötigte LDIF-Datei, die die Gruppe anlegt und jeweils einen Benutzer der Abteilung in die entsprechende Gruppe einträgt:
dn: cn=secretary,ou=groups,ou=verwaltung,ou=firma,dc=example,dc=net objectClass: groupOfNames cn: secretary Member: cn=u1-verw,ou=users,ou=verwaltung,ou=firma,dc=example,dc=net dn: cn=secretary,ou=groups,ou=produktion,ou=firma,dc=example,dc=net objectClass: groupOfNames cn: secretary Member: cn=u1-prod,ou=users,ou=produktion,ou=firma,dc=example,dc=net
Jetzt kommt dann noch die Anpassung der beiden ACLs für Passwortverwaltung, denn dort wird jetzt die Gruppe hinzugefügt. In Listing 9.44 finden Sie die geänderte ACL:
dn: olcDatabase={2}mdb,cn=config changetype: modify delete: olcAccess olcAccess: {2} - delete: olcAccess olcAccess: {1} - add: olcAccess olcAccess: {1} to attrs=userPassword by anonymous auth by self write by dn.exact=uid=ldap-admin,ou=users,dc=example,dc=net write by dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth manage by set="[cn=pw-set,ou=groups,dc=example,dc=net]/Member* & user" write by set="this/manager/secretary & [cn=secretary,ou=groups,ou=verwaltung,\ ou=firma,dc=example,dc=net]/member* & user" write by set="this/manager/secretary & [cn=secretary,ou=groups,ou=produktion, ou=firma,dc=example,dc=net]/member* & user" write by * none - add: olcAccess olcAccess: {2} to attrs=shadowLastChange by anonymous auth by self write by dn.exact=uid=ldap-admin,ou=users,dc=example,dc=net write by dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth manage by set="[cn=pw-set,ou=groups,dc=example,dc=net]/Member* & user" write by set="this/manager/secretary & [cn=secretary,ou=groups,ou=verwaltung,\ ou=firma,dc=example,dc=net]/member* & user" write by set="this/manager/secretary & [cn=secretary,ou=groups,ou=produktion,\ ou=firma,dc=example,dc=net]/member* & user" write by * none
Warum haben wir zwei Gruppen für die Änderung der Passwörter angelegt und eingetragen? Da das Gruppenobjekt sich in der Abteilung befindet und der Abteilungsleiter das Recht hat, alle Objekte seiner Abteilung zu verwalten, kann jeder Abteilungsleiter seine Stellvertreter selbst verwalten. Sie können aber auch eine zentrale Gruppe mit allen Stellvertretern erstellen und dann an diese Gruppe die Rechte vergeben. Wenn Sie die ACL jetzt alleine betrachten, sieht es so aus, als könnten alle Stellvertreter aller Abteilungen alle Passwörter in allen Abteilungen verwalten. Sie dürfen aber die ACLs nicht alleine betrachten, sondern immer im Kontext zu den anderen ACLs. Da die Benutzer immer nur Zugriff auf die Objekte der eigenen Abteilung haben, können Sie die Passwörter der anderen Abteilung nicht verwalten.
Die Vergabe von Berechtigungen über set ist sehr umfangreich und kann sehr komplex werden. Hier ist es besonders wichtig, dass Sie diese ACLs genau dokumentieren.
9.2.8.1 | Alle ACLs |
In diesem Kapitel haben wir bis zu diesem Punkt einige ACLs erstellt, geändert und die Position verschoben. Alle Listings finden Sie auch in den Downloads zum Buch. Bevor wir uns im letzten Abschnitt des Kapitels mit der Prüfung von ACLs beschäftigen, sehen Sie in Listing 9.45 alle ACLs, wie sie zu diesem Zeitpunkt gesetzt sind.
olcAccess: {0}to dn.exact=dc=example,dc=net by users read olcAccess: {1} to attrs=userPassword by anonymous auth by self write by dn.exact=uid=ldap-admin,ou=users,dc=example,dc=net write by dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth manage by set="[cn=pw-set,ou=groups,dc=example,dc=net]/Member* & user" write by * none olcAccess: {2} to attrs=shadowLastChange by anonymous auth by self write by dn.exact=uid=ldap-admin,ou=users,dc=example,dc=net write by dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth manage by set="[cn=pw-set,ou=groups,dc=example,dc=net]/Member* & user" write by * none olcAccess: {3}to * by dn.exact=gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth manage by dn.exact=uid=sssd-user,ou=users,dc=example,dc=net read by dn.exact=uid=ldap-admin,ou=users,dc=example,dc=net write by dn.exact=uid=lam-user,ou=users,dc=example,dc=net read by * break olcAccess: {4} to dn.exact="cn=u1 verw,ou=users,ou=verwaltung,ou=firma,\ dc=example,dc=net" by peername.regex=192\.168\.56\.201 read by peername.regex=192\.168\.56\.12 read by * none olcAccess: {5} to dn.regex=ou=adressen,cn=(.+),ou=users,ou=(.+),\ ou=firma,dc=example,dc=net filter=(objectClass=posixAccount) by dn.regex=cn=(.+),ou=users,ou=(.+),ou=firma,dc=example,dc=net read by * break olcAccess: {6}to dn.regex="ou=adressen,cn=(.+),ou=users,ou=(.+),\ ou=firma,dc=example,dc=net$" by dn.regex="$1,ou=users,ou=$2,ou=firma,dc=example,dc=net$" write by * none olcAccess: {7}to dn.sub=ou=verwaltung,ou=firma,dc=example,dc=net by set="this/manager & user" write by set="this/manager/secretary & user" write by "dn.children=ou=users,ou=verwaltung,ou=firma,dc=example,dc=net" read by * none olcAccess: {8}to dn.sub=ou=produktion,ou=firma,dc=example,dc=net by set="this/manager & user" write by set="this/manager/secretary & user" write by "dn.children=ou=users,ou=produktion,ou=firma,dc=example,dc=net" read by * none olcAccess: {9}to "dn.exact=ou=firma,dc=example,dc=net" by users read by * none
Tipp: Wenn Sie sich die Konfiguration anzeigen lassen, um die ACLs oder andere Teile der Konfiguration zu sehen, werden alle Zeilen nach 76 Zeichen umbrochen.
Um die Zeilen in vollständiger Länge sehen zu können, verwenden Sie das Kommando slapcat -n 0 | perl -p0e "s/\n //g". Damit werden alle Zeilen in der vollen Länge angezeigt.
Jetzt haben Sie sehr viele Beispiele zum Erstellen von ACLs erhalten. Zum Abschluss folgen hier noch ein paar Tipps:
Erstellen Sie nie alle ACLs auf einmal, das wird nicht klappen. Testen Sie jede neue ACL einzeln.
Dokumentieren Sie jede einzelne ACL mit Funktion und möglichst dem Grund, warum Sie diese ACL eingetragen haben.
Sichern Sie Ihre Liste der ACLs, bevor Sie Änderungen vornehmen, so können Sie schnell wieder auf den alten Stand zurückwechseln.
Verwenden Sie so wenig ACLs mit regex wie möglich. ACLs müssen noch genauer dokumentiert werden als ACLs ohne regex.
Wenn Sie eine ACL einspielen, ist diese sofort wirksam. Aus diesem Grund kann es sinnvoll sein, eine Testumgebung zu nutzen, um neue ACLs zu testen.
9.2.9 | Prüfen von ACLs |
Ein grafisches Werkzeug zum Prüfen, ob alle Berechtigungen richtig gesetzt sind, gibt es nicht. Sie haben entweder die Möglichkeit, sich als ein bestimmter Benutzer anzumelden und dann die Zugriffe zu testen, oder Sie können einzelne ACLs mit dem Kommando slapacl testen. Der Test mit einem der grafischen Werkzeuge kann Ihnen einen sehr guten Überblick über die Berechtigungen eines einzelnen Benutzers geben. Wenn Sie die ACLs, die wir in diesem Kapitel vorstellen, grafisch prüfen wollen, melden Sie sich zum Beispiel am LAM als ein Benutzer der Abteilung an und prüfen Sie, ob Sie dann die Objekte sehen, die Sie erwartet haben. Auch die Zugriffsrechte können Sie so testen: Kann ein Benutzer, mit dem Sie sich am LAM angemeldet haben, die Passwörter ändern? Wenn ja, war das so geplant oder ist das ein Fehler? Bei dem Test mit slapacl können Sie den Zugriff auf Objekte und einzelne Attribute testen.
Die Prüfung, welche Rechte der Benutzer cn=u1 prod,ou=users,ou=produktion,ou=firma, dc=example,dc=net an seinem Adressbuch hat, sehen Sie in Listing 9.46:
root@provider01:~# slapacl -D "cn=u1-prod,ou=users,ou=produktion, \ou=firma,dc=example,dc=net" -b "ou=Adressen,cn=u1-Prod,\ ou=users,ou=Produktion,ou=firma,dc=example,dc=net" authcDN: "cn=u1-prod,ou=users,ou=produktion,ou=firma,dc=example,dc=net" entry: write(=wrscxd) children: write(=wrscxd) ou=Adressen: write(=wrscxd) objectClass=organizationalUnit: write(=wrscxd) objectClass=top: write(=wrscxd) structuralObjectClass=organizationalUnit: write(=wrscxd) entryUUID=306bfac2-3b4c-103d-8369-df671eee03ce: write(=wrscxd) creatorsName=cn=admin,dc=example,dc=net: write(=wrscxd) createTimestamp=20230207155947Z: write(=wrscxd) entryCSN=20230207155947.028597Z#000000#000#000000: write(=wrscxd) modifiersName=cn=admin,dc=example,dc=net: write(=wrscxd) modifyTimestamp=20230207155947Z: write(=wrscxd)
Mit der Option -D geben Sie das Objekt an, dessen Berechtigung Sie prüfen wollen. Mit der zweiten Option -b legen Sie das Objekt fest, auf das der Zugriff geprüft werden soll. Im Ergebnis sehen Sie, dass der Benutzer an allen Attributen des Objekts das Schreibrecht hat.
Sie können auch die Rechte an einem einzelnen Attribut testen, ein Beispiel dafür sehen Sie in Listing 9.47:
root@provider01:~# slapacl -D "cn=u1-prod,ou=users,ou=produktion,\ ou=firma,dc=example,dc=net" -b "ou=Adressen,cn=u1-Prod,\ ou=users,ou=Produktion,ou=firma,dc=example,\ dc=net" userPassword authcDN: "cn=u1-prod,ou=users,ou=produktion,ou=firma,dc=example,dc=net" userPassword: write(=wrscxd)
Hier wird lediglich geprüft, welche Rechte der Benutzer an dem Attribut userPassword seines Objekts besitzt.
Oft wollen Sie nur prüfen, ob ein Benutzer ein ganz bestimmtes Recht an einem Attribut besitzt, und auch das können Sie mit dem Kommando slapacl realisieren. In Listing 9.48 wird geprüft, ob der Benutzer das Schreibrecht an den children besitzt:
root@provider01:~# slapacl -D "cn=u1-prod,ou=users,ou=produktion,\ ou=firma,dc=example,dc=net" -b "ou=Adressen,cn=u1-Prod,\ ou=users,ou=Produktion,ou=firma,dc=example,dc=net" \ userPassword/write authcDN: "cn=u1-prod,ou=users,ou=produktion,ou=firma,dc=example,dc=net" write access to userPassword: ALLOWED
Als Ergebnis erhalten Sie entweder ein ALLOWED, wenn der Benutzer das Schreibrecht am Attribut besitzt, oder ein DENIED, wenn das Recht für das Attribut nicht vorhanden ist.
Verwenden Sie das Kommando slapacl immer dann, wenn Sie komplexe ACLs erstellt oder größere Änderungen an den ACLs vorgenommen haben. Im Gegensatz zur Prüfung mit einem grafischen Werkzeug benötigen Sie bei der Überprüfung mit slapacl kein Passwort für den Benutzer, dessen Zugriffsrechte Sie prüfen wollen. Das Kommando slapacl kann nur vom root verwendet werden.
Selbstverständlich gibt es noch sehr viel mehr Möglichkeiten, wie Sie mithilfe von ACLs die Berechtigungen anpassen können. Im Verlauf des Buches werden wir immer wieder ACLs erstellen, um bestimmten Objekten Rechte im LDAP-Baum zu geben. Hier auch noch einmal der Hinweis: Viel mehr und auch viele Beispiele finden Sie in der Manpage slapd.access.