Installation via Docker
Beschreibung:
Ein ldap Server für Adressbücher mit carddav sync.
Installation:
Docker installieren
apt install docker.io docker-compose curl
Nun Projektverzeichnisse erstellen
ldap-carddav-stack/
├── docker-compose.yml
├── .env
├── ldap-carddav/
│ └── conf.php # deine Konfiguration
├── Dockerfile # für ldap-carddav
├── ldap-carddav-data
mkdir -p /root/ldap-carddav-stack/ldap-carddav
mkdir -p /root/ldap-carddav-stack/ldap-carddav-data
Dockerfile erstellen zum image bauen
nano /root/ldap-carddav-stack/Dockerfile
Inhalt
FROM debian:bullseye
ENV DEBIAN_FRONTEND=noninteractive
# Abhängigkeiten installieren
RUN apt-get update && apt-get install -y \
apache2 \
php \
php-ldap \
php-xml \
php-mbstring \
php-sqlite3 \
sqlite3 \
libapache2-mod-php \
nano \
curl \
ldap-utils \
composer \
&& apt-get clean
# ldap-carddav klonen
RUN git clone https://github.com/isubsoft/ldap-carddav.git /var/www/html/ldap-carddav
# Composer-Abhängigkeiten installieren
WORKDIR /var/www/html/ldap-carddav
RUN composer install
# Rewrite-Modul aktivieren
RUN a2enmod rewrite
# 000-default.conf ersetzen
RUN rm /etc/apache2/sites-enabled/000-default.conf && \
echo '<VirtualHost *:80>\n\
ServerAdmin admin@example.org\n\
DocumentRoot /var/www/html/ldap-carddav\n\
\n\
<Directory /var/www/html/ldap-carddav>\n\
Options Indexes FollowSymLinks\n\
AllowOverride All\n\
Require all granted\n\
DirectoryIndex server.php\n\
RewriteEngine On\n\
RewriteCond %{REQUEST_FILENAME} !-f\n\
RewriteCond %{REQUEST_FILENAME} !-d\n\
RewriteRule ^(.*)$ server.php [QSA,L]\n\
</Directory>\n\
\n\
Redirect 301 /.well-known/carddav /server.php\n\
</VirtualHost>' > /etc/apache2/sites-enabled/000-default.conf
# Apache starten
CMD ["apachectl", "-D", "FOREGROUND"]
EXPOSE 80
Die .env Datei
nano /root/ldap-carddav-stack/.env
Inhalt
LDAP_ORGANISATION=ExampleCorp
LDAP_DOMAIN=example
LDAP_TOP_DOMAIN=local
LDAP_ADMIN_PASSWORD=admin
Die compose Datei
nano /root/ldap-carddav-stack/docker-compose.yml
Inhalt
version: '3.8'
services:
ldap:
image: osixia/openldap:1.5.0
container_name: ldap
environment:
LDAP_ORGANISATION: ${LDAP_ORGANISATION}
LDAP_DOMAIN: ${LDAP_DOMAIN}.${LDAP_TOP_DOMAIN}
LDAP_ADMIN_PASSWORD: ${LDAP_ADMIN_PASSWORD}
volumes:
- ./ldap_data:/var/lib/ldap
- ./ldap_config:/etc/ldap/slapd.d
ports:
- "389:389"
phpldapadmin:
image: osixia/phpldapadmin:0.9.0
container_name: phpldapadmin
environment:
PHPLDAPADMIN_LDAP_HOSTS: ldap
ports:
- "6443:443"
carddav:
build:
context: .
dockerfile: Dockerfile
container_name: ldap-carddav
ports:
- "80:80"
volumes:
- ./ldap-carddav/conf.php:/var/www/html/ldap-carddav/conf/conf.php:ro
- ./ldap-carddav-data:/var/www/html/ldap-carddav/data
depends_on:
- ldap
environment:
- LDAP_HOST=ldap
- LDAP_BASE_DN=ou=${LDAP_ORGANISATION},dc=${LDAP_DOMAIN},dc=${LDAP_TOP_DOMAIN}
- LDAP_BIND_DN=cn=admin,ou=${LDAP_ORGANISATION},dc=${LDAP_DOMAIN},dc=${LDAP_TOP_DOMAIN}
- LDAP_BIND_PASSWORD=${LDAP_ADMIN_PASSWORD}
# - LDAP_BASE_DN_SYNC=dc=${LDAP_DOMAIN},dc=${LDAP_TOP_DOMAIN}
# - LDAP_BIND_DN_SYNC=cn=shacker,dc=${LDAP_DOMAIN},dc=${LDAP_TOP_DOMAIN}
# - LDAP_BIND_PASSWORD_SYNC=1234
Container starten:
docker-compose up -d
PHP File
nano /root/ldap-carddav-stack/ldap-carddav/conf.php
Inhalt
<?php
$config = [];
// === TEMP / DATA ===
$config['tmpdir'] = '%systempdir';
$config['datadir'] = '/var/www/html/ldap-carddav/data';
// === DATABASE ===
$config['sync_database'] = [
'dsn' => 'sqlite:/var/www/html/ldap-carddav/data/cards.db',
'username' => '',
'password' => '',
'options' => [],
'init_commands' => []
];
// === LDAP SERVER CONFIG ===
$config['server']['ldap'] = [
'host' => getenv('LDAP_HOST') ?: 'localhost',
'network_timeout' => 10,
'connection_security' => 'none'
];
// === LDAP AUTH ===
$config['auth']['ldap'] = [
'base_dn' => getenv('LDAP_BASE_DN') ?: 'dc=example,dc=local',
'bind_dn' => '%dn',
'bind_pass' => '%p',
'search_base_dn' => '',
'search_filter' => '(&(objectclass=inetOrgPerson)(uid=%u))',
'search_bind_dn' => getenv('LDAP_BIND_DN') ?: 'cn=admin,dc=example,dc=local',
'search_bind_pw' => getenv('LDAP_BIND_PASSWORD') ?: 'admin',
'scope' => 'list'
];
// === PRINCIPAL SEARCH ===
$config['principal']['ldap'] = [
'base_dn' => getenv('LDAP_BASE_DN') ?: 'dc=example,dc=local',
'search_base_dn' => '',
'search_filter' => '(&(objectclass=inetOrgPerson)(uid=*))',
'search_bind_dn' => getenv('LDAP_BIND_DN') ?: 'cn=admin,dc=example,dc=local',
'search_bind_pw' => getenv('LDAP_BIND_PASSWORD') ?: 'admin',
'scope' => 'list',
'fieldmap' => [
'id' => 'uid',
'displayname' => 'cn',
'mail' => 'mail'
]
];
// Hinweis: Die folgenden Einträge sind stark gekürzt. Siehe Original für volle Struktur.
// Du kannst z. B. $config['card']['addressbook']['ldap']['me'], ['global'], ['personal'] wie oben mit getenv() einbinden.
// Beispiel für ein Adressbuch-Eintrag mit bind_dn über ENV
$config['card']['addressbook']['ldap']['personal'] = [
'name' => 'starface',
'description' => 'Starface Kontakte',
'user_specific' => true,
'writable' => true,
'group_LDAP_Object_Classes' => ['groupOfNames'],
'group_required_fields' => ['cn', 'member'],
'group_LDAP_rdn' => 'cn',
'group_member_map' => [ 'MEMBER' => [ 'field_name' => 'member' ] ],
'base_dn' => getenv('LDAP_BASE_DN') ?: 'dc=example,dc=local',
'filter' => '(objectClass=inetOrgPerson)',
'bind_dn' => getenv('LDAP_BIND_DN') ?: 'cn=admin,dc=example,dc=local',
'bind_pass' => getenv('LDAP_BIND_PASSWORD') ?: 'admin',
'scope' => 'sub',
'LDAP_Object_Classes' => ['inetOrgPerson'],
'required_fields' => ['cn','sn'],
'LDAP_rdn' => 'cn',
// Schreibrechte aktivieren
//'field_acl' => [
// 'eval' => 'w'
// 'list' => ['displayName', 'homePhone', 'telephoneNumber', 'facsimileTelephoneNumber', 'pager', 'mobile', 'homePostalAddress', 'preferredLanguage']
// leere Liste = alles erlaubt
// ],
// Vollständige Feldzuordnung für Outlook / CalDAV
'fieldmap' => [
'FN' => ['field_name' => 'cn'],
'N' => ['field_name' => [
'last_name' => 'sn',
'first_name' => 'givenName',
'prefix' => 'personalTitle'
]],
'EMAIL' => ['field_name' => 'mail'],
'ORG' => ['field_name' => [
'org_name' => 'o',
'org_unit_name' => 'ou'
]],
'TITLE' => ['field_name' => 'title'],
'ROLE' => ['field_name' => 'employeeType'],
'NICKNAME' => ['field_name' => 'displayName'],
'PHOTO' => [[
'field_name' => 'jpegphoto',
'parameters' => [],
'reverse_map_parameter_index' => 0,
'decode_file' => true
]],
'NOTE' => ['field_name' => 'description'],
'TEL' => [
[ // Fax number
'field_name' => 'facsimileTelephoneNumber',
'parameters' => [
['TYPE' => ['fax']],
['TYPE' => ['fax', 'work']],
['TYPE' => ['work', 'fax']],
['TYPE' => 'facsimile'],
['TYPE' => ['voice', 'fax']],
['TYPE' => ['fax', 'voice']],
null
],
'reverse_map_parameter_index' => 0
],
[ // Work number
'field_name' => 'telephoneNumber',
'parameters' => [
['TYPE' => ['work']],
['TYPE' => ['voice', 'work']],
['TYPE' => 'work'],
['TYPE' => 'voice'],
null
],
'reverse_map_parameter_index' => 0
],
[ // Home number
'field_name' => 'homePhone',
'parameters' => [
['TYPE' => ['home']],
['TYPE' => ['voice', 'home']],
['TYPE' => 'home'],
null
],
'reverse_map_parameter_index' => 0
],
[ // Mobile number
'field_name' => 'mobile',
'parameters' => [
['TYPE' => ['cell']],
['TYPE' => ['voice', 'cell']],
['TYPE' => 'cell'],
null
],
'reverse_map_parameter_index' => 0
],
[ // Pager
'field_name' => 'pager',
'parameters' => [
['TYPE' => ['pager']],
null
],
'reverse_map_parameter_index' => 0
]
],
'NOTE' => [
'field_name' => 'description',
'parameters' => [],
'reverse_map_parameter_index' => 0
],
'ADR' => [
[
'field_name' => [
'po_box' => 'postOfficeBox',
'street' => 'street',
'locality' => 'l',
'province' => 'st',
'postal_code' => 'postalCode'
],
'parameters' => ['TYPE' => 'work'],
'map_component_separator' => ';',
'reverse_map_parameter_index' => 0
],
// Privatadresse
[
'field_name' => 'homePostalAddress',
'parameters' => ['TYPE' => 'home'],
'map_component_separator' => '$',
'reverse_map_parameter_index' => 0
]
],
'LANG' => ['field_name' => 'preferredLanguage']
]
];
Aufrufen LDAP und co:
-
phpLDAPadmin:
https://localhost:6443
Benutzername aus unserem Beispiel : cn=admin,dc=example,dc=local
Passwort aus unserem Beispiel : admin -
ldap-carddav WebDAV/CardDAV: http://localhost/ldap-carddav/
Die Benutzer dazu werden im LDAP Webgui angelegt, dazu ein ein eigenes Kaptitel
Datenbank initialiseren
docker-compose exec carddav /bin/bash
sqlite3 /var/www/html/ldap-carddav/data/cards.db < /var/www/html/ldap-carddav//sql/sqlite/ddl.sql
php /var/www/html/ldap-carddav/src/App/syncdb.php init
Danach vom conatiner wieder abmelden und chmod 777 über die card.db
chmod 777 /root/ldap-carddav-stack/ldap-carddav-data/cards.db
PHP ini Änderungen durchführen und neu mit der Datenbank synchroniesieren:
Wenn die PHP geändert wird um zum Beispiel Felder hinzugefügt werden, muss ide Datenbank neu initialisert werden.
docker-compose exec carddav /bin/bash
php /var/www/html/ldap-carddav/src/App/syncdb.php
Ausgabe:
Dort 0 Auswählen
hoose the entity you want to operate upon. Enter 0 for addressbook and 1 for user:
Nun mit 3 bestätigen
Enter the operation to perform on address book. Enter 0 to list, 1 to add, 2 to rename and 3 to delete:
Nun Adressbuchname eingeben:personal.
Enter name of the address book to delete:
Nun wurde das Buch gelöscht
Address book 'personal' has been deleted.
Nun kann ein neuer init stattfinden
php /var/www/html/ldap-carddav/src/App/syncdb.php init
Ausgabe:
Initializing sync database ...
Address book 'personal' has been successfully added to sync database.
Address book(s) successfully imported.
Benutzer anlegen:
Im LDAP Webgui einloggen unter
https://<ip>:6443
Dort neues child Element anlegen...
Vom Typ PosixGroup
mit dem namen users
Nun nochmals bestätigen
nun eine weitere elemt vom typ organizationRole mit den namen write anlegen
nun den Namen vergeben
runter scrollen bis create object
Nun einen Benutzer anlegen vom typ inetOrgPerson
Daten ausfüllen
Nun sieht das ganz so aus:
Nun den benutzer im carddav Backend anlegen, nicht im ldap, das haben wir gerade getan.
docker-compose exec carddav /bin/bash
php /var/www/html/ldap-carddav/src/App/syncdb.php
Fehlersuche:
Sollte Verbindung nicht zustande kommen, dann mal in die Apache2 log schauen.
Im container anmelden
docker-compose exec carddav /bin/bash
cat /var/log/apache2/error.log
Ausgabe:
Testen der Daten
ldapwhoami -x -D "cn=admin,dc=example,dc=local" -w admin -H ldap://ldap
Ausgabe:
Testen ob ein Objekt sich ändern lässt
ldapmodify -x -D "cn=admin,dc=example,dc=local" -w admin -H ldap://ldap
Einfügen und dann strg+d drücken
dn: cn=shacker,dc=example,dc=local
changetype: modify
replace: mail
mail: test@example.org
Ein komplettes Objekt ausgeben
ldapsearch -x \
-D "cn=admin,dc=example,dc=local" \
-w admin \
-b "cn=Wolf\, SDaniel,dc=example,dc=local" \
-LLL
Ausgabe:














