Local OpenLDAP Setup

About

Setting up a local LDAP server is essential for learning, testing integrations, or developing features dependent on centralized directory services. This setup mimics real-world LDAP deployments and allows experimentation with authentication, user federation (e.g., in Keycloak), and automation.

For simplicity and repeatability, we use Docker to spin up:

  • An OpenLDAP server using the osixia/openldap image

  • A GUI client (phpLDAPadmin) to manage the directory visually

Prerequisites

  • Docker and Docker Compose installed

  • Basic knowledge of LDAP terminology (DN, OU, CN, etc.)

  • Familiarity with command-line operations

Technology Stack

Component
Description

OpenLDAP

Lightweight directory server implementation

phpLDAPadmin

Web UI for LDAP directory management

Docker

Containerized setup for isolated testing

Folder Structure

Create a folder named local-ldap-setup:

mkdir local-ldap-setup && cd local-ldap-setup

Create the following files:

.
├── docker-compose.yml
└── ldap-bootstrap/seed-data.ldif

docker-compose.yml

The docker-compose.yml file is a configuration file used by Docker Compose to define and run multi-container Docker applications — in this case, an LDAP server and a GUI admin interface.

What it does in this setup

  • Defines two services:

    • openldap: Starts the OpenLDAP directory server

    • phpldapadmin: Starts a web-based GUI to interact with the LDAP directory

  • Sets environment variables:

    • LDAP_DOMAIN, LDAP_ORGANISATION, and LDAP_ADMIN_PASSWORD define the base configuration for the OpenLDAP server.

  • Exposes ports:

    • Port 389: for standard LDAP communication

    • Port 636: for LDAPS (secure LDAP, optional)

    • Port 6443: to access phpLDAPadmin via a browser over HTTPS

  • Mounts seed data:

    • Maps the local file seed-data.ldif into the container so it can be auto-imported at startup to bootstrap the directory with users, groups, and OUs.

This file enables you to spin up a complete LDAP environment with a single command:

docker-compose up -d

seed-data.ldif

The seed-data.ldif file serves as a bootstrap initializer for your LDAP directory. It defines a realistic, structured set of directory entries that simulate how an enterprise LDAP setup might look. This includes:

  • Organizational Units (OUs) for different categories of entries

  • Users across departments

  • Groups for access control

  • Service accounts for system integrations (e.g., Keycloak)

When this file is mounted in the Docker Compose setup, the OpenLDAP container reads and loads its contents at startup — allowing your directory to come pre-populated with meaningful data.

1. Organizational Units (OUs)

Organizational Units help categorize and separate types of directory entries. This keeps the directory clean and enables easier access control, delegation, and search filters.

dn: ou=People,dc=corp,dc=acme,dc=com
objectClass: organizationalUnit
ou: People
  • ou=People: Stores human user accounts like employees.

  • ou=Groups: Stores role-based groups (engineering, HR, etc.).

  • ou=Departments: Placeholder for defining department-based entries (can be linked via departmentNumber).

  • ou=ServiceAccounts: Stores machine or integration accounts used by applications.

These OUs mirror common real-world enterprise directory layouts.

2. User Entries

Each user is an inetOrgPerson object — a standard LDAP object class for representing people. It includes attributes like cn, sn, uid, mail, and userPassword.

Example:

dn: uid=alice.jones,ou=People,dc=corp,dc=acme,dc=com
objectClass: inetOrgPerson
cn: Alice Jones
sn: Jones
uid: alice.jones
mail: [email protected]
userPassword: Password@123
departmentNumber: ENG
title: Senior Software Engineer
  • UID: Unique identifier within the directory.

  • departmentNumber: Common attribute used for department-level filtering.

  • title: Useful for role-based access, UI display, or hierarchy mapping.

This structure makes it easy to:

  • Filter users by department

  • Assign access based on job title

  • Identify users with consistent naming conventions

3. Group Entries

Groups are created using the groupOfNames object class, which references user DNs via the member attribute.

dn: cn=engineering,ou=Groups,dc=corp,dc=abc,dc=com
objectClass: groupOfNames
cn: engineering
member: uid=alice.jones,ou=People,dc=corp,dc=abc,dc=com
  • cn=engineering: Group name

  • member: References full DN of user(s)

These groups support:

  • Role-based access control (RBAC) in external apps

  • LDAP queries for access policies (memberOf attributes, filters)

You can associate users with multiple groups for layered access.

4. Service Account

Service accounts are special user entries used by applications (e.g., Keycloak) to connect to the LDAP server.

dn: uid=svc.keycloak,ou=ServiceAccounts,dc=corp,dc=abc,dc=com
objectClass: inetOrgPerson
cn: Keycloak Service Account
sn: Service
uid: svc.keycloak
mail: [email protected]
userPassword: Keycloak@2024
  • Located under ou=ServiceAccounts

  • Follows naming convention svc.<application>

  • Can be granted read-only or scoped privileges

These accounts enable:

  • Application integration without using human credentials

  • Logging and auditing by account identity

  • Password rotation automation via DevOps pipelines

Docker Compose File

docker-compose.yml

version: '3.8'

services:
  openldap:
    image: osixia/openldap:1.5.0
    container_name: openldap
    command: --copy-service
    environment:
      LDAP_ORGANISATION: "Abc Solutions Pvt Ltd"
      LDAP_DOMAIN: "corp.abc.com"
      LDAP_ADMIN_PASSWORD: "StrongAdminPass123"
    ports:
      - "389:389"
      - "636:636"
    volumes:
      - ./ldap-bootstrap/seed-data.ldif:/container/service/slapd/assets/config/bootstrap/ldif/50-bootstrap.ldif

  phpldapadmin:
    image: osixia/phpldapadmin:0.9.0
    container_name: phpldapadmin
    environment:
      PHPLDAPADMIN_LDAP_HOSTS: openldap
    ports:
      - "6443:443"
    depends_on:
      - openldap

Always make sure our LDIF entries use the same base DN that matches our LDAP_DOMAIN setting — otherwise, the directory won’t accept them during bootstrapping or imports.

If we change LDAP_DOMAIN, also update all dn: entries in your LDIF files accordingly.

If our domain is corp.abc.com, our full Distinguished Names (DNs) in the LDAP tree will look like:

  • Base DN:

    dc=corp,dc=abc,dc=com
  • A user:

    uid=john.doe,ou=People,dc=corp,dc=abc,dc=com
  • A group:

    cn=admins,ou=Groups,dc=corp,dc=abc,dc=com

Initial Directory Seed (seed-data.ldif)

# Organizational Units
dn: ou=People,dc=corp,dc=abc,dc=com
objectClass: organizationalUnit
ou: People

dn: ou=Groups,dc=corp,dc=abc,dc=com
objectClass: organizationalUnit
ou: Groups

dn: ou=Departments,dc=corp,dc=abc,dc=com
objectClass: organizationalUnit
ou: Departments

dn: ou=ServiceAccounts,dc=corp,dc=abc,dc=com
objectClass: organizationalUnit
ou: ServiceAccounts

# Sample user in Engineering
dn: uid=alice.jones,ou=People,dc=corp,dc=abc,dc=com
objectClass: inetOrgPerson
cn: Alice Jones
sn: Jones
uid: alice.jones
mail: [email protected]
userPassword: Password@123
departmentNumber: ENG
title: Senior Software Engineer

# Sample user in HR
dn: uid=bob.smith,ou=People,dc=corp,dc=abc,dc=com
objectClass: inetOrgPerson
cn: Bob Smith
sn: Smith
uid: bob.smith
mail: [email protected]
userPassword: Password@123
departmentNumber: HR
title: HR Manager

# Sample group for engineers
dn: cn=engineering,ou=Groups,dc=corp,dc=abc,dc=com
objectClass: groupOfNames
cn: engineering
member: uid=alice.jones,ou=People,dc=corp,dc=abc,dc=com

# Sample group for HR
dn: cn=hr-team,ou=Groups,dc=corp,dc=abc,dc=com
objectClass: groupOfNames
cn: hr-team
member: uid=bob.smith,ou=People,dc=corp,dc=abc,dc=com

# Service account
dn: uid=svc.keycloak,ou=ServiceAccounts,dc=corp,dc=abc,dc=com
objectClass: inetOrgPerson
cn: Keycloak Service Account
sn: Service
uid: svc.keycloak
mail: [email protected]
userPassword: Keycloak@2024

Start the Containers

docker-compose up -d

Check logs:

docker logs -f openldap

Access phpLDAPadmin UI

Open your browser and visit:

https://localhost:6443

Login DN: cn=admin,dc=example,dc=com

Password: admin

Once logged in:

  • Browse entries

  • Add more users/groups via GUI

  • Edit attributes

Verifying LDAP Server Using ldapsearch

Install LDAP utilities if not already:

sudo apt install ldap-utils  # Debian/Ubuntu

Example command:

ldapsearch -x -H ldap://localhost -D "cn=admin,dc=example,dc=com" -w admin -b "dc=example,dc=com"

Search for user:

ldapsearch -x -b "dc=example,dc=com" "(uid=john.doe)"

Based on local LDAP setup

Keycloak LDAP Configuration

Field

Value

Edit Mode

READ_ONLY or IMPORT (choose based on use case)

Vendor

Other

Connection URL

ldap://localhost:389

Bind DN

cn=admin,dc=corp,dc=abc,dc=com

Bind Credential

StrongAdminPass123 (or whatever you used in docker-compose)

Users DN

ou=People,dc=corp,dc=abc,dc=com

Username LDAP attribute

uid

RDN LDAP attribute

uid

UUID LDAP attribute

entryUUID

User Object Classes

inetOrgPerson

Search Scope

One Level or Subtree (Subtree recommended)

Pagination

ON

Sync Registrations

OFF (or ON if you want new Keycloak users written to LDAP)

Allow Kerberos Authentication

OFF (unless you’re integrating with AD or SASL/Kerberos)

Batch Size For LDAP Queries

1000 (default)

Keycloak Group Mapper Configuration

If we're syncing groups from LDAP as well (e.g., cn=engineering, cn=hr-team):

Field

Value

Mapper Type

Group LDAP Mapper

Name

LDAP Groups Mapper

LDAP Groups DN

ou=Groups,dc=corp,dc=abc,dc=com

Group Name LDAP Attribute

cn

Group Object Classes

groupOfNames

Membership LDAP Attribute

member

Membership Attribute Type

DN

User Groups Retrieve Strategy

LOAD_GROUPS_BY_MEMBER_ATTRIBUTE

Mapped Group Attributes

Leave blank or include additional group info like description

Mode

READ_ONLY

Keycloak Test LDAP Sync

Once configured:

  1. Navigate to User Federation → ldap → Synchronize all users

  2. Check the Users tab → alice.jones and bob.smith should appear.

  3. Navigate to Groups (if group mapper is configured) → engineering, hr-team should be available.

phpLDAPadmin Login Details

Field

Value

Login URL

https://localhost:6443

Login DN (username)

cn=admin,dc=corp,dc=abc,dc=com

Password

StrongAdminPass123 (as defined in LDAP_ADMIN_PASSWORD)

We may get a browser warning because the container uses a self-signed TLS certificate. We can safely proceed for local use.

  • Bind DN Format: The full distinguished name (cn=admin,dc=corp,dc=abc,dc=com) is required — not just admin.

  • Default login user is cn=admin, which has full access to the directory unless restricted via ACL.

  • phpLDAPadmin does not support plain HTTP, it runs on HTTPS (port 6443) by default.

  • We can add other admin or read-only users via seed-data.ldif or phpLDAPadmin interface later if needed.

Verify seeded data for People, Groups, Departments, and ServiceAccounts

Last updated