Batch 3: SQL Databases LDAP YAML ← Full TOC

📁 Chapter 36 — LDAP & Directory Services

Complete reference for LDAP — OpenLDAP, Active Directory, LDIF format, ldapsearch CLI tools, Python ldap3, and authentication integration.
Part of The Direct Path to Linux Ubuntu — free for all.

6Topics
142+Concepts
5Examples
ldapsearchCLI tool
LDAPv3Protocol
FreeNo login
📂 LDAP Concepts 📄 LDIF Format 🔍 CLI Tools 🐍 Python LDAP 🏢 Active Directory ⚙️ OpenLDAP Setup ⚡ Examples

📂 LDAP Concepts

📂 LDAP ConceptsDirectory structure, DNs, object classes, attributes

📄 LDIF Format

📄 LDIF FormatLDIF syntax for add, modify, delete, rename entries

🔍 CLI Tools

🔍 ldapsearch & CLIldapsearch, ldapadd, ldapmodify, ldapdelete command-line tools

🐍 Python LDAP

🐍 Python LDAPpython-ldap and ldap3 — search, add, modify entries

🏢 Active Directory

🏢 Active DirectoryAD-specific attributes, queries, and authentication

⚙️ OpenLDAP Setup

⚙️ OpenLDAP SetupInstall, configure, and manage OpenLDAP on Ubuntu

⚡ Quick Examples

Install client tools: sudo apt install ldap-utils. Python: pip install ldap3.

LDAP Quick Reference

Most-used ldapsearch commands and filter syntax.

# ── ldapsearch quick reference ────────────────────────────────

# Basic search (anonymous)
ldapsearch -x -H ldap://localhost -b "dc=example,dc=com" "(objectClass=*)"

# Authenticated search — clean LDIF output
ldapsearch -LLL -x -H ldap://localhost   -D "cn=admin,dc=example,dc=com" -W   -b "ou=users,dc=example,dc=com"   "(objectClass=inetOrgPerson)" cn uid mail

# Find specific user
ldapsearch -LLL -x -H ldap://localhost   -b "dc=example,dc=com" "(uid=alice)" "*"

# Search filters
(uid=alice)                              # exact match
(mail=*@example.com)                     # wildcard
(&(objectClass=person)(uid=alice))       # AND
(|(uid=alice)(uid=bob))                  # OR
(!(uid=admin))                           # NOT
(&(objectClass=posixAccount)(uidNumber>=10000))  # numeric compare

# Active Directory filters
(&(objectCategory=person)(objectClass=user))             # all users
(&(objectClass=user)(sAMAccountName=alice))              # specific user
(&(objectClass=user)(mail=alice@example.com))            # by email
(&(objectClass=user)(memberOf=CN=Admins,OU=Groups,...))  # group member
(&(objectClass=user)(!(userAccountControl:1.2.840.113556.1.4.803:=2)))  # enabled

# Add, modify, delete
ldapadd    -x -H ldap://localhost -D "cn=admin,dc=ex,dc=com" -W -f add.ldif
ldapmodify -x -H ldap://localhost -D "cn=admin,dc=ex,dc=com" -W -f mod.ldif
ldapdelete -x -H ldap://localhost -D "cn=admin,dc=ex,dc=com" -W "uid=bob,ou=users,dc=ex,dc=com"

# Change password
ldappasswd -x -H ldap://localhost -D "cn=admin,dc=ex,dc=com" -W   -S "uid=alice,ou=users,dc=example,dc=com"

# Test authentication
ldapwhoami -x -H ldap://localhost -D "uid=alice,ou=users,dc=ex,dc=com" -W

Add Users via LDIF

Complete LDIF file to add OUs, users, and groups.

# add_structure.ldif — run: ldapadd -x -H ldap://localhost -D "cn=admin,dc=mwu,dc=com" -W -f add_structure.ldif
version: 1

# Organizational Units
dn: ou=users,dc=mywebuniversity,dc=com
changetype: add
objectClass: top
objectClass: organizationalUnit
ou: users

dn: ou=groups,dc=mywebuniversity,dc=com
changetype: add
objectClass: top
objectClass: organizationalUnit
ou: groups

# User: Alice
dn: uid=alice,ou=users,dc=mywebuniversity,dc=com
changetype: add
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: inetOrgPerson
objectClass: posixAccount
objectClass: shadowAccount
uid: alice
cn: Alice Smith
sn: Smith
givenName: Alice
displayName: Alice Smith
mail: alice@mywebuniversity.com
title: Senior Developer
departmentNumber: Engineering
uidNumber: 10001
gidNumber: 5000
homeDirectory: /home/alice
loginShell: /bin/bash
userPassword: {SSHA}your_hashed_password_here

# Generate password hash:
# slappasswd -s mypassword

# Group: developers
dn: cn=developers,ou=groups,dc=mywebuniversity,dc=com
changetype: add
objectClass: top
objectClass: groupOfNames
objectClass: posixGroup
cn: developers
gidNumber: 5001
member: uid=alice,ou=users,dc=mywebuniversity,dc=com
description: Software developers

Python LDAP Authentication

Authenticate users against LDAP/Active Directory.

# pip install ldap3

from ldap3 import Server, Connection
from functools import lru_cache
import hashlib

LDAP_URI  = "ldap://localhost"
BASE_DN   = "dc=mywebuniversity,dc=com"
USERS_OU  = f"ou=users,{BASE_DN}"
BIND_DN   = f"cn=admin,{BASE_DN}"
BIND_PASS = "admin_password"

def authenticate(username: str, password: str) -> dict | None:
    '''
    Authenticate user against LDAP.
    Returns user info dict on success, None on failure.
    Pattern: bind as service account, find user DN, then bind as user
    '''
    server = Server(LDAP_URI)

    # Step 1: Bind as service account to find user
    with Connection(server, user=BIND_DN, password=BIND_PASS, auto_bind=True) as conn:
        conn.search(USERS_OU,
            f"(&(objectClass=inetOrgPerson)(uid={username}))",
            attributes=["dn","cn","mail","title","departmentNumber","memberOf"])

        if not conn.entries:
            return None  # user not found

        user_entry = conn.entries[0]
        user_dn    = user_entry.entry_dn

    # Step 2: Bind as the user to verify password
    try:
        with Connection(server, user=user_dn, password=password, auto_bind=True) as user_conn:
            return {
                "dn":         user_dn,
                "username":   username,
                "name":       str(user_entry.cn),
                "email":      str(user_entry.mail),
                "title":      str(user_entry.title) if user_entry.title else None,
                "department": str(user_entry.departmentNumber) if user_entry.departmentNumber else None,
                "groups":     [dn.split(",")[0].replace("cn=","")
                               for dn in (user_entry.memberOf.values or [])],
            }
    except Exception:
        return None  # wrong password

def is_in_group(username: str, group_name: str) -> bool:
    server = Server(LDAP_URI)
    with Connection(server, user=BIND_DN, password=BIND_PASS, auto_bind=True) as conn:
        group_dn = f"cn={group_name},ou=groups,{BASE_DN}"
        conn.search(USERS_OU,
            f"(&(objectClass=inetOrgPerson)(uid={username})(memberOf={group_dn}))",
            attributes=["uid"])
        return len(conn.entries) > 0

# Usage
user = authenticate("alice", "alice_password")
if user:
    print(f"Authenticated: {user['name']} ({user['email']})")
    print(f"Department: {user['department']}")
    print(f"Groups: {user['groups']}")
    print(f"Is developer: {is_in_group('alice', 'developers')}")
else:
    print("Authentication failed")

# python3 auth_demo.py

Sync LDAP to Database

Pull LDAP users into PostgreSQL with Python.

# pip install ldap3 psycopg2-binary
import sqlite3  # using SQLite for demo
from ldap3 import Server, Connection, SUBTREE

LDAP_URI  = "ldap://localhost"
BASE_DN   = "dc=mywebuniversity,dc=com"
BIND_DN   = f"cn=admin,{BASE_DN}"
BIND_PASS = "admin_password"
USERS_OU  = f"ou=users,{BASE_DN}"

def sync_users_to_db():
    # LDAP connection
    server = Server(LDAP_URI)
    ldap   = Connection(server, user=BIND_DN, password=BIND_PASS, auto_bind=True)

    # Search all users
    ldap.search(USERS_OU,
        "(objectClass=inetOrgPerson)",
        search_scope=SUBTREE,
        attributes=["uid","cn","sn","givenName","mail","title",
                    "departmentNumber","telephoneNumber","memberOf"])

    ldap_users = [
        {
            "uid":        str(e.uid),
            "cn":         str(e.cn),
            "email":      str(e.mail) if e.mail else None,
            "title":      str(e.title) if e.title else None,
            "department": str(e.departmentNumber) if e.departmentNumber else None,
            "phone":      str(e.telephoneNumber) if e.telephoneNumber else None,
            "groups":     ",".join(dn.split(",")[0].replace("cn=","")
                          for dn in (e.memberOf.values or [])),
        }
        for e in ldap.entries
    ]
    ldap.unbind()
    print(f"Found {len(ldap_users)} LDAP users")

    # SQLite sync
    conn = sqlite3.connect(":memory:")
    conn.execute('''
        CREATE TABLE IF NOT EXISTS users (
            uid        TEXT PRIMARY KEY,
            cn         TEXT,
            email      TEXT,
            title      TEXT,
            department TEXT,
            phone      TEXT,
            groups     TEXT,
            synced_at  TIMESTAMP DEFAULT CURRENT_TIMESTAMP
        )
    ''')

    synced = 0
    for u in ldap_users:
        conn.execute('''
            INSERT INTO users (uid,cn,email,title,department,phone,groups)
            VALUES (:uid,:cn,:email,:title,:department,:phone,:groups)
            ON CONFLICT(uid) DO UPDATE SET
                cn=excluded.cn, email=excluded.email,
                title=excluded.title, department=excluded.department,
                phone=excluded.phone, groups=excluded.groups,
                synced_at=CURRENT_TIMESTAMP
        ''', u)
        synced += 1
    conn.commit()

    # Report
    print(f"Synced {synced} users")
    for row in conn.execute("SELECT uid, cn, department FROM users ORDER BY department, uid"):
        print(f"  {row[0]:12} {row[1]:25} {row[2] or 'N/A'}")

    conn.close()

sync_users_to_db()
# python3 ldap_sync.py

OpenLDAP Install & Test

Complete OpenLDAP setup on Ubuntu from scratch.

#!/bin/bash
# OpenLDAP complete setup on Ubuntu 22.04/24.04
# Run as root or with sudo

DOMAIN="mywebuniversity.com"
BASE_DN="dc=mywebuniversity,dc=com"
ADMIN_PW="AdminSecurePass123!"  # Change this!

# ── 1. Install ────────────────────────────────────────────────
apt update
apt install -y slapd ldap-utils

# ── 2. Configure ─────────────────────────────────────────────
DEBIAN_FRONTEND=noninteractive dpkg-reconfigure slapd <<EOF
no
${DOMAIN}
MyWebUniversity
${ADMIN_PW}
${ADMIN_PW}
MDB
no
yes
EOF

# ── 3. Add schemas ────────────────────────────────────────────
ldapadd -Y EXTERNAL -H ldapi:/// -f /etc/ldap/schema/cosine.ldif
ldapadd -Y EXTERNAL -H ldapi:/// -f /etc/ldap/schema/inetorgperson.ldif
ldapadd -Y EXTERNAL -H ldapi:/// -f /etc/ldap/schema/nis.ldif

# ── 4. Create base OUs ────────────────────────────────────────
cat > /tmp/base.ldif << LDIF
dn: ou=users,${BASE_DN}
changetype: add
objectClass: organizationalUnit
ou: users

dn: ou=groups,${BASE_DN}
changetype: add
objectClass: organizationalUnit
ou: groups
LDIF

ldapadd -x -H ldap://localhost     -D "cn=admin,${BASE_DN}" -w "${ADMIN_PW}"     -f /tmp/base.ldif

# ── 5. Add test user ──────────────────────────────────────────
HASH=$(slappasswd -s "UserPass123!")

cat > /tmp/user.ldif << LDIF
dn: uid=alice,ou=users,${BASE_DN}
changetype: add
objectClass: inetOrgPerson
objectClass: posixAccount
uid: alice
cn: Alice Smith
sn: Smith
givenName: Alice
mail: alice@${DOMAIN}
uidNumber: 10001
gidNumber: 5000
homeDirectory: /home/alice
loginShell: /bin/bash
userPassword: ${HASH}
LDIF

ldapadd -x -H ldap://localhost     -D "cn=admin,${BASE_DN}" -w "${ADMIN_PW}"     -f /tmp/user.ldif

# ── 6. Test ───────────────────────────────────────────────────
echo "=== All users ==="
ldapsearch -LLL -x -H ldap://localhost     -D "cn=admin,${BASE_DN}" -w "${ADMIN_PW}"     -b "ou=users,${BASE_DN}" "(objectClass=inetOrgPerson)" uid cn mail

echo "=== Test alice authentication ==="
ldapwhoami -x -H ldap://localhost     -D "uid=alice,ou=users,${BASE_DN}" -w "UserPass123!"

echo "OpenLDAP setup complete!"
echo "Base DN: ${BASE_DN}"
echo "Admin DN: cn=admin,${BASE_DN}"
# bash setup_ldap.sh