- Abraxas - https://www.effinger.org -

Dovecot, Exim, OpenLDAP und getmail unter Ubuntu – (1) OpenLDAP

In diesem Blog-Eintrag geht es um die Einrichtung eines Mailservers, der dovecot [1], Exim [2], OpenLDAP [3] und getmail [4] verwendet. Folgendes wird mit der Konfiguration erreicht:

Am Beispiel sollte es klarer werden. Paul ist mit der Userid paul in OpenLDAP eingetragen und hat auf dem lokalen Rechner mit der Domain myserver die Mailaddresse paul@myserver. Paul kann per IMAP Mails abrufen (Login: paul@paul Server: myserver) und ebenfalls per SMTP Mails verschicken. Außerdem hat Paul eine weitere E-Mailadresse aus der Zeit als er sich noch keine eigene Domain leisten konnte. Diese lautet paulpanzer@gmx.de. Paul möchte seine E-Mails zukünftig auf seinem lokalen Server speichern und nicht mehr bei GMX. Er bzw. der Administrator trägt dazu die POP3-Zugangsdaten von GMX in OpenLDAP ein. Danach werden alle E-Mails automatisch heruntergeladen und Paul kann über den Login paul@paulpanzer@gmx.de auf dem Server myserver seine GMX-Mails per IMAP abrufen. Trägt Paul bzw. der Administrator auch die SMTP-Daten für die E-Mailaddresse paulpanzer@gmx.de in OpenLDAP ein, so kann Paul außerdem Mails indirekt via GMX- Mailserver verschicken, indem er als SMTP-Login paul@paulpanzer@gmx.de auf seinem lokalen Server wählt. So sieht das fertige Schema mit der verwendeten Software aus:

General Software Setup - Dovecot, Exim & Co. [5]Der Zugriff per Webinterface auf Mails und der Abruf der Mails vom externen Server bzw. das Empfangen von Mails ist hier dargestellt:

General software setup - Getmail, Roundcube & Co. [6]

Einrichtung

Bevor man überhaupt daran denkt, ein E-Mail-Serversystem aufzusetzen, müssen zwei Bedingungen erfüllt sein. Die erste betrifft eine genaue Systemzeit, der zweite den Hostnamen. Obwohl fast alles im Blog detailliert beschrieben ist, empfehle ich, die Konfigurationsdateien für das Mailsystem herunterzuladen [7].

Genaue Systemzeit mit Ubuntu

Leider installiert Ubuntu standardmäßig das Paket ntpdate. Durch dieses Paket wird jedes Mal, wenn man mit dem Internet verbunden ist (genauer gesagt beim ifup), die Zeit mit einem NTP-Server im Internet abgeglichen und ggfs. die Systemzeit entsprechend geändert. An sich ja keine schlechte Sache, aber dovecot reagiert auf Zeitänderungen sehr verstimmt [8] und quittiert den Dienst. Deshalb setze ich stets ntpd ein. Dies ist ein NTP-Server, der systematisch die Differenz zwischen Systemzeitgeber und dem Internetserver beobachtet und so systematisches zu schell bzw. zu langsam gehen des Systemzeitgebers korrigieren kann. Man erhält damit eine genauere Systemzeit und damit auch ein stabiles dovecot. Deshalb installiere ich ntpd und entferne ntpdate:

sudo apt-get autoremove --purge ntpdate && sudo apt-get install ntp

Um für den Ausfall des Ubuntu NTP-Servers gewappnet zu sein, habe ich die Server-Einträge in /etc/ntp.conf noch um Server aus dem öffentlichen NTP-Pool [9] ergänzt:

# You do need to talk to an NTP server or two (or three).
server 0.pool.ntp.org
server 1.pool.ntp.org
server 2.pool.ntp.org
server ntp.ubuntu.com

Nach einem Speichern der Konfigurationsdatei und einem Neustart des NTP-Servers mit

sudo /etc/init.d/ntp restart

haben wir den ersten Punkt erledigt. Christoph Langner beschreibt die Installation von ntpd [10] noch etwas ausführlicher.

Hostname für den Mailserver

Der zweite wichtige Punkt ist der Hostname. Der fully qualified domain name (FQDN) des Rechners muss bekannt sein. Nachprüfen lässt sich das über

hostname --fqdn

Bekommt man keine Ausgabe wie host.domain.de, dann muss man die /etc/hosts anpassen, so dass die Reihenfolge der Servernamen stimmt:

127.0.0.1	myserver	localhost

Bitte unbedingt beachten, dass das beispielhaft aufgeführte myserver kein FQDN ist. Ein FQDN besteht immer auch aus einer Top-Level-Domain.

Erzeugen von SSL-Zertifikaten

Damit wir mit imaps, dem durch Verschlüsselung abgesicherten IMAP, auf unseren Server zugreifen können, und auch den SMTP- und LDAP-Zugriff verschlüsseln können, benötigen wir entsprechende SSL-Zertifikate. Am einfachsten lassen sich solche Zertifikate mit easy-rsa aus dem Paktet openvpn erzeugen. Übrigens gibt es gibt es dazu gute Anleitungen von linux.neoberserker.de [11] oder im OpenVPN-Wiki [12]. Das Paket openvpn müssen wir jedoch nicht installieren. Es genügt, wenn wir mit

mkdir openvpn && cd openvpn && apt-get source openvpn

den Quelltext herunterladen. Wir führen alle folgenden Befehle zur Zertifikatserstellung als root aus (wichtig, damit später nicht jeder Benutzer die geheimen Dateien lesen kann). Zunächst kopieren wir das entsprechende Unterverzeichnis in /etc/ssl und löschen anschließend die nicht mehr benötigten Verzeichnisse mit

sudo -s
cp -R openvpn-2.1~rc11/easy-rsa/2.0 /etc/ssl/easy-rsa
cd .. && rm -rf openvpn
cd /etc/ssl/easy-rsa

Nun editieren wir die Datei vars und passen Sie entsprechend an. Bei mir habe ich folgende Zeilen am Ende angepasst:

export KEY_COUNTRY="DE"
export KEY_PROVINCE="RP"
export KEY_CITY="Ludwigshafen"
export KEY_ORG="Effinger"
export KEY_EMAIL="nospam@effinger.org"

Anschließend führen wir folgende Schritte durch, um die CA-Dateien zur Ausstellung eigener Zertifikate zu erzeugen. Beim Schritt ./build-ca kann man alle Punkte mit Enter bestätigen bis auf common name. Hier sollte man einen einfachen Namen, z.B. MyCA, angeben (am Besten nur Buchstaben und Zahlen verwenden – keine Leerzeichen oder sonstige Sonderzeichen).

. ./vars
./clean-all
./build-ca
./build-dh

Im nächsten Schritt erstellen wir für unseren Mailserver ein Server-Schlüsselpaar mit dem Befehl

./build-key-server myserver

myserver sollte dabei durch den DNS-Eintrag des Servers ersetzt werden (Thunderbird fragt ansonsten jedes Mal sicherheitshalber nach, ob das Zertifikat akzeptiert werden soll, weil der Zertifikatsname nicht mit dem Hostnamen übereinstimmt). Auch hier bestätigen wir wieder alles mit Enter bis auf den Punkt Sign the certificate? Hier antworten wir mit ja (y) und bestätigen die darauf erfolgende Rückfrage ebenfalls mit y. Ein Client-Zertifikat kann man mit

./build-key myclient

analog erzeugen. Dieses benötigt man jedoch nur, wenn man ausschließlich Clients mit gültigem Zertifikat auf den Mailserver zugreifen lassen möchte. Bei dovecot ist das der Fall, wenn der Parameter ssl_require_client_cert=yes gesetzt ist. Da dovecot sich mit dem LDAP-Server verbindet, können wir dazu ebenfalls ein Client-Zertifikat verwenden. Wir erzeugen es mit

./build-key dovecot-client

Im Unterverzeichnis keys sind nun einige Dateien, die wir noch in das richtige Verzeichnis kopieren müssen. Wichtig ist, dass niemand unbefugt auf die geheimzuhaltenden Dateien [13] zugreifen kann, die allesamt mit der Erweiterung .key enden. Am Besten ist es, vor dem Kopieren die Berechtigungen zu prüfen. Die öffentlichen Zertifikate werden in das Verzeichnis /etc/ssl/certs und die geheimen Zertifikate in /etc/ssl/private kopiert mit

cp /etc/ssl/easy-rsa/keys/myserver.crt /etc/ssl/easy-rsa/keys/ca.crt /etc/ssl/certs/
cp /etc/ssl/easy-rsa/keys/myserver.key /etc/ssl/private/
chown root.ssl-cert /etc/ssl/private/myserver.key
chmod 640 /etc/ssl/private/myserver.key

Die letzten beiden Zeilen dienen dazu, Exim, Dovecot und LDAP den Zugriff auf die geheime Key-Datei zu gewähren. Die entsprechendenBenutzer müssen dazu in der Gruppe ssl-cert sein (Anleitung erfolgt bei der Installation des jeweiligen Programms). Falls man später doch noch OpenVPN nutzen möchte, kann man den Diffie-Hellman Parameter ebenfalls kopieren

cp /etc/ssl/easy-rsa/keys/dh1024.pem /etc/ssl/

Die Client-Zertifikate muss man nun natürlich dem E-Mail-Programm bekannt machen. Bei Thunderbird fügt man über Extras>Einstellungen>Erweitert>Zertifikate>Zertifikate>Zertifizierungsstellen>Importieren die Datei ca.crt hinzu, wechselt anschließend auf den Reiter Ihre Zertifikate, um dort dann die Dateien myclient.p12 importieren. Letzere Datei erzeugt man durch ein

openssl pkcs12 -export -name "myclient" -in myclient.crt -inkey myclient.key -out myclient.p12

OpenLDAP installieren

Als nächstes installieren wir OpenLDAP und die ldap-utils nach meinem OpenLDAP 1×1 [14]. Hier die Schritte in Kurzform (falls Schritte unklar sind – im OpenLDAP 1×1 [14] ist alles sehr ausführlich erklärt):

  1. Alle folgenden Befehle als root ausführen mit einem
    sudo bash

    oder

    sudo -s
  2. apt-get install slapd ldap-utils

    Unbedingt das Administrator-Passwort bei der Einrichtung merken.

  3. /etc/init.d/slapd stop
  4. Die automatisch bei der Einrichtung erzeugte Datenbank löschen (Vorsicht, wenn OpenLDAP bereits im Einsatz ist! Dieser Befehl kann die aktive Datenbank löschen)
  5. rm /etc/ldap/slapd.d/cn\=config/olcDatabase\=\{1\}hdb.ldif && rm /var/lib/ldap/*
  6. Die folgende Datei database.ldif passen:
    # Database settings
    dn: olcDatabase=hdb,cn=config
    objectClass: olcDatabaseConfig
    objectClass: olcHdbConfig
    olcDatabase: hdb
    # The base of your directory
    olcSuffix: o=effinger
    # rootdn directive for specifying a superuser on the database. This is needed
    # for syncrepl.
    olcRootDN: cn=admin,o=effinger
    # Superuser Password for the database
    # {SSHA}pEvotN6PmSjx0JV/mZl5BWeSVOKR1Ejt equals "test"
    # CHANGE this for your installation!!!
    olcRootPW: {SSHA}pEvotN6PmSjx0JV/mZl5BWeSVOKR1Ejt
    # Where the database file are physically stored
    olcDbDirectory: /var/lib/ldap
    # The dbconfig settings are used to generate a DB_CONFIG file the first
    # time slapd starts.  They do NOT override existing an existing DB_CONFIG
    # file.  You should therefore change these settings in DB_CONFIG directly
    # or remove DB_CONFIG and restart slapd for changes to take effect.
    # For the Debian package we use 2MB as default but be sure to update this
    # value if you have plenty of RAM
    
    olcDbConfig: set_cachesize 0 2097152 0
    # Sven Hartge reported that he had to set this value incredibly high
    # to get slapd running at all. See http://bugs.debian.org/303057 for more
    # information.
    # Number of objects that can be locked at the same time.
    olcDbConfig: set_lk_max_objects 1500
    # Number of locks (both requested and granted)
    olcDbConfig: set_lk_max_locks 1500
    # Number of lockers
    olcDbConfig: set_lk_max_lockers 1500
    # Indexing options
    olcDbIndex: objectClass eq
    # Save the time that the entry gets modified
    olcLastMod: TRUE
    # Checkpoint the BerkeleyDB database periodically in case of system
    # failure and to speed slapd shutdown.
    olcDbCheckpoint: 512 30
    # The userPassword by default can be changed
    # by the entry owning it if they are authenticated.
    # Others should not be able to see it, except the
    # admin entry below
    # These access lines apply to database #1 only
    olcAccess: to attrs=userPassword,shadowLastChange by dn="cn=admin,o=effinger" write by anonymous auth by self write by * none
    # Ensure read access to the base for things like
    # supportedSASLMechanisms.  Without this you may
    # have problems with SASL not knowing what
    # mechanisms are available and the like.
    # Note that this is covered by the 'access to *'
    # ACL below too but if you change that as people
    # are wont to do you'll still need this if you
    # want SASL (and possible other things) to work
    # happily.
    olcAccess: to dn.base="" by * read
    # The admin dn has full write access, everyone else
    # can read everything.
    olcAccess: to * by dn="cn=admin,o=effinger" write by * read
    # For Netscape Roaming support, each user gets a roaming
    # profile for which they have write access to
    #olcAccess: to dn=".*,ou=Roaming,o=morsnet" by dn="cn=admin,o=effinger" write by dnattr=owner write

    Bitte unbedingt das Passwort (olcRootPW) ändern.
    Falls gewünscht auch die RootDN (hier „o=effinger“) anpassen. Dadurch müssen einige der folgenden Befehle/Dateien angepasst werden.

  7. Eine neue Datenbank mit Hilfe der database.ldif erzeugen (Administratorpasswort aus Schritt 2 wird benötigt)
    /etc/init.d/slapd start
    ldapadd -f database.ldif -x -D "cn=admin,cn=config" -W

Jetzt haben wir eine saubere Datenbank mit der RootDN „o=effinger“ und dem entsprechenden Administrator „cn=admin,o=effinger“, die wir nun weiter konfigurieren können. Bevor wir damit loslegen, muss man sich jedoch Gedanken machen, wie die Daten in OpenLDAP strukturiert werden sollen. Ich habe mich für folgendes Setup entschieden:

Nach den konzeptionellen Überlegungen machen wir uns nun an die Arbeit. Wir konfigurieren das DynList-Overlay, wobei mir eine Anleitung für Gentoo [18] und die kurze Einführung für Ubuntu [19] sehr geholfen haben und ergänzen OpenLDAP mit einem von mir erstellten Schema (dovecot.schema.ldif) [20].

  1. Folgenden Inhalt unter dem Namen dyngroup.schema.ldif abspeichern:
    dn: cn=dyngroup,cn=schema,cn=config
    objectClass: olcSchemaConfig
    cn: dyngroup
    olcObjectIdentifier: NetscapeRoot 2.16.840.1.113730
    olcObjectIdentifier: NetscapeLDAP NetscapeRoot:3
    olcObjectIdentifier: NetscapeLDAPattributeType NetscapeLDAP:1
    olcObjectIdentifier: NetscapeLDAPobjectClass NetscapeLDAP:2
    olcObjectIdentifier: OpenLDAPExp11 1.3.6.1.4.1.4203.666.11
    olcObjectIdentifier: DynGroupBase OpenLDAPExp11:8
    olcObjectIdentifier: DynGroupAttr DynGroupBase:1
    olcObjectIdentifier: DynGroupOC DynGroupBase:2
    olcAttributeTypes: ( NetscapeLDAPattributeType:198 NAME 'memberURL' DESC 'Identifies an URL associated with each member of a group. Any type of labeled URL can be used.' SUP labeledURI )
    olcAttributeTypes: ( DynGroupAttr:1 NAME 'dgIdentity' DESC 'Identity to use when processing the memberURL' SUP distinguishedName SINGLE-VALUE )
    olcAttributeTypes: ( DynGroupAttr:2 NAME 'dgAuthz' DESC 'Optional authorization rules that determine who is allowed to assume the dgIdentity' EQUALITY authzMatch SYNTAX 1.3.6.1.4.1.4203.666.2.7 X-ORDERED 'VALUES' )
    olcObjectClasses: ( NetscapeLDAPobjectClass:33 NAME 'groupOfURLs' SUP top STRUCTURAL MUST cn MAY ( memberURL $ businessCategory $ description $ o $ ou $ owner $ seeAlso ) )
    olcObjectClasses: ( DynGroupOC:1 NAME 'dgIdentityAux' SUP top AUXILIARY MAY ( dgIdentity $ dgAuthz ) )

    und mit einem

    ldapadd -D "cn=admin,cn=config" -x -W -f dyngroup.schema.ldif

    hinzufügen (Administrator-Passwort wird benötigt).

  2. Als nächstes folgenden Inhalt unter dem Namen dovecot.schema.ldif abspeichern
    dn: cn=dovecot,cn=schema,cn=config
    objectClass: olcSchemaConfig
    cn: dovecot
    olcAttributeTypes: ( 1.3.6.1.4.1.32589.1.1.1.1 NAME 'dcMailMessageStore' DESC 'Path to the maildir/mbox on the mail system' EQUALITY caseExactIA5Match SUBSTR caseIgnoreIA5SubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{256} SINGLE-VALUE )
    olcAttributeTypes: ( 1.3.6.1.4.1.32589.1.1.1.2 NAME 'dcMailAlias' DESC 'Secondary (alias) mailaddresses for a user' EQUALITY caseIgnoreIA5Match SUBSTR caseIgnoreIA5SubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{256} )
    olcAttributeTypes: ( 1.3.6.1.4.1.32589.1.2.1.1 NAME 'dcSubMailAddress' DESC 'A users secondary e-mail address for which mail from on another Mailserver has to be fetched' EQUALITY caseIgnoreIA5Match SUBSTR caseIgnoreIA5SubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{256} )
    olcAttributeTypes: ( 1.3.6.1.4.1.32589.1.2.1.2 NAME 'dcAccountStatus' DESC 'The status of a user account: active, noaccess, disabled, deleted' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
    olcAttributeTypes: ( 1.3.6.1.4.1.32589.1.2.1.3 NAME 'dcSMTPServer' DESC 'Outgoing mails should be delivered to this Mailserver via SMTP.' EQUALITY caseIgnoreIA5Match SUBSTR caseIgnoreIA5SubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{256} SINGLE-VALUE )
    olcAttributeTypes: ( 1.3.6.1.4.1.32589.1.2.1.4 NAME 'dcSMTPLogin' DESC 'Login credential to send Mail with the SMTP server' EQUALITY caseExactIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
    olcAttributeTypes: ( 1.3.6.1.4.1.32589.1.2.1.5 NAME 'dcSMTPPassword' DESC 'A separate text that stores the SMTP account password in clear text' EQUALITY caseExactIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
    olcAttributeTypes: ( 1.3.6.1.4.1.32589.1.2.1.6 NAME 'dcRetrieveType' DESC 'Tells getmail what mail account to retrieve mail from, and how to access that account, e.g. SimplePOP3Retriever and BrokenUIDLPOP3SSLRetriever' EQUALITY caseIgnoreIA5Match SUBSTR caseIgnoreIA5SubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{256} SINGLE-VALUE )
    olcAttributeTypes: ( 1.3.6.1.4.1.32589.1.2.1.7 NAME 'dcRetrieveServer' DESC 'Incoming mails have to be downloaded from this server' EQUALITY caseIgnoreIA5Match SUBSTR caseIgnoreIA5SubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{256} SINGLE-VALUE )
    olcAttributeTypes: ( 1.3.6.1.4.1.32589.1.2.1.8 NAME 'dcRetrieveLogin' DESC 'Login credential to receive Mail from the server' EQUALITY caseExactIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
    olcAttributeTypes: ( 1.3.6.1.4.1.32589.1.2.1.9 NAME 'dcRetrievePassword' DESC 'Password for mail retrieval in clear text' EQUALITY caseExactIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )
    olcAttributeTypes: ( 1.3.6.1.4.1.32589.1.2.1.10 NAME 'dcMailQuota' DESC 'The size of space the user can have until further messages get bounced.' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
    olcAttributeTypes: ( 1.3.6.1.4.1.32589.1.2.1.11 NAME 'dcMailSizeMax' DESC 'The maximum size of a single messages the user accepts.' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )
    olcAttributeTypes: ( 1.3.6.1.4.1.32589.1.2.1.12 NAME 'dcMailAlternateAddress' DESC 'Secondary (alias) mailaddresses for an external Mail Account' EQUALITY caseIgnoreIA5Match SUBSTR caseIgnoreIA5SubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{256} )
    olcAttributeTypes: ( 1.3.6.1.4.1.32589.1.3.1.1 NAME 'dcPosixOwnerURL' DESC 'Identifies an URL associated with the posixOwner of the entry. Any type of labeled URL can be used.' SUP labeledURI )
    olcObjectClasses: ( 1.3.6.1.4.1.32589.1.1.2.1 NAME 'dcMailUser' DESC 'Dovecot-LDAP User' SUP top AUXILIARY MUST dcMailMessageStore MAY dcMailAlias )
    olcObjectClasses: ( 1.3.6.1.4.1.32589.1.2.2.1 NAME 'dcExternalMailAccount' DESC 'Dovecot-LDAP external mail account' SUP top STRUCTURAL MUST ( dcSubMailAddress $ dcAccountStatus ) MAY ( dcSMTPServer $ dcSMTPLogin $ dcSMTPPassword $ dcRetrieveType $ dcRetrieveServer $ dcRetrieveLogin $ dcRetrievePassword $ dcMailQuota $ dcMailSizeMax $ dcMailAlternateAddress ) )
    olcObjectClasses: ( 1.3.6.1.4.1.32589.1.3.2.1 NAME 'dcPosixSubAccount' DESC 'LDAP-URL for retrieving the respective posixAccount of an entry' SUP top AUXILIARY MAY dcPosixOwnerURL )

    und ebenfalls in die Konfiguration übernehmen mit

    ldapadd -D "cn=admin,cn=config" -x -W -f dovecot.schema.ldif
  3. Jetzt aktivieren wir das DynList-Modul und speichern dazu Folgendes als dynlist_moduleLoad.ldif
    dn: cn=module{0},cn=config
    changetype: modify
    add: olcModuleLoad
    olcModuleLoad: dynlist.so

    um es anschließend zur OpenLDAP-Konfiguration hinzuzufügen mit einem

    ldapmodify -D "cn=admin,cn=config" -x -W -f dynlist_moduleLoad.ldif
  4. Dem Dynlist-Overlay [21] müssen wir außerdem mitteilen, dass das Attribut dcPosixOwnerURL jeweils auf den zugehörigen Benutzer verweist. Das machen wir, indem wir den folgenden Inhalt als dynlist_activateOnSuffix.ldif abspeichern
    dn: olcOverlay=dynlist,olcDatabase={1}hdb,cn=config
    objectClass: olcOverlayConfig
    objectClass: olcDynamicList
    olcDLattrSet: dcPosixSubAccount dcPosixOwnerURL
    olcOverlay: dynlist

    und dann

    ldapadd -D "cn=admin,cn=config" -x -W -f dynlist_activateOnSuffix.ldif

    ausführen.

  5. Nun müssen wir einen Grundeintrag in LDAP vornehmen, bevor wir starten können. Außerdem benötigen wir einen Benutzer secmail, um Mails von externen Servern in festen Intervallen abzurufen und in das jeweilige lokale IMAP-Postfach abzulegen. Daher legen wir mit der folgenden LDIF-Datei database-content.ldif zunächst den Grundeintrag und den Benutzer secmail an (Passwort wieder unbedingt mit slappasswd anpassen) an
    # effinger
    dn: o=effinger
    objectclass: organization
    objectclass: top
    o: effinger
    
    # users, effinger
    dn: ou=users,o=effinger
    objectClass: organizationalUnit
    objectClass: top
    ou: users
    
    # secmail, users, effinger
    dn: uid=secmail,ou=users,o=effinger
    objectClass: posixAccount
    objectClass: account
    objectClass: top
    cn: secmail
    gidNumber: 134
    homeDirectory: /home/secmail
    uid: secmail
    uidNumber: 121
    # This password equals test
    userPassword: {SSHA}R+pQv9aIQINrPYdgljEJ0B7jzCp2cCzz

    und fügen sie mit einem

    ldapadd -D "cn=admin,o=effinger" -x -W -f database-content.ldif

    zum LDAP-Verzeichnis hinzu (Achtung Passwort verwenden, das für database.ldif erzeugt wurde)

  6. SSL/TLS aktivieren, dazu folgenden Inhalt als ssl-tls_init.ldif abspeichern
    dn: cn=config
    changetype: modify
    add: olcTLSCACertificateFile
    olcTLSCACertificateFile: /etc/ssl/certs/ca.crt
    
    dn: cn=config
    changetype: modify
    add: olcTLSCertificateFile
    olcTLSCertificateFile: /etc/ssl/certs/myserver.crt
    
    dn: cn=config
    changetype: modify
    add: olcTLSCertificateKeyFile
    olcTLSCertificateKeyFile: /etc/ssl/private/myserver.key

    und folgende Befehle ausführen:

    ldapmodify -D "cn=admin,cn=config" -x -W -f ssl-tls_init.ldif
    sudo adduser openldap ssl-cert

    Ein Neustart ist ebenfalls erforderlich, da ich die Erfahrung gemacht habe, dass ohne einen Neustart der Zugriff über TLS nicht funktioniert.

    /etc/init.d/slapd restart
  7. Im nächsten Schritt der LDAP-Konfiguration richten wir Zugriffsrechte ein. Generell soll auf die Login-Daten bei externen Mailservern nur der jeweilige Benutzer selbst und der LDAP-Administrator lesend und schreibend zugreifen können, während der secmail Benutzer nur Leserechte benötigt. Diese Zugriffsrechte werden in der Datei add_acl.ldif mit dem Inhalt
    dn: olcDatabase={1}hdb,cn=config
    changetype: modify
    add: olcAccess
    # The information to send and receive mails from remote servers
    # can only be modified by the user itself and the admin
    # the secmail user which will retrieve mails must have read access
    olcAccess: {1}to dn.regex=".*uid=([^,]+),ou=users,o=effinger" attrs=dcRetrieveType,dcRetrieveLogin,dcRetrievePassword,dcRetrieveServer,dcSMTPLogin,dcSMTPPassword,dcSMTPServer by dn="cn=admin,o=effinger" write by dn.exact,expand="uid=$1,ou=users,o=effinger" write by dn="uid=secmail,ou=users,o=effinger" read by * none
    # Users shall have write access to their attributes
    # admin shall have write access as well
    # all other users have only read access
    olcAccess: {2}to dn.regex=".*uid=([^,]+),ou=users,o=effinger" attrs=dcMailAlternateAddress by dn="cn=admin,o=effinger" write by dn.exact,expand="uid=$1,ou=users,o=effinger" write by * read

    durch den Befehl

    ldapmodify -D "cn=admin,cn=config" -x -W -f add_acl.ldif

    zum LDAP-Verzeichnis hinzugefügt.

  8. Im letzten Schritt indizieren wir wichtige Felder für die Suche im LDAP-Verzeichnis. Das schont Ressourcen und sorgt außerdem dafür,  dass die dezenten Hinweise von OpenLDAP in der Syslog, dass einzelne Attribute indiziert werden sollten, verschwinden. Was die einzelnen Indexoptionen bedeuten [22] ist auf zytrax.com erklärt. Wir legen die Datei add_attribute_indices.ldif mit dem Inhalt
    dn: olcDatabase={1}hdb,cn=config
    changetype: modify
    add: olcDbIndex
    olcDbIndex: cn pres,eq,sub
    olcDbIndex: sn pres,eq,sub
    olcDbIndex: uid pres,eq
    olcDbIndex: mail pres,eq,sub
    olcDbIndex: dcMailAlias pres,eq
    olcDbIndex: givenName pres,eq,sub
    olcDbIndex: dcSubMailAddress pres,eq
    olcDbIndex: dcMailAlternateAddress pres,eq
    olcDbIndex: dcAccountStatus pres,eq

    an und fügen sie mit dem Befehl

    ldapmodify -D "cn=admin,cn=config" -x -W -f add_attribute_indices.ldif

    dem Verzeichnis hinzu.

Zugriff auf OpenLDAP-Server absichern

Durch die Anpassung der Datei ldap.conf können wir festlegen, an welchen LDAP-Server Anfragen standardmäßig gerichtet werden sollen und unter welchen Bedingungen eine Verbindung mit diesem akzeptiert wird. Wir möchten die Konfiguration so anpassen, dass der OpenLDAP-Server ein gültiges Zertifikat vorweisen muss und standardmäßig der gerade eingerichtete LDAP-Server befragt wird. Standardmäßig werden die Anfragen zwar sowieso an den lokalen Rechner gerichtet, aber die Angabe des DNS-Namens ist notwendig, da sonst die Gültigkeitsprüfung für das Serverzertifikat fehlschlagt. In der Datei /etc/ldap/ldap.conf tragen wir folgende Zeilen ein (myserver durch den DNS-Namen ersetzen):

URI             ldap://myserver
TLS_CACERT      /etc/ssl/certs/ca.crt
TLS_REQCERT     demand

Ein anschließender Test mit dem Befehl

ldapsearch -x -b "o=effinger" -ZZ

sollte Einträge des LDAP-Servers zurückgeben. Eigentlich wäre es auch das Beste, wenn der LDAP-Server nur TLS-verschlüsselte Verbindungen akzeptieren würde. Das kann man über ACL mit den Security Strength Factors (SSF) [23] einstellen, allerdings unterstützt Exim bislang keine TLS-verschlüsselten LDAP-Verbindungen, so dass wir hierauf verzichten.

LDAP-Einträge hinzufügen

Jetzt können wir Einträge in OpenLDAP anlegen. Sehr komfortabel kann man dazu JXplorer [24] verwenden (zur Installation siehe Abschnitt Die nächsten Schritt mit LDAP im OpenLDAP 1×1 [14]). Wir starten JXplorer und wählen im Menü Datei>Verbinden. Dort tragen wir folgendes ein (localhost bzw. myserver anpassen):
Connection Dialog from JXplorer [25]

Die Daten noch mal im Überblick:

Nach der erfolgreichen Verbindungsherstellung sehen wir die LDAP-Hierarchie und erweitern den Baum unter o=effinger, so dass wir folgendes sehen:
LDAP Hierarchie nach dem Hinzufügen von users [26]

Im nächsten Schritt fügen wir unseren ersten Benutzer Paul hinzu. Dazu Rechtsklick auf users im LDAP-Baum und Neu auswählen. Im nachfolgenden Dialog ändern wir die RDN auf uid=paul und fügen die Klassen top, person, organizationalPerson, inetOrgPerson, posixAccount und dcMailUser hinzu.
Adding a user to the LDAP directory with JXplorer [27]

Nun sehen wir im nächsten Dialog einige fett gedruckte Attribute – diese sind Pflichtattribute und müssen daher ausgefüllt werden.
Adding necessary user attributes with JXplorer [28]

Danach klicken wir auf den Button Abschicken und Paul ist als Benutzer in LDAP verfügbar.

Jetzt richten wir noch den externen MailAccount von Paul ein, damit er auch seine Mails von GMX abrufen bzw. über GMX schicken kann. Dazu erweitern wir die Ansicht der LDAP-Hierarchie, so dass wir mit einem Rechtsklick auf paul erneut den Eintrag Neu wählen können.
LDAP tree after adding paul as a user [29]
Im erscheinenden Dialogfeld wählen wir die Klassen dcExternalMailAccount und dcPosixSubAccount aus. RDN setzen wir auf dcSubMailAddress=paulpanzer@gmx.de. Folgende Attribute können bzw. müssen wir eintragen:

Und so sieht das bei mir aus:
Adding an external mail account to the LDAP directory [32]
Nun wieder auf Abschicken klicken und der Eintrag sollte vorhanden sein. Wichtig ist, jetzt nochmal zu prüfen, ob auch das Dynlist-Overlay funktioniert. Das machen wir mit einem

ldapsearch -b "dcSubMailAddress=paulpanzer@gmx.de,uid=paul,ou=users,o=effinger" -x

Dieser Befehl sollte unter anderem die Zeilen mit den Attributen uid, uidNumber,gidNumber und dcMailMessageStore augeben:

dcMailMessageStore: /home/paul
gidNumber: 1000
uidNumber: 1000
uid: paul

Testen und Mitschneiden von Anfragen an OpenLDAP

Zum Testen und Debuggen ist es oft hilfreich, zu überprüfen, welche LDAP Anfragen an den OpenLDAP-Server gestellt wurden. Dazu kann man unter cn=config (mit cn=admin,cn=config einloggen) den Parameter olcLogLevel von none auf einen numerischen Wert ändern. 256 hat sich für mich als gut bewährt, bei Terrence Miao findet man aber eine detaillierte Auflistung der einzelnen Loglevels [33]. Die LDAP-Anfragen werden dann in die syslog geschrieben.

Da der Artikel nun schon ziemlich lang ist, folgt die Konfiguration von dovecot in einem zweiten Teil [34].