Direkt zum Hauptinhalt

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:bookwormbullseye

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-KonfigurationRewrite-Modul erweitern für mod_rewriteaktivieren
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\
        </Directory>"DirectoryIndex >>server.php\n\
        /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\
    </Directory>\n\
\n\
    Redirect 301 /.well-known/carddav /ldap-carddav/server.php\n\
</VirtualHost>' \ > /var/www/html/ldap-carddav/.htaccessetc/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}

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

    grafik.png



  • 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.

 

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:

grafik.png

Testen der Daten

Hinweis:  Paramter -w ist das Passwort aus der .env Datei für ldap

ldapwhoami -x -D "cn=admin,dc=example,dc=local" -w admin -H ldap://ldap

Ausgabe:

grafik.png

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:

grafik.png