{"id":28896377,"url":"https://github.com/maximewewer/openldap-docker-setup","last_synced_at":"2026-04-24T20:34:31.186Z","repository":{"id":297839593,"uuid":"998062161","full_name":"MaximeWewer/OpenLDAP-docker-setup","owner":"MaximeWewer","description":"OpenLDAP-docker-setup is a project designed to simplify the deployment and management of an OpenLDAP server with phpLDAPadmin and self-service-password interfaces. Ideal for development or production environments.","archived":false,"fork":false,"pushed_at":"2025-06-12T01:31:12.000Z","size":44,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-06-21T05:45:52.079Z","etag":null,"topics":["backup","docker","ldap","openldap","openldap-docker","openldap-server","phpldapadmin","selfservice-password-reset","zitadel"],"latest_commit_sha":null,"homepage":"","language":"Shell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/MaximeWewer.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-06-07T19:33:07.000Z","updated_at":"2025-06-12T01:31:15.000Z","dependencies_parsed_at":"2025-06-07T20:38:14.769Z","dependency_job_id":null,"html_url":"https://github.com/MaximeWewer/OpenLDAP-docker-setup","commit_stats":null,"previous_names":["maximewewer/openldap-docker-setup"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/MaximeWewer/OpenLDAP-docker-setup","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MaximeWewer%2FOpenLDAP-docker-setup","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MaximeWewer%2FOpenLDAP-docker-setup/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MaximeWewer%2FOpenLDAP-docker-setup/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MaximeWewer%2FOpenLDAP-docker-setup/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/MaximeWewer","download_url":"https://codeload.github.com/MaximeWewer/OpenLDAP-docker-setup/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MaximeWewer%2FOpenLDAP-docker-setup/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32239874,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-24T13:21:15.438Z","status":"ssl_error","status_checked_at":"2026-04-24T13:21:15.005Z","response_time":64,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["backup","docker","ldap","openldap","openldap-docker","openldap-server","phpldapadmin","selfservice-password-reset","zitadel"],"created_at":"2025-06-21T05:38:52.738Z","updated_at":"2026-04-24T20:34:31.180Z","avatar_url":"https://github.com/MaximeWewer.png","language":"Shell","readme":"# OpenLDAP docker setup\r\n\r\nA streamlined way to deploy an **[OpenLDAP](https://openldap.org/)** server along with **[phpLDAPadmin](https://github.com/leenooks/phpLDAPadmin)** and **[Self Service Password](https://github.com/ltb-project/self-service-password)** using Docker Compose. Built on the minimal [cleanstart/openldap](https://hub.docker.com/r/cleanstart/openldap) image (OpenLDAP 2.6).\r\n\r\n## Key features\r\n\r\n- **Minimal image**: Uses `cleanstart/openldap` — no shell, no bootstrap scripts, full control via `slapadd`\r\n- **Secure by default**: Least-privilege ACLs per OU, SSHA-hashed rootDN passwords, ECDSA P-384 TLS certificates, isolated Docker network\r\n- **Pre-configured overlays**: memberof, referential integrity, password policy, dynamic lists\r\n- **Service accounts**: Dedicated OU with per-account ACL injection via scripts\r\n- **POSIX optional**: POSIX support (posixAccount/shadowAccount) available via opt-in flag\r\n- **Administration scripts**: Manage users, groups, and service accounts from the command line\r\n\r\n## Architecture\r\n\r\n```\r\ndc=example,dc=org\r\n  |-- ou=users                 # User accounts (inetOrgPerson)\r\n  |-- ou=groups                # Groups (groupOfNames)\r\n  |-- ou=service-accounts      # Service accounts (phpldapadmin, ssp, custom)\r\n  |-- ou=policies              # Password policies\r\n```\r\n\r\n### ACL matrix (least privilege)\r\n\r\n**Main database** (`dc=example,dc=org`):\r\n\r\n| Identity         | userPassword | service-accounts | users | groups | policies | base DN |\r\n| ---------------- | ------------ | ---------------- | ----- | ------ | -------- | ------- |\r\n| self             | write        | -                | write | -      | -        | -       |\r\n| admin (ou=users) | write        | write            | write | write  | read     | write   |\r\n| adminconfig      | -            | -                | read  | -      | -        | -       |\r\n| ssp              | write        | -                | -     | -      | read     | -       |\r\n| phpldapadmin     | -            | -                | read  | read   | read     | -       |\r\n| anonymous        | auth only    | -                | -     | -      | read     | read    |\r\n\r\n**Infrastructure databases**:\r\n\r\n| Identity    | cn=config | cn=accesslog | cn=Monitor |\r\n| ----------- | --------- | ------------ | ---------- |\r\n| adminconfig | manage    | read         | read       |\r\n| *           | -         | -            | -          |\r\n\r\nUsers and applications that need read access to `ou=users` or `ou=groups` must use a dedicated service account (see [Service accounts](#service-accounts)).\r\n\r\n## Getting started\r\n\r\n### Prerequisites\r\n\r\n- Docker \u0026 Docker Compose\r\n- `ldap-utils` (`ldapsearch`, `ldapadd`, `ldapmodify`, `ldapdelete`)\r\n- `pwgen`\r\n\r\n### Installation\r\n\r\n1. **Generate certificates** (optional, for TLS)\r\n\r\n```bash\r\nbash 00-certs.sh\r\n```\r\n\r\n2. **Run the setup**\r\n\r\nThis bootstraps the configuration via `slapadd`, loads initial data, and starts the containers:\r\n\r\n```bash\r\nbash 01-setup.sh\r\n```\r\n\r\nTo reinitialize from scratch:\r\n\r\n```bash\r\nbash 01-setup.sh --reset\r\n```\r\n\r\n3. **Access the services**\r\n\r\n| Service               | URL                    | Default login                                           |\r\n| --------------------- | ---------------------- | ------------------------------------------------------- |\r\n| OpenLDAP              | `ldap://localhost:389` | `cn=admin,ou=users,dc=example,dc=org` / `adminpassword` |\r\n| phpLDAPadmin          | http://localhost:8080  | `admin` / `adminpassword`                               |\r\n| Self Service Password | http://localhost:8088  | Any LDAP user                                           |\r\n\r\n\u003e **Important**: Change all default passwords before production use.\r\n\r\n4. **Change default passwords**\r\n\r\n```bash\r\n# Change admin user password (cn=admin,ou=users)\r\nbash 03-change-user-password.sh admin\r\n\r\n# Change config admin password (cn=adminconfig,cn=config)\r\n# Generate a new SSHA hash:\r\ndocker run --rm --entrypoint slappasswd cleanstart/openldap:2.6.13 -s \"NEW_PASSWORD\"\r\n# Then update the rootDN password:\r\nldapmodify -x -H ldap://localhost:389 -D \"cn=adminconfig,cn=config\" -w \"adminpasswordconfig\" \u003c\u003cEOF\r\ndn: olcDatabase={0}config,cn=config\r\nchangetype: modify\r\nreplace: olcRootPW\r\nolcRootPW: {SSHA}PASTE_HASH_HERE\r\nEOF\r\n\r\n# Change data rootDN password (cn=admin,dc=example,dc=org)\r\nldapmodify -x -H ldap://localhost:389 -D \"cn=adminconfig,cn=config\" -w \"adminpasswordconfig\" \u003c\u003cEOF\r\ndn: olcDatabase={1}mdb,cn=config\r\nchangetype: modify\r\nreplace: olcRootPW\r\nolcRootPW: {SSHA}PASTE_HASH_HERE\r\nEOF\r\n```\r\n\r\n\u003e After changing the config admin password, update `CONFIG_ADMIN_PASS` in `common.sh`.\r\n\u003e After changing the data rootDN password, it does not affect `LOCAL_ADMIN_PASS` (scripts use the admin user, not the rootDN).\r\n\r\n## Project structure\r\n\r\n```\r\n.\r\n|-- common.sh                             # Shared configuration and helpers\r\n|-- 00-certs.sh                           # TLS certificate generation (ECDSA P-384)\r\n|-- 01-setup.sh                           # Bootstrap and start (slapadd + docker compose)\r\n|-- 02-create-users.sh                    # Create users [--group=name] [--posix]\r\n|-- 03-change-user-password.sh            # Change user password\r\n|-- 04-delete-users.sh                    # Delete users (+ group cleanup)\r\n|-- 05-create-group.sh                    # Create group with members\r\n|-- 06-add-service-account.sh             # Create service account + inject ACL\r\n|-- 07-change-service-account-password.sh # Change service account password\r\n|-- 08-delete-service-account.sh          # Delete service account + cleanup ACL\r\n|-- docker-compose.yml\r\n|-- ssp.conf.php                          # Self Service Password configuration\r\n|-- init-config/\r\n|   |-- slapd-config.ldif                 # Full cn=config (modules, schemas, ACLs, overlays)\r\n|-- init-ldifs/\r\n|   |-- 01-base.ldif                      # Base DN\r\n|   |-- 02-org-ou.ldif                    # Organizational units\r\n|   |-- 03-users.ldif                     # Default users\r\n|   |-- 04-service-accounts.ldif          # phpldapadmin \u0026 ssp accounts\r\n|   |-- 05-groups.ldif                    # Default groups\r\n|   |-- 06-default-ppolicy.ldif           # Password policy\r\n|-- certs/                                # TLS certificates\r\n|-- data/                                 # Persistent data (slapd.d + MDB)\r\n|-- backup/                               # Backup directory\r\n```\r\n\r\n## Administration scripts\r\n\r\nAll scripts source `common.sh` for shared configuration (`set -euo pipefail`, LDAP connection, helpers). Passwords are never passed via `-w` on the command line (uses `-y` with temp files). Temp files are cleaned up on exit via `trap`.\r\n\r\nAll scripts use `cn=admin,ou=users,dc=example,dc=org` (subject to ACLs, not the rootDN).\r\n\r\nPasswords are generated with `pwgen -s -y -r '#\u003c\u003e\\ \"'\"'\"' 32` (32 chars, symbols, LDIF-safe).\r\n\r\n### Create users\r\n\r\nUsernames must follow the `firstname.lastname` pattern. The script auto-populates `cn`, `sn`, `givenName`, `displayName`, and `mail`.\r\n\r\n```bash\r\n# Standard (inetOrgPerson only)\r\nbash 02-create-users.sh john.doe jane.smith --group=demo\r\n\r\n# With POSIX attributes (requires nis schema enabled in slapd-config.ldif)\r\nbash 02-create-users.sh john.doe jane.smith --group=demo --posix\r\n```\r\n\r\n### Change user password\r\n\r\n```bash\r\nbash 03-change-user-password.sh john.doe\r\n```\r\n\r\n### Delete users\r\n\r\nAutomatically removes the user from all groups before deletion:\r\n\r\n```bash\r\nbash 04-delete-users.sh john.doe jane.smith\r\n```\r\n\r\n### Create group\r\n\r\nAt least one member is required (`groupOfNames` schema constraint):\r\n\r\n```bash\r\nbash 05-create-group.sh groupName john.doe jane.smith\r\n```\r\n\r\n### Service accounts\r\n\r\nCreate a service account with specific access rights. The script creates the account in `ou=service-accounts` and injects the access rule into the existing ACL for the target subtree:\r\n\r\n```bash\r\n# Read access to ou=users\r\nbash 06-add-service-account.sh gitea --access read --subtree \"ou=users,dc=example,dc=org\"\r\n\r\n# Write access to ou=groups\r\nbash 06-add-service-account.sh myapp --access write --subtree \"ou=groups,dc=example,dc=org\"\r\n```\r\n\r\nChange password:\r\n\r\n```bash\r\nbash 07-change-service-account-password.sh gitea\r\n```\r\n\r\nDelete (also cleans up ACL references):\r\n\r\n```bash\r\nbash 08-delete-service-account.sh gitea\r\n```\r\n\r\n## POSIX support\r\n\r\nPOSIX attributes (`posixAccount`, `shadowAccount`, `uidNumber`, `gidNumber`, `homeDirectory`, `loginShell`) are **disabled by default**.\r\n\r\nTo enable POSIX support, uncomment these lines in `init-config/slapd-config.ldif` before running `01-setup.sh`:\r\n\r\n```ldif\r\n# Schema\r\n#include: file:///etc/openldap/schema/nis.ldif\r\n\r\n# Index\r\n#olcDbIndex: uidNumber,gidNumber eq\r\n\r\n# ACL (insert as {1}, shift subsequent indexes)\r\n#olcAccess: {1}to attrs=shadowLastChange by self write by * read\r\n```\r\n\r\nThen use `--posix` when creating users:\r\n\r\n```bash\r\nbash 02-create-users.sh john.doe --posix\r\n```\r\n\r\n## TLS / LDAPS\r\n\r\n1. Generate certificates:\r\n\r\n```bash\r\nbash 00-certs.sh\r\n```\r\n\r\n2. Uncomment the TLS lines in `init-config/slapd-config.ldif` (in the `cn=config` entry):\r\n\r\n```ldif\r\nolcTLSCACertificateFile: /etc/openldap/certs/openldapCA.crt\r\nolcTLSCertificateFile: /etc/openldap/certs/openldap.crt\r\nolcTLSCertificateKeyFile: /etc/openldap/certs/openldap.key\r\nolcTLSVerifyClient: never\r\n```\r\n\r\n3. Uncomment the `command` line in `docker-compose.yml` to enable `ldaps://`:\r\n\r\n```yaml\r\ncommand: [\"slapd\", \"-d\", \"0\", \"-h\", \"ldap:// ldaps://\", \"-F\", \"/etc/openldap/slapd.d\"]\r\n```\r\n\r\n4. If using phpLDAPadmin over LDAPS, update the env vars in `docker-compose.yml`:\r\n\r\n```yaml\r\n- LDAP_CONNECTION=ldaps\r\n- LDAP_PORT=636\r\n```\r\n\r\n5. Test:\r\n\r\n```bash\r\n# LDAPS\r\nLDAPTLS_CACERT=./certs/openldapCA.crt ldapsearch -x -H ldaps://localhost:636 -D \"cn=admin,ou=users,dc=example,dc=org\" -w \"adminpassword\" -b \"dc=example,dc=org\"\r\n```\r\n\r\n\u003e **Note**: If TLS is enabled after initial setup (without `--reset`), you can add the TLS config at runtime via `ldapmodify` on `cn=config` without re-bootstrapping.\r\n\r\n## Backup \u0026 restore\r\n\r\n\u003e Store backup files on an encrypted partition — they contain password hashes.\r\n\r\n### Backup\r\n\r\nSince `cleanstart/openldap` has no shell, backups are done via `tar` in an alpine container:\r\n\r\n```bash\r\n# Config backup\r\ndocker run --rm -v ./data/slapd.d:/slapd.d:ro -v ./backup:/backup alpine:latest \\\r\n  sh -c \"tar czf /backup/config_$(date +%Y%m%d).tar.gz -C /slapd.d .\"\r\n\r\n# Data backup\r\ndocker run --rm -v ./data/openldap-data:/data:ro -v ./backup:/backup alpine:latest \\\r\n  sh -c \"tar czf /backup/data_$(date +%Y%m%d).tar.gz -C /data .\"\r\n\r\n# Accesslog backup\r\ndocker run --rm -v ./data/accesslog-data:/data:ro -v ./backup:/backup alpine:latest \\\r\n  sh -c \"tar czf /backup/accesslog_$(date +%Y%m%d).tar.gz -C /data .\"\r\n```\r\n\r\n### Restore\r\n\r\n```bash\r\ndocker compose down\r\n\r\n# Clean existing data\r\ndocker run --rm -v ./data:/data alpine:latest \\\r\n  sh -c \"rm -rf /data/slapd.d/* /data/openldap-data/* /data/accesslog-data/*\"\r\n\r\n# Restore config\r\ndocker run --rm -v ./data/slapd.d:/slapd.d -v ./backup:/backup alpine:latest \\\r\n  sh -c \"tar xzf /backup/config_DATE.tar.gz -C /slapd.d\"\r\n\r\n# Restore data\r\ndocker run --rm -v ./data/openldap-data:/data -v ./backup:/backup alpine:latest \\\r\n  sh -c \"tar xzf /backup/data_DATE.tar.gz -C /data\"\r\n\r\n# Restore accesslog\r\ndocker run --rm -v ./data/accesslog-data:/data -v ./backup:/backup alpine:latest \\\r\n  sh -c \"tar xzf /backup/accesslog_DATE.tar.gz -C /data\"\r\n\r\n# Fix permissions\r\ndocker run --rm \\\r\n  -v ./data/slapd.d:/slapd.d \\\r\n  -v ./data/openldap-data:/data \\\r\n  -v ./data/accesslog-data:/alog \\\r\n  alpine:latest sh -c \"chown -R 101:102 /slapd.d /data /alog\"\r\n\r\ndocker compose up -d\r\n```\r\n\r\n### Cronjob\r\n\r\n```bash\r\n# Daily backup at 10 PM + cleanup after 30 days\r\n0 22 * * * cd /path/to/OpenLDAP-docker-setup \u0026\u0026 docker run --rm -v ./data/slapd.d:/slapd.d:ro -v ./backup:/backup alpine:latest sh -c \"tar czf /backup/config_$(date +\\%Y\\%m\\%d).tar.gz -C /slapd.d .\"\r\n0 22 * * * cd /path/to/OpenLDAP-docker-setup \u0026\u0026 docker run --rm -v ./data/openldap-data:/data:ro -v ./backup:/backup alpine:latest sh -c \"tar czf /backup/data_$(date +\\%Y\\%m\\%d).tar.gz -C /data .\"\r\n0 22 * * * cd /path/to/OpenLDAP-docker-setup \u0026\u0026 docker run --rm -v ./data/accesslog-data:/data:ro -v ./backup:/backup alpine:latest sh -c \"tar czf /backup/accesslog_$(date +\\%Y\\%m\\%d).tar.gz -C /data .\"\r\n0 23 * * * find /path/to/OpenLDAP-docker-setup/backup -name \"*.tar.gz\" -mtime +30 -delete\r\n```\r\n\r\n## LDAP commands reference\r\n\r\n```bash\r\n# List all entries\r\nldapsearch -x -H ldap://localhost:389 -D \"cn=admin,ou=users,dc=example,dc=org\" -w \"adminpassword\" -b \"dc=example,dc=org\"\r\n\r\n# List modules\r\nldapsearch -x -H ldap://localhost:389 -D \"cn=adminconfig,cn=config\" -w \"adminpasswordconfig\" \\\r\n  -b cn=config \"(objectClass=olcModuleList)\" olcModuleLoad -LLL\r\n\r\n# View ACLs\r\nldapsearch -x -H ldap://localhost:389 -D \"cn=adminconfig,cn=config\" -w \"adminpasswordconfig\" \\\r\n  -b \"olcDatabase={1}mdb,cn=config\" olcAccess -LLL\r\n\r\n# Test service account access\r\nldapsearch -x -H ldap://localhost:389 -D \"cn=gitea,ou=service-accounts,dc=example,dc=org\" -w \"PASSWORD\" \\\r\n  -b \"ou=users,dc=example,dc=org\" \"(uid=john.doe)\" cn mail\r\n```\r\n\r\n## Integration example (Zitadel, Gitea, etc.)\r\n\r\nCreate a dedicated service account instead of using the admin account:\r\n\r\n```bash\r\nbash 06-add-service-account.sh myapp --access read --subtree \"ou=users,dc=example,dc=org\"\r\n```\r\n\r\nThen configure your application with:\r\n\r\n| Setting           | Value                                            |\r\n| ----------------- | ------------------------------------------------ |\r\n| Server            | `ldap://IP_or_FQDN:389`                          |\r\n| Base DN           | `dc=example,dc=org`                              |\r\n| Bind DN           | `cn=myapp,ou=service-accounts,dc=example,dc=org` |\r\n| Bind Password     | _(generated by the script)_                      |\r\n| User filter       | `(uid=%s)`                                       |\r\n| User object class | `inetOrgPerson`                                  |\r\n| ID attribute      | `uid`                                            |\r\n| Display name      | `displayName`                                    |\r\n| Email             | `mail`                                           |\r\n| First name        | `givenName`                                      |\r\n| Last name         | `sn`                                             |\r\n\r\n## Monitoring\r\n\r\nThe `back_monitor` module is enabled in `slapd-config.ldif`. It exposes server statistics via `cn=Monitor` (connections, operations, threads, etc.), accessible with the config admin credentials:\r\n\r\n```bash\r\nldapsearch -x -H ldap://localhost:389 -D \"cn=adminconfig,cn=config\" -w \"adminpasswordconfig\" \\\r\n  -b \"cn=Monitor\" \"(objectClass=*)\" -LLL\r\n```\r\n\r\nTo expose these metrics to Prometheus, use the [OpenLDAP Prometheus Exporter](https://github.com/MaximeWewer/OpenLDAP_prometheus_exporter). It connects to `cn=Monitor` and serves metrics on an HTTP endpoint for Prometheus scraping.\r\n\r\n## Password policy\r\n\r\nThe default password policy (`cn=defaultppolicy,ou=policies`) enforces:\r\n\r\n| Rule                       | Value                   |\r\n| -------------------------- | ----------------------- |\r\n| Minimum length             | 16 characters           |\r\n| Quality check              | Enabled                 |\r\n| Max age                    | 365 days                |\r\n| Expiry warning             | 7 days before           |\r\n| History                    | 5 passwords             |\r\n| Lockout after              | 3 failed attempts       |\r\n| Lockout duration           | 30 minutes              |\r\n| Must change on first login | Yes                     |\r\n| Cleartext passwords        | Auto-hashed server-side |\r\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmaximewewer%2Fopenldap-docker-setup","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmaximewewer%2Fopenldap-docker-setup","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmaximewewer%2Fopenldap-docker-setup/lists"}