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/
│ └── config.php # deine Konfiguration
├── Dockerfile # für ldap-carddav
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:bookworm
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
# Apache-Konfiguration erweitern für mod_rewrite
RUN echo "<Directory /var/www/html/ldap-carddav>\n\
Options Indexes FollowSymLinks\n\
AllowOverride All\n\
Require all granted\n\
</Directory>" >> /etc/apache2/apache2.conf
# Rewrite-Modul aktivieren
RUN a2enmod rewrite
# .htaccess für Clean URLs und .well-known Redirect einfügen
RUN echo 'RewriteEngine On\n\
RewriteBase /ldap-carddav\n\
RewriteCond %{REQUEST_FILENAME} !-f\n\
RewriteCond %{REQUEST_FILENAME} !-d\n\
RewriteRule ^(.*)$ server.php [QSA,L]\n\
\n\
Redirect 301 /.well-known/carddav /ldap-carddav/' \
> /var/www/html/ldap-carddav/.htaccess
# 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}
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' => 'uid'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' => 'telephoneNumber'mobile',
'parameters' => [
['TYPE' => ['cell']],
['field_name'TYPE' => ['voice', 'cell']],
['TYPE' => 'facsimileTelephoneNumber'cell'],
null
],
'reverse_map_parameter_index' => 0
],
[ // Pager
'field_name' => 'pager'],
['field_name'parameters' => [
['mobile'TYPE' => ['pager']],
null
],
'reverse_map_parameter_index' => 0
]
],
'ADR' => [
['field_name'
=> 'homePostalAddress'],
['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
-
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
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:


